JWT_EXPIRES_IN faellt ohne Eintrag in .env jetzt auf 7d statt 12h zurueck. Doku und .env.example angepasst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.9 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 # zusätzlich 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 lädt die systemd-Unit nicht mit hoch. Nach Änderungen 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 |
Die zweite Unit project-scanner-backend (Port 5005, /var/www/Projekt Scanner/backend)
ist mit dem Rückbau der alten App
am 09.09.2026 entfallen. Port 5005 ist seither frei.
MemoryMax=1536M statt der ursprünglichen 512M: ein Scan-Lauf startet Chromium
als Kindprozess in derselben cgroup. Mit 512M würde 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 außen nur über Apache |
DB_* |
Verbindung zu perinco_CRM |
JWT_SECRET, JWT_EXPIRES_IN |
Signatur der Sitzungen, Standard 7 Tage (7d) |
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-Schlüssel für Portal-Passwörter |
SCANNER_UPLOAD_DIR |
CV-Verzeichnis, leer = <server>/uploads |
SCANNER_SCHEDULER |
Hintergrund-Scraper, Standard an, nur false schaltet ab |
SCANNER_SCHEDULER_OFFSET |
Minuten Versatz für den ersten Lauf, seit dem Rückbau der alten App 0 |
MS_TENANT_ID, MS_CLIENT_ID, MS_CLIENT_SECRET |
App-only-Zugriff auf Microsoft Graph |
MS_MAILBOX |
abzugleichendes Postfach, Standard crm@perinco.de |
MS_MAIL_FOLDER |
Ordner, Standard inbox |
MAIL_SYNC |
Postfachabgleich, Standard an, nur false schaltet ab |
MAIL_MAX_PAGES, MAIL_MAX_BODY_CHARS |
Bremsen für Erstabgleich und Textgröße |
MS_SSO_ENABLED |
Anmeldung über Microsoft, Standard aus, nur true schaltet ein |
MS_SSO_REDIRECT_URI |
Umleitungsadresse, zeichengenau wie in Entra eingetragen |
MS_SSO_ALLOWED_DOMAINS |
Positivliste von E-Mail-Domains, leer = keine zusätzliche Prüfung |
CRM_ENCRYPTION_KEY |
verschlüsselt das in der Oberfläche hinterlegte Client-Secret; fehlt er, dient JWT_SECRET als Schlüssel |
Die Scanner-Datenbank wird beim Start automatisch angelegt
(CREATE DATABASE IF NOT EXISTS), Tabellen kommen aus sequelize.sync(), Portale
und Standardeinstellungen werden geseedet.
Fehlen die MS_*-Werte, startet das Backend normal und protokolliert
[Mail] Zugangsdaten fehlen. Die Ansicht Mails und der Einstellungsreiter
Postfach nennen die fehlende Variable beim Namen. Einrichtung in Entra und
Exchange Online: 11 Postfachanbindung.
Die
MS_*-Werte werden beim Start gelesen. Nach einer Änderung an der.envmuss der Dienst neu starten, ein Speichern in der Oberfläche genügt nicht - dort liegen nur Intervall, Schalter und Zeitraum (crm_settings).
Die MS_SSO_*-Werte gehören zur Anmeldung über Microsoft und nutzen dieselbe
App-Registrierung wie das Postfach. Ohne sie bleibt es bei Benutzername und
Passwort. Einrichtung: 13 Anmeldung über Microsoft.
MS_CLIENT_SECRET ist seit dem 21.09.2026 nur noch die Rückfallebene: Vorrang
hat das Secret, das Administratoren unter Einstellungen → Anmeldung eintragen.
Es liegt verschlüsselt in crm_settings, wird bei jedem Bedarf frisch gelesen
und braucht deshalb keinen Neustart.
Playwright
playwright ist auf exakt 1.60.0 gepinnt. Der passende Chromium-Build
(chromium-1223) liegt in /root/.cache/ms-playwright - dieser Cache stammt aus
der Installation der alten App und hat deren Rückbau überlebt, weil beide Apps
ihn geteilt haben und PLAYWRIGHT_BROWSERS_PATH nirgends gesetzt ist. Ein ^ in
der Version würde einen zweiten Browser-Download ziehen; die Begründung steht als
comments-Feld in server/package.json.
Beim Aufräumen nicht anfassen:
/root/.cache/ms-playwright(641 MB) liegt ausserhalb beider Anwendungsverzeichnisse. Wer ihn mit löscht, nimmt dem Scanner den Browser - Oberfläche und Treffer bleiben, Scan-Lauf und Verbindungstest fallen aus.
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 ergänzen, Schlüssel erzeugen
systemctl restart perinco-crm-backend
Datenbank und Tabellen legt der Start selbst an, Portale und
Standardeinstellungen werden geseedet. Agenten, Zugangsdaten und Cookies kommen
danach über die Oberfläche. Das frühere npm run scanner:import holte diese
Daten aus der Datenbank der alten App - beides ist mit dem Rückbau am 09.09.2026
entfallen.
Störungssuche
| Symptom | Blick darauf |
|---|---|
| Anmeldung schlägt 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 (Einstellungen → KI) |
| KI-Fehler „model not found“ | Modell unter Einstellungen → KI auf eines aus der Liste stellen |
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? |
| Keine Mails im Postfachreiter | journalctl -u perinco-crm-backend | grep '\[Mail\]', dann Verbindungstest in den Einstellungen |
Mail-Abgleich meldet ErrorAccessDenied |
Administratorzustimmung für Mail.Read fehlt, oder die ApplicationAccessPolicy erlaubt das Postfach nicht |
Nützliche 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"
# Zustand des Postfachabgleichs
mysql -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -e \
"SELECT mailbox, has_more, last_run_at, last_success_at, last_error FROM crm_mail_sync_state;
SELECT COUNT(*) AS mails FROM crm_mail_messages;
SELECT status, COUNT(*) FROM crm_mail_contact_candidates GROUP BY status"
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 gehört nicht ins Repository, aber in die Sicherung - ohne
SCANNER_ENCRYPTION_KEY sind die Portal-Passwörter verloren. Genau das ist der
alten App passiert.