Bisher hatte nur der Service sync ein "restart: unless-stopped". Nach einem Reboot, einem OOM-Kill (512 MiB RAM in der Produktion) oder einem Docker-Neustart kam deshalb nur sync von allein zurueck, waehrend api (8091) und web (8090) liegen blieben. "docker compose ps" zeigte dann genau einen laufenden Container und der Monitor war ueber die Firewall-Weiterleitung nicht mehr erreichbar - ein Fehlerbild, das nach Firewall/Reverse Proxy aussieht, obwohl schlicht nichts auf den Ports lauschte. unless-stopped statt always, damit ein bewusstes "docker compose stop" bestehen bleibt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
113 lines
5.7 KiB
YAML
113 lines
5.7 KiB
YAML
services:
|
|
api:
|
|
build:
|
|
context: ./api
|
|
dockerfile: docker/Dockerfile
|
|
volumes:
|
|
- ./api:/app
|
|
# Optional: echte status.txt aus der Produktion zum Testen einbinden, z.B.
|
|
# - ./status.txt:/status.txt:ro
|
|
working_dir: /app
|
|
# Ohne Restart-Policy bleiben api und web nach einem Reboot, einem OOM-Kill (die
|
|
# Produktionsmaschine hat 512 MiB RAM) oder einem Docker-Neustart einfach liegen,
|
|
# waehrend sync von allein zurueckkommt. "docker compose ps" zeigt dann genau einen
|
|
# laufenden Container und der Monitor antwortet gar nicht mehr - der Fehler sieht nach
|
|
# Firewall/Proxy aus, obwohl schlicht nichts auf 8090/8091 lauscht.
|
|
# unless-stopped statt always: ein bewusstes "docker compose stop" bleibt bestehen.
|
|
restart: unless-stopped
|
|
# Achtung: Eine Aenderung des Host-Ports muss in .env (MONITOR_API_BASE) nachgezogen
|
|
# werden - das Frontend laeuft im Browser und erreicht die API nicht ueber das
|
|
# Compose-Netzwerk, sondern ueber genau diesen Port.
|
|
ports:
|
|
- "8091:8080"
|
|
environment:
|
|
# Der eingebaute PHP-Server arbeitet Anfragen sonst streng nacheinander ab: eine einzige
|
|
# offene SSE-Verbindung (GET /api/v1/stream, bis zu 60s) blockiert damit jede weitere
|
|
# Anfrage - gemessen 16s fuer ein GET /api/v1/tickets. Ab dem zweiten offenen Monitor
|
|
# laeuft die API praktisch dauerhaft dicht, das Frontend faellt auf Demo-Daten zurueck
|
|
# und der PO reagiert dann auf nichts mehr (Fehlerbericht zu Issue #27).
|
|
PHP_CLI_SERVER_WORKERS: 4
|
|
MONITOR_DB_PATH: /app/data/monitor.sqlite
|
|
MONITOR_ADMIN_API_KEY: ${MONITOR_ADMIN_API_KEY:-dev-admin-key}
|
|
MONITOR_STATUS_FILE: /status.txt
|
|
MONITOR_DESK_COUNT: 8
|
|
MONITOR_SSE_MAX_ITERATIONS: 30
|
|
MONITOR_SSE_POLL_INTERVAL: 2
|
|
MONITOR_SYNC_TRIGGER_FILE: /app/data/sync-requested
|
|
CODEBERG_API_BASE: https://codeberg.org/api/v1
|
|
CODEBERG_TOKEN: ${CODEBERG_TOKEN:-}
|
|
# Secret des Codeberg-Webhooks. Ohne diesen Wert weist POST /api/v1/webhooks/codeberg
|
|
# jede Anfrage mit 401 ab - der Endpunkt ist aus dem Internet erreichbar und die
|
|
# Signatur ist sein einziger Schutz.
|
|
CODEBERG_WEBHOOK_SECRET: ${CODEBERG_WEBHOOK_SECRET:-}
|
|
# Zaehldatei fuer den Hooks-KPI (Issue #38, GET /api/v1/webhooks/stats): wird bei jedem
|
|
# Start dieses Containers von entrypoint.sh auf 0 zurueckgesetzt.
|
|
MONITOR_HOOKS_COUNTER_FILE: /app/data/hooks-count
|
|
|
|
# Haelt Backlog/Done aus Codeberg aktuell (F4, siehe API.md Abschnitt "Codeberg-Sync"):
|
|
# laeuft dasselbe Image wie api und teilt sich ueber das ./api-Volume dieselbe SQLite-Datei.
|
|
# Ohne diesen Service bleibt das Board leer, solange kein Agent selbst Tickets meldet.
|
|
#
|
|
# Der Watch-Modus laeuft aus zwei Gruenden: periodisch alle MONITOR_SYNC_INTERVAL Sekunden
|
|
# und sofort, wenn der api-Container ueber die Trigger-Datei einen Codeberg-Webhook
|
|
# weiterreicht (siehe Sync\SyncTrigger). Mit funktionierendem Webhook ist das Intervall nur
|
|
# noch das Sicherheitsnetz fuer verpasste Zustellungen und darf deshalb gross sein.
|
|
#
|
|
# Dieser Service selbst baut weiterhin nur ausgehende Verbindungen auf und braucht kein
|
|
# Port-Mapping. Die eingehende Freigabe betrifft ausschliesslich den api-Service, ueber den
|
|
# Codeberg den Webhook zustellt.
|
|
sync:
|
|
build:
|
|
context: ./api
|
|
dockerfile: docker/Dockerfile
|
|
volumes:
|
|
- ./api:/app
|
|
working_dir: /app
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- api
|
|
environment:
|
|
MONITOR_DB_PATH: /app/data/monitor.sqlite
|
|
MONITOR_SYNC_INTERVAL: ${MONITOR_SYNC_INTERVAL:-600}
|
|
MONITOR_SYNC_TRIGGER_FILE: /app/data/sync-requested
|
|
CODEBERG_API_BASE: https://codeberg.org/api/v1
|
|
CODEBERG_TOKEN: ${CODEBERG_TOKEN:-}
|
|
command: php bin/sync-codeberg.php --watch
|
|
|
|
# Statisches Frontend (html/) - Phase 2 der Neuausrichtung (Buero-Visualisierung).
|
|
# Nur zur lokalen Ansicht/Entwicklung, kein Build-Schritt noetig (Vanilla HTML/CSS/JS).
|
|
web:
|
|
image: nginx:alpine
|
|
# Siehe api: ohne Restart-Policy kommt das Frontend nach Reboot/OOM nicht von allein
|
|
# zurueck - und genau dieser Container bedient den nach aussen freigegebenen Port 8090.
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./html:/usr/share/nginx/html:ro
|
|
# Ueberlagert den Platzhalter html/API.md - der muss existieren, weil in
|
|
# einem read-only gemounteten Verzeichnis kein Mountpoint angelegt werden kann.
|
|
- ./API.md:/usr/share/nginx/html/API.md:ro
|
|
# Setzt office.html als Einstiegsseite (sonst 403 auf der Basis-URL) und liefert
|
|
# /config.js aus. Als Template gemountet, damit das nginx-Image beim Start
|
|
# MONITOR_API_BASE per envsubst einsetzt (Ausgabe: /etc/nginx/conf.d/default.conf).
|
|
- ./docker/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
|
|
environment:
|
|
# Wird in docker/nginx/default.conf.template eingesetzt und als /config.js an den
|
|
# Browser ausgeliefert. Leer = gleicher Origin (relatives /api/v1).
|
|
MONITOR_API_BASE: ${MONITOR_API_BASE:-}
|
|
# Speist nur den kosmetischen Reload-Countdown im Header (Issue #12) - gleicher
|
|
# Default wie beim sync-Service oben, damit die Anzeige zur tatsaechlichen
|
|
# Sync-Frequenz passt, wenn MONITOR_SYNC_INTERVAL nicht gesetzt ist.
|
|
MONITOR_SYNC_INTERVAL: ${MONITOR_SYNC_INTERVAL:-600}
|
|
ports:
|
|
- "8090:80"
|
|
|
|
# Fuehrt die Node-basierten Frontend-Tests (tests/frontend/) aus. Node/npm sind nur im
|
|
# Container noetig, nicht auf dem Host (analog zum api-Service mit PHP/Composer).
|
|
# Node >= 21 noetig: das Test-Skript in package.json uebergibt ein Glob-Muster an
|
|
# "node --test", das aeltere Versionen als Dateinamen lesen und nicht aufloesen.
|
|
frontend:
|
|
image: node:24-alpine
|
|
working_dir: /app
|
|
volumes:
|
|
- .:/app
|
|
command: npm test
|