5.9 KiB
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.htmlvon Hand ändert und dann mit-SkipBuildhochlädt, spielt das altedist/aus. Nach jeder Änderung ansrc/oderindex.htmlgilt: 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)
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
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.