Files
perinco-CRM/docs/06-betrieb.md
T
Sascha WolfandClaude Opus 5 82d1b7eab4 feat: Projekt Scanner integrieren, Whisper in die Einstellungen
Der eigenstaendige Projekt Scanner zieht komplett ins CRM ein und laeuft
parallel zur bestehenden App weiter - jede geteilte Ressource ist verdoppelt
statt geteilt, damit die alte Installation unberuehrt bleibt:

- eigene Datenbank perinco_scanner, legt sich beim Start selbst an
- eigener SCANNER_ENCRYPTION_KEY, eigenes Upload-Verzeichnis
- eigener Scheduler mit Zeitversatz, damit nicht beide Instanzen gleichzeitig
  mit denselben Portal-Cookies scrapen (Rate-Limits, CAPTCHA, doppelter RAM)
- Sperre gegen ueberlappende Laeufe: Scheduler und "Jetzt scannen" teilen sie,
  ein zweiter Aufruf bekommt 409 statt doppelter OpenAI-Kosten

Backend unter /api/scanner hinter der CRM-Anmeldung; die HTTP-Basic-Auth des
Originals entfaellt. CV-PDFs kommen als Stream mit Token statt aus einem
statischen Verzeichnis. Bestandsdaten holt "npm run scanner:import" aus der
alten Datenbank, ohne sie zu veraendern.

Oberflaeche in Tailwind und CI-Farben neu gebaut: Menuepunkt Scanner mit
aufklappbaren Unterpunkten, Einstellungen als eigener Reiter.

Whisper hat keinen eigenen Menuepunkt mehr, sondern ist ein Reiter in den
Einstellungen. Damit diese Geraeteeinstellung jeder Rolle offensteht, sind die
Einstellungen jetzt fuer alle sichtbar - welche Reiter erscheinen, entscheidet
die Rolle.

Ausserdem: kein festes Admin-Passwort mehr im Quelltext, playwright exakt auf
1.60.0 gepinnt (nutzt den vorhandenen Chromium-Build des Servers), technische
Dokumentation mit Funktionszeichnungen unter docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 15:08:59 +02:00

5.3 KiB

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.

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<br/>perinco-crm-backend"]
    S2 --> R
    R --> H["curl /api/health"]
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 = <server>/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:

cd /var/www/perinco_CRM/server && npx playwright install chromium

Erststart des Scanners

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:

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:

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.