Karte 12: systemd-Timer für den täglichen Eskalations-Prüflauf
CI / backend-tests (push) Successful in 1m8s
CI / frontend-build (push) Successful in 25s

scripts/eskalation_pruefen.py ruft den Service direkt gegen die DB auf (kein
HTTP/Admin-Token nötig, gleiches Muster wie scripts/create_admin.py).
mabea-eskalation.timer läuft täglich 06:00 Uhr. Zeitschwellen bleiben in der
DB (eskalation_konfiguration), nicht im Timer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-04 18:28:46 +02:00
co-authored by Claude Sonnet 5
parent e0cbf4b8de
commit c71df1ac4d
5 changed files with 145 additions and 0 deletions
+63
View File
@@ -2690,3 +2690,66 @@ Keine Commits in dieser Session.
- backend/tests/test_eskalation.py | 73 ++++++++++++++++
---
## 2026-09-04 18:07 18:08 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** frontend
### Commits
- e0cbf4b Playwright-E2E-Tests für die PWA (Login, Sperrlogik, Fortschritt, Offline-Queue)
### Geänderte Dateien
- DEVLOG.md | 22 +++++++++
- frontend/.gitignore | 3 ++
- frontend/DEVLOG.md | 22 +++++++++
- frontend/README.md | 18 +++++--
- frontend/e2e/fortschritt.spec.ts | 41 ++++++++++++++++
- frontend/e2e/login.spec.ts | 30 ++++++++++++
- frontend/e2e/mockApi.ts | 98 ++++++++++++++++++++++++++++++++++++++
- frontend/e2e/offline-queue.spec.ts | 65 +++++++++++++++++++++++++
- frontend/e2e/sperrlogik.spec.ts | 34 +++++++++++++
- frontend/package.json | 5 +-
- frontend/playwright.config.ts | 21 ++++++++
---
## 2026-09-04 18:15 18:20 (5m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 22 +++++++++
- frontend/.gitignore | 3 ++
- frontend/DEVLOG.md | 22 +++++++++
- frontend/README.md | 18 +++++--
- frontend/e2e/fortschritt.spec.ts | 41 ++++++++++++++++
- frontend/e2e/login.spec.ts | 30 ++++++++++++
- frontend/e2e/mockApi.ts | 98 ++++++++++++++++++++++++++++++++++++++
- frontend/e2e/offline-queue.spec.ts | 65 +++++++++++++++++++++++++
- frontend/e2e/sperrlogik.spec.ts | 34 +++++++++++++
- frontend/package.json | 5 +-
- frontend/playwright.config.ts | 21 ++++++++
---
## 2026-09-04 18:27 18:27 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 22 +++++++++
- frontend/.gitignore | 3 ++
- frontend/DEVLOG.md | 22 +++++++++
- frontend/README.md | 18 +++++--
- frontend/e2e/fortschritt.spec.ts | 41 ++++++++++++++++
- frontend/e2e/login.spec.ts | 30 ++++++++++++
- frontend/e2e/mockApi.ts | 98 ++++++++++++++++++++++++++++++++++++++
- frontend/e2e/offline-queue.spec.ts | 65 +++++++++++++++++++++++++
- frontend/e2e/sperrlogik.spec.ts | 34 +++++++++++++
- frontend/package.json | 5 +-
- frontend/playwright.config.ts | 21 ++++++++
---
+37
View File
@@ -0,0 +1,37 @@
"""Karte 12: periodischer Eskalations-Prüflauf, für einen systemd-Timer gedacht.
Deployment-Regel (Prompt 19): nur Skript-Datei hier, Ausführung auf Zielsystem.
Direkter DB-Zugriff statt HTTP-Aufruf des Endpunkts (POST /eskalation/pruefen) -
kein Admin-Token/Login nötig für einen lokalen Cron/Timer auf demselben Host,
gleiches Muster wie scripts/create_admin.py.
Aufruf (im aktivierten venv auf dem Server):
python -m scripts.eskalation_pruefen
"""
from __future__ import annotations
import asyncio
import logging
from app.db.session import SessionLocal
from app.services.eskalation import pruefe_offene_fehlbestaende
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
async def main() -> None:
async with SessionLocal() as db:
ergebnis = await pruefe_offene_fehlbestaende(db)
await db.commit()
logger.info(
"Eskalations-Prüflauf: %d Erinnerung(en), %d Eskalation(en) verschickt.",
ergebnis["erinnerungen"],
ergebnis["eskalationen"],
)
if __name__ == "__main__":
asyncio.run(main())
+16
View File
@@ -21,7 +21,23 @@ Installiert PostgreSQL + nginx, legt DB-Rolle/Datenbank an, richtet nginx als Re
3. `alembic upgrade head` ausführen.
4. `mabea-backend.service.template` nach `/etc/systemd/system/mabea-backend.service` kopieren (Pfade/User anpassen), `systemctl daemon-reload && systemctl enable --now mabea-backend`.
## Eskalations-Prüfung einrichten (Karte 12, nach Backend-Deploy)
```bash
cp mabea-eskalation.service.template /etc/systemd/system/mabea-eskalation.service
cp mabea-eskalation.timer.template /etc/systemd/system/mabea-eskalation.timer
systemctl daemon-reload
systemctl enable --now mabea-eskalation.timer
```
Läuft täglich 06:00 Uhr (±5min Streuung), ruft `scripts/eskalation_pruefen.py` direkt
gegen die DB auf (kein HTTP/Admin-Token nötig). Zeitschwellen selbst liegen in der DB
(`eskalation_konfiguration`, admin-editierbar über `GET/PUT /api/v1/eskalation/konfiguration`),
nicht in dieser Timer-Konfiguration. Manueller Testlauf: `systemctl start mabea-eskalation.service`,
Ergebnis in `journalctl -u mabea-eskalation.service`.
## Dateien
- `install_server.sh` Server-Grundausstattung (PostgreSQL, nginx), idempotent
- `nginx_mabea.conf.template` Reverse-Proxy-Vorlage, von `install_server.sh` verwendet
- `mabea-backend.service.template` systemd-Unit-Vorlage für das Backend (noch nicht aktiviert, Pfade anpassen)
- `mabea-eskalation.service.template` / `mabea-eskalation.timer.template` täglicher Eskalations-Prüflauf (Karte 12)
+17
View File
@@ -0,0 +1,17 @@
[Unit]
Description=MABEA Eskalations-Pruefung (Karte 12, oneshot)
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=oneshot
User=mabea
WorkingDirectory=/opt/mabea/backend
EnvironmentFile=/opt/mabea/backend/.env
ExecStart=/opt/mabea/backend/.venv/bin/python -m scripts.eskalation_pruefen
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/mabea/backend
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=MABEA Eskalations-Pruefung taeglich (Karte 12)
[Timer]
# Taegliche Pruefung reicht - Schwellen (eskalation_konfiguration) sind in Tagen,
# ein untertaegiges Intervall brächte keinen echten Zusatznutzen, nur mehr Last.
OnCalendar=*-*-* 06:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target