Files
SaschaandClaude Opus 5.5 4adf48f336 Anmeldung gilt 7 Tage statt 12 Stunden
JWT-Laufzeit (JWT_EXPIRES_IN) standardmaessig auf 7d gesetzt, damit
Benutzer nicht mehr taeglich neu anmelden muessen. Doku (Betrieb,
Benutzeranleitung, Glossar) und .env.example angepasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 22:10:46 +02:00

6.8 KiB
Raw Permalink Blame History

07 Betrieb

Deployment

Gebaut wird lokal, auf den Server gehen nur dist/ plus die Backend-Quellen.

$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:

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 7 Tage (7d)
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)

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:

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

Der alte Name crm.icon-experts.de

Das erste ICON CRM lief unter crm.icon-experts.de auf 212.227.135.41 (/var/www/ICON, Supabase als Datenbank). Seit dem 14.09.2026 zeigt der Name auf diesen Server und wird vom Vhost icrm.icon-experts.de als ServerAlias mitbedient – dasselbe Zertifikat deckt beide Namen ab. Verschickte Links und Lesezeichen laufen damit weiter, es gibt keinen zweiten Stand zu pflegen.

Der alte Aufbau ist zurückgebaut: Vhosts (icon, icon-le-ssl) abgeschaltet, /var/www/ICON gelöscht, das dortige Zertifikat für crm.icon-experts.de entfernt (sonst hätte die Erneuerung ab sofort täglich Fehler gemeldet).

Gesichert ist er als icon-crm-alt-2026-09-14.zip – Quellcode, Build, .env und beide Vhost-Dateien, ohne node_modules. Die Datei liegt auf beiden Servern unter /root/backups/, Prüfsumme 252f7fa3226c2df7c4020a0234c3095c.

Die Supabase-Instanz icondb.perinco.de läuft weiter. Sie war die Datenbank des alten CRM und ist die Quelle der Migration (server/src/migrate.js) – abzuschalten wäre eine eigene Entscheidung, nicht Teil dieses Rückbaus.

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
perinco-Kontakte fehlen GET /api/perinco/status; Token beidseitig gesetzt?
Diktat lädt nicht siehe 04 Diktat – 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:

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:

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.