# 06 Betrieb ## Deployment Gebaut wird **lokal**, auf den Server geht nur `dist/` plus Backend-Quellen. Der Server hat wenig freien RAM und volles Swap - dort wird nicht gebaut. ```mermaid flowchart LR L["Arbeitsplatz"] -->|"npm run build"| D["dist/"] D -->|pscp| S1["/var/www/perinco_CRM/dist"] L -->|pscp| S2["/var/www/perinco_CRM/server/src"] L -->|pscp| S3["server/package.json"] S3 -.->|"nur mit -Deps"| NI["npm install --omit=dev"] NI --> R["systemctl restart
perinco-crm-backend"] S2 --> R R --> H["curl /api/health"] ``` ```powershell npm run build powershell -File deploy/deploy.ps1 # dist + Backend powershell -File deploy/deploy.ps1 -Deps # zusaetzlich npm install powershell -File deploy/deploy.ps1 -SkipBuild # ohne neuen Build ``` Das Passwort kommt aus `$env:PERINCO_CRM_SSH_PASSWORD` oder per `-Password`. `deploy.ps1` laedt die systemd-Unit **nicht** mit hoch. Nach Aenderungen an `deploy/perinco-crm-backend.service` von Hand kopieren und `daemon-reload`. ## Dienste auf dem Server | Unit | Verzeichnis | Port | Anmerkung | | --- | --- | --- | --- | | `perinco-crm-backend` | `/var/www/perinco_CRM/server` | 5006 | `MemoryMax=1536M` | | `project-scanner-backend` | `/var/www/Projekt Scanner/backend` | 5005 | alte App, laeuft bis zur Ablosung | Der Verzeichnisname der alten App enthaelt ein **Leerzeichen**. In Skripten und in der `.env` immer in Anfuehrungszeichen setzen - systemd liest `.env` als `EnvironmentFile` und trennt sonst am Leerzeichen. `MemoryMax=1536M` statt der urspruenglichen 512M: ein Scan-Lauf startet Chromium als Kindprozess in derselben cgroup. Mit 512M wuerde der OOM-Killer das Backend mitten im Scrapen beenden. ## Umgebungsvariablen `server/.env`, Rechte `600`. Vorlage: `server/.env.example`. | Variable | Bedeutung | | --- | --- | | `PORT`, `HOST` | `5006`, `127.0.0.1` - von aussen nur ueber Apache | | `DB_*` | Verbindung zu `perinco_CRM` | | `JWT_SECRET`, `JWT_EXPIRES_IN` | Signatur der Sitzungen, Standard 12 h | | `ADMIN_*` | wird beim ersten Start angelegt, nur wenn `crm_users` leer ist | | `SCANNER_DB_NAME` | eigene Scanner-Datenbank, Standard `perinco_scanner` | | `SCANNER_ENCRYPTION_KEY` | AES-Schluessel fuer Portal-Passwoerter | | `SCANNER_UPLOAD_DIR` | CV-Verzeichnis, leer = `/uploads` | | `SCANNER_SCHEDULER` | Hintergrund-Scraper, Standard an, nur `false` schaltet ab | | `SCANNER_SCHEDULER_OFFSET` | Minuten Versatz fuer den ersten Lauf, Standard 30 | | `LEGACY_*` | Quelle fuer `npm run scanner:import` | Die Scanner-Datenbank wird beim Start automatisch angelegt (`CREATE DATABASE IF NOT EXISTS`), Tabellen kommen aus `sequelize.sync()`, Portale und Standardeinstellungen werden geseedet. ## Playwright `playwright` ist auf **exakt 1.60.0** gepinnt - dieselbe Version wie die alte App. Deren Chromium-Build (`chromium-1223`) liegt in `/root/.cache/ms-playwright`, also faellt kein zweiter Browser-Download an. Ein `^` in der Version wuerde das aufgeben; die Begruendung steht als `comments`-Feld in `server/package.json`. Fehlt der Browser, funktioniert alles ausser Scan-Lauf und Verbindungstest: ```bash cd /var/www/perinco_CRM/server && npx playwright install chromium ``` ## Erststart des Scanners ```bash bash /var/www/perinco_CRM/deploy/scanner-setup.sh # .env ergaenzen, Schluessel erzeugen cd /var/www/perinco_CRM/server npm run scanner:import -- --dry-run npm run scanner:import -- --with-matches systemctl restart perinco-crm-backend ``` > **Reihenfolge:** Der Import schreibt `search_interval` aus der alten App in die > Einstellungen. Der laufende Scheduler merkt die Aenderung binnen einer Minute > und plant sich neu - beim Rollout hat er direkt nach dem Import einen Scan > gestartet, den der folgende Restart mitten im Scrapen beendet hat. Harmlos, aber > vermeidbar: erst neu starten, dann importieren. ## Stoerungssuche | Symptom | Blick darauf | | --- | --- | | Anmeldung schlaegt fehl | `journalctl -u perinco-crm-backend -n 50`, `JWT_SECRET` gesetzt? | | Scanner-Reiter leer, 500er | `[Scanner] Datenbank nicht erreichbar` im Journal | | Kein Treffer trotz Scan | `scanning_active`, aktive Agenten mit CV, OpenAI-Key gesetzt | | Portal `expired` | Cookies neu importieren (Cookie-Editor) | | `Target page ... has been closed` | Dienst wurde mitten im Scrapen neu gestartet | | Backend stirbt beim Scrapen | `MemoryMax` zu klein, `systemctl show ... -p MemoryCurrent` | | Ton bleibt aus | Schwellenwert, Dashboard-Ansicht offen, einmal auf der Seite geklickt? | Nuetzliche Abfragen: ```bash systemctl status perinco-crm-backend journalctl -u perinco-crm-backend -f curl -s http://127.0.0.1:5006/api/health set -a; . /var/www/perinco_CRM/server/.env; set +a mysql -u"$DB_USER" -p"$DB_PASSWORD" "$SCANNER_DB_NAME" -e \ "SELECT COUNT(*) FROM matches; SELECT key_name, value FROM settings" ``` ## Sicherung Beide Datenbanken einzeln, dazu die CV-Dateien: ```bash set -a; . /var/www/perinco_CRM/server/.env; set +a mysqldump -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" > crm_$(date +%F).sql mysqldump -u"$DB_USER" -p"$DB_PASSWORD" "$SCANNER_DB_NAME" > scanner_$(date +%F).sql tar czf uploads_$(date +%F).tar.gz -C /var/www/perinco_CRM/server uploads ``` Die `.env` gehoert **nicht** ins Repository, aber in die Sicherung - ohne `SCANNER_ENCRYPTION_KEY` sind die Portal-Passwoerter verloren. Genau das ist der alten App passiert.