139 lines
5.9 KiB
Markdown
139 lines
5.9 KiB
Markdown
# 07 Betrieb
|
||
|
||
## Deployment
|
||
|
||
Gebaut wird **lokal**, auf den Server gehen nur `dist/` plus die Backend-Quellen.
|
||
|
||
```powershell
|
||
$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:
|
||
|
||
```mermaid
|
||
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 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)
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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 | head` – kommt das durch den Proxy? |
|
||
| perinco-Kontakte fehlen | `GET /api/perinco/status`; Token beidseitig gesetzt? |
|
||
| Diktat lädt nicht | siehe [04 Diktat](04-diktat.md) – 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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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.
|