From e3a5140d8326d506ed6498473e08c30285963c02 Mon Sep 17 00:00:00 2001 From: Sascha Date: Mon, 14 Sep 2026 15:34:16 +0200 Subject: [PATCH] Dokumentation: docs/07-betrieb.md --- docs/07-betrieb.md | 138 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/07-betrieb.md diff --git a/docs/07-betrieb.md b/docs/07-betrieb.md new file mode 100644 index 0000000..a9f19bd --- /dev/null +++ b/docs/07-betrieb.md @@ -0,0 +1,138 @@ +# 07 Betrieb + +## Deployment + +Gebaut wird **lokal**, auf den Server gehen nur `dist/` plus die Backend-Quellen. + +```powershell +$env:ICON_CRM_SSH_PASSWORD = '…' +powershell -File deploy/deploy.ps1 +powershell -File deploy/deploy.ps1 -SkipBuild # ohne neuen Build +powershell -File deploy/deploy.ps1 -Deps # bei neuen Backend-Paketen +``` + +Das Skript (`deploy/deploy.ps1`) macht in dieser Reihenfolge: + +```mermaid +flowchart LR + B["npm run build"] --> U1["pscp dist/"] + U1 --> U2["pscp server/src + package.json"] + U2 --> R["systemctl restart icon-crm-backend"] + R --> C["curl /api/health"] +``` + +Schlägt der Build fehl, wird **nichts** hochgeladen – sonst stünde auf dem +Server ein halber Stand. + +> **Falle aus der Praxis:** Wer `index.html` von Hand ändert und dann mit +> `-SkipBuild` hochlädt, spielt das alte `dist/` aus. Nach jeder Änderung an +> `src/` oder `index.html` gilt: erst bauen, dann hochladen. + +Alte Build-Dateien werden beim Hochladen **nicht** entfernt – `dist/assets/` +wächst mit jeder Auslieferung. Das ist Absicht: ein Browser mit offenem Tab holt +sonst ein Bruchstück, das es nicht mehr gibt. Gelegentlich aufräumen schadet +trotzdem nicht. + +## Dienste auf dem Server + +| Was | Befehl | +| --- | --- | +| Zustand | `systemctl status icon-crm-backend` | +| Neustart | `systemctl restart icon-crm-backend` | +| Log | `journalctl -u icon-crm-backend -n 100 --no-pager` | +| Gesundheit | `curl -s http://127.0.0.1:5007/api/health` → `{"ok":true,"db":"up"}` | +| Apache | `systemctl reload apache2`, Logs unter `/var/log/apache2/icon_crm_*.log` | + +Die systemd-Unit begrenzt den Speicher auf 1 GB (`MemoryMax=1024M`). Das ist +kein Sparzwang, sondern ein Sicherheitsnetz: ein Leck im Backend soll nicht +Apache und MariaDB mitreißen. + +## Umgebungsvariablen + +`server/.env`, chmod 600, gehört **nicht** ins Repository. Vorlage: +`server/.env.example`. + +| Variable | Bedeutung | +| --- | --- | +| `PORT`, `HOST` | `5007` auf `127.0.0.1` – von außen nur über Apache | +| `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME` | MariaDB, Datenbank `icon_CRM` | +| `JWT_SECRET`, `JWT_EXPIRES_IN` | Sitzungen, Vorgabe 12 h | +| `ADMIN_USERNAME`, `ADMIN_NAME`, `ADMIN_EMAIL`, `ADMIN_PASSWORD` | erster Administrator, **nur solange `icon_users` leer ist** | +| `CORS_ORIGINS` | erlaubte Herkunft für den lokalen Dev-Server | +| `UPLOAD_DIR` | Ablage der Lebensläufe | +| `OPENAI_API_KEY`, `OPENAI_MODEL` | KI; fehlt der Schlüssel, laufen die übrigen Teile weiter | +| `PERINCO_API_URL`, `PERINCO_API_TOKEN` | ICON liest perinco | +| `PERINCO_INBOUND_TOKEN` | perinco liest ICON | + +Ohne `ADMIN_PASSWORD` erzeugt das Backend eines und schreibt es **einmalig** ins +Log. Danach ist das Passwort nur noch über die Nutzerverwaltung bzw. *Mein +Konto* änderbar. + +## Erstaufsetzung (einmalig, bereits erledigt) + +```bash +apt-get install -y apache2 mariadb-server certbot python3-certbot-apache nodejs +a2enmod proxy proxy_http rewrite headers expires +mariadb -e "CREATE DATABASE icon_CRM CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +mariadb -e "CREATE USER 'iconcrm'@'localhost' IDENTIFIED BY '…'; + GRANT ALL PRIVILEGES ON icon_CRM.* TO 'iconcrm'@'localhost';" +cd /var/www/icon_CRM/server && npm install --omit=dev +cp deploy/icon-crm-backend.service /etc/systemd/system/ +systemctl daemon-reload && systemctl enable --now icon-crm-backend +cp deploy/icrm.icon-experts.de.conf /etc/apache2/sites-available/ +a2ensite icrm.icon-experts.de && systemctl reload apache2 +certbot --apache -d icrm.icon-experts.de +``` + +Das Schema legt das Backend beim Start selbst an (`schema.sql` + +Migrationen in `db.js`). Die Firewall (`ufw`) lässt 22, 80 und 443 durch; das +Zertifikat erneuert certbot selbst. + +## Sicherung + +Der Bestand steht vollständig in MariaDB; hochgeladene Lebensläufe liegen unter +`/var/www/icon_CRM/uploads`. Beides gehört in die Sicherung: + +```bash +mariadb-dump --single-transaction icon_CRM | gzip > icon_CRM-$(date +%F).sql.gz +tar czf uploads-$(date +%F).tar.gz -C /var/www/icon_CRM uploads +``` + +## Störungssuche + +| Symptom | Erster Griff | +| --- | --- | +| Weiße Seite nach dem Rollout | `index.html` darf nicht aus dem Cache kommen – der Vhost setzt dafür `no-cache`; im Zweifel Strg+F5 | +| 502 auf `/api` | Backend aus: `systemctl status icon-crm-backend`, dann `journalctl -u …` | +| `{"db":"down"}` | MariaDB aus oder Zugangsdaten falsch | +| Anmeldung schlägt fehl | `icon_users` prüfen: existiert der Benutzer, ist `is_active = 1`? | +| Zertifikatswarnung | `certbot certificates`; deckt das Zertifikat **alle** Namen ab, auch `www`? | +| Website zeigt alte Ausschreibungen | `curl -s https://icon-experts.de/api/public/tenders | head` – kommt das durch den Proxy? | +| perinco-Kontakte fehlen | `GET /api/perinco/status`; Token beidseitig gesetzt? | +| Diktat lädt nicht | siehe [04 Diktat](04-diktat.md) – das ist eine Sache des Browsers, nicht des Servers | + +### Wenn Daten zu fehlen scheinen + +Erst messen, dann handeln. Die drei Griffe, die in der Praxis geholfen haben: + +```bash +mysql icon_CRM -e "SELECT COUNT(*) FROM icon_contacts" # steht es in der Datenbank? +curl -s .../api/contacts -H "Authorization: Bearer …" | wc -c # liefert die API es aus? +tail -n 50 /var/log/apache2/icon_crm_access.log # kam es beim Browser an? +``` + +Stimmen alle drei, liegt es an der Ansicht – meist an einem aktiven Filter. + +### DNS-Umstellungen + +Nach einem Wechsel des A-Records antworten fremde Resolver unterschiedlich +lange mit der alten Adresse. Zum Prüfen die **autoritativen** Nameserver und +mindestens einen öffentlichen Resolver befragen, nicht nur den eigenen: + +```bash +dig +short icon-experts.de @1.1.1.1 +dig +short icon-experts.de @8.8.8.8 +``` + +Ein lokaler Resolver (PiHole, Router) kann den alten Wert deutlich länger +halten – das sieht dann aus wie ein Serverfehler und ist keiner.