Files
MABEA/arbeitskacheln/01_foundation.md
T
patrickandClaude Sonnet 5 e5dc80c3a6
CI / backend-tests (push) Successful in 3m14s
CI / frontend-build (push) Successful in 21s
feat(foundation): FOUND-013 optionales Error-Tracking (GlitchTip)
Zielserver-Entscheidung durch optionales Install-Script aufgelöst statt
festgeschrieben: deploy/glitchtip-install.sh installiert GlitchTip per
Docker Compose auf einem beliebigen, selbst gewählten Host - nichts
automatisch/ungefragt.

DSN wird zur Laufzeit über eine neue Admin-GUI-Seite ein-/ausschaltbar
(Systemeinstellung-Singleton, GET/PATCH /einstellungen/sentry), statt
nur per .env - kein Redeploy zum Umschalten nötig. Frontend lädt
@sentry/react nur dynamisch nach, wenn tatsächlich aktiv.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
2026-09-10 19:35:47 +02:00

18 KiB

Epic 01 — Foundation

Details zu FOUND-001 … FOUND-008. FOUND-007/008 ergänzt am 2026-09-09 aus HiOrg-Server-Vergleich, offen/ungeplant. Übersichtstabelle mit allen FOUND-Kacheln (auch die noch nicht detaillierten) siehe 00_index.md.


FOUND-001 — Repo & Projektstruktur

  • Modul: Foundation
  • Ziel: Einheitliche, nachvollziehbare Code-Basis, auf der alle weiteren Kacheln aufbauen.
  • Beschreibung: Verzeichnisstruktur Backend/Frontend, Namenskonventionen, Formatierungs-/Lint-Regeln, Basis-README.
  • Benutzerwert: Keiner direkt (Entwickler-Infrastruktur), aber Voraussetzung für alles.
  • Abhängigkeiten: keine
  • Datenmodell: keins
  • Backend: Grundgerüst (App-Einstiegspunkt, Router-Registrierung leer)
  • Frontend: Grundgerüst (Build-Tooling, leere Startseite)
  • Mobile: noch nicht relevant
  • QR-Code: nein
  • Seriennummer: nein
  • Inventarnummer: nein
  • Akte: nein
  • Rechte: keine (noch kein Auth)
  • Audit: nein
  • Akzeptanzkriterien: Repo klont sich, Backend startet, Frontend baut, Lint läuft ohne Fehler.
  • Tests: Smoke-Test „App startet"
  • DoD: lauffähiges leeres Grundgerüst, dokumentiert im README.

FOUND-002 — DB-Grundgerüst + Migrationen

  • Ziel: Versionierte, reproduzierbare Datenbankstruktur.
  • Beschreibung: PostgreSQL-Anbindung, Migrationstool (z.B. Alembic), erste leere Migration.
  • Benutzerwert: indirekt (Datenintegrität/Nachvollziehbarkeit für alle Module).
  • Abhängigkeiten: FOUND-001
  • Datenmodell: Migrations-Metatabelle (vom Tool selbst verwaltet)
  • Backend: DB-Session-Handling, Connection-Pool
  • Frontend: —
  • Mobile: —
  • QR-Code / Seriennummer / Inventarnummer / Akte: nein
  • Rechte: keine
  • Audit: nein
  • Akzeptanzkriterien: Migration lässt sich anwenden und zurückrollen, DB-Verbindung aus App funktioniert.
  • Tests: Migrations-Up/Down-Test gegen Testdatenbank.
  • DoD: Migration reproduzierbar auf frischer DB, CI führt sie aus.

FOUND-003 — Authentication

  • Ziel: Sichere Anmeldung als Basis für jede weitere Rechteprüfung.
  • Beschreibung: Login mit Passwort (gehashed), Token-Ausgabe (JWT o.ä.), Token-Validierung.
  • Benutzerwert: Jeder Nutzer kann sich anmelden — Grundvoraussetzung für alles Weitere.
  • Abhängigkeiten: FOUND-002
  • Datenmodell: benutzer (login, passwort_hash, aktiv)
  • Backend: POST /auth/login, Token-Decode-Dependency
  • Frontend: Login-Seite
  • Mobile: gleicher Login-Flow
  • QR-Code/Seriennummer/Inventarnummer: nein
  • Akte: nein
  • Rechte: noch keine Rollen, nur „angemeldet/nicht angemeldet"
  • Audit: Login-Fehlversuche protokollieren (Brute-Force-Erkennung, optional P2)
  • Akzeptanzkriterien: korrektes Passwort → Token; falsches Passwort → 401; abgelaufener Token → 401.
  • Tests: Login erfolgreich/fehlgeschlagen, Token-Ablauf.
  • DoD: Login funktioniert clientseitig und serverseitig, Passwort niemals im Klartext gespeichert/geloggt.

FOUND-004 — Authorization-Grundgerüst

  • Ziel: Zentrale Stelle, an der jeder Endpunkt Rechte prüfen kann.
  • Beschreibung: Rollen-Modell (fest, kein granulares System — das kommt erst in Personnel/Readiness-Phase oder eigener späterer Kachel), Dependency/Middleware für Rollenprüfung.
  • Benutzerwert: Schützt sensible Aktionen (z.B. nur Materialwart darf Material anlegen).
  • Abhängigkeiten: FOUND-003
  • Datenmodell: rolle, benutzer_rolle
  • Backend: require_roles()-Dependency
  • Frontend: Rollenbasiertes Ein-/Ausblenden von UI-Elementen
  • Mobile: gleiches Prinzip
  • Akte/QR/SN/Inv: nein
  • Rechte: dies IST das Rechte-Grundgerüst
  • Audit: Rollenänderungen protokollieren
  • Akzeptanzkriterien: Endpunkt ohne passende Rolle → 403; mit passender Rolle → 200.
  • Tests: je Rolle ein Zugriffstest.
  • DoD: mind. 2 Rollen (z.B. Administrator, Helfer) funktionieren nachweisbar unterschiedlich.

FOUND-005 — API-Grundgerüst

  • Ziel: Konsistente API für alle künftigen Module.
  • Beschreibung: Einheitliches Fehlerformat, Versionierungs-Präfix (/api/v1), OpenAPI-Dokumentation automatisch.
  • Benutzerwert: indirekt — konsistente, vorhersehbare API für Frontend/Mobile/ Drittsysteme.
  • Abhängigkeiten: FOUND-002
  • Datenmodell: keins
  • Backend: Fehler-Handler, Health-Endpoint
  • Frontend: generischer API-Client mit einheitlicher Fehlerbehandlung
  • Mobile: gleicher Client
  • Akte/QR/SN/Inv: nein
  • Rechte: keine (Health-Endpoint öffentlich)
  • Audit: nein
  • Akzeptanzkriterien: /health antwortet 200, ein absichtlich provozierter Fehler liefert einheitliches JSON-Fehlerformat.
  • Tests: Health-Check-Test, Fehlerformat-Test.
  • DoD: OpenAPI-Doku ist unter /docs erreichbar.

FOUND-006 — Logging & Audit-Grundgerüst

  • Ziel: Jede sicherheits-/fachlich relevante Aktion muss später nachvollziehbar sein.
  • Beschreibung: Zentrale Audit-Tabelle (append-only), generische Log-Funktion (wer/wann/was/alter Wert/neuer Wert).
  • Benutzerwert: Nachvollziehbarkeit für Zugführer/Administrator, Vertrauenswürdigkeit des Systems.
  • Abhängigkeiten: FOUND-002
  • Datenmodell: audit_log (id, zeitpunkt, benutzer_id, ereignistyp, entitaet_typ, entitaet_id, alter_wert, neuer_wert)
  • Backend: log()-Service-Funktion, von anderen Modulen aufrufbar
  • Frontend: Änderungslog-Ansicht (rudimentär, volle UI evtl. später eigene Kachel)
  • Mobile: —
  • Akte/QR/SN/Inv: nein direkt, aber Grundlage für FILE-005/007
  • Rechte: Lesen nur privilegierte Rollen
  • Audit: ist selbst das Audit-System
  • Akzeptanzkriterien: eine Testaktion erzeugt einen Log-Eintrag mit korrektem Vorher/Nachher-Wert.
  • Tests: Log-Eintrag wird bei simulierter Aktion erzeugt und ist abrufbar.
  • DoD: mind. ein reales Ereignis (z.B. Login) wird bereits geloggt.

FOUND-007 — CSV/XLS-Import/Export für Objekt- und Materialstammdaten (P2, neu,

Scope geklärt 2026-09-09)

  • Ziel: Massenpflege von Objekt- UND Materialstammdaten statt Einzelanlage per UI.
  • Scope (entschieden 2026-09-09): beide Ebenen — Objekt (Behälter-Stammdaten: Rucksack/Fahrzeug/Standort) UND GeraetInstanz (Einzelgeräte mit Seriennummer darin). Passt zum Ursprungsfall Excel-Rucksackliste, wo beide Ebenen typischerweise in einer Tabelle stehen.
  • Beschreibung: Export bestehender Objekte+Geräteinstanzen als CSV, Import mit Validierung (Duplikat-Erkennung über Inventarnummer/Seriennummer, Fehlerliste bei ungültigen Zeilen statt Alles-oder-Nichts-Abbruch). Ein Import-Vorgang kann beide Ebenen in einer Datei abdecken (verschachtelte Struktur: Objekt-Zeile + zugehörige Geräte-Zeilen) oder zwei getrennte Dateien — Implementierungsdetail, nicht mehr Scope-Frage.
  • Benutzerwert: Direkter Nutzen für den Ursprungsfall von MABEA — Migration bestehender Excel-Rucksacklisten in Massenpflege statt Zeile für Zeile manuell nacherfassen, inklusive der darin enthaltenen Einzelgeräte.
  • Abhängigkeiten: ASSET-003, GeraetInstanz-Modell
  • Datenmodell: keins neu (nutzt bestehende Objekt-/GeraetInstanz-Tabellen)
  • Backend: POST /objekte/import + POST /geraete/import (oder kombinierter Endpunkt, je nach gewählter Dateistruktur), jeweils Dry-Run-Modus + Commit-Modus; GET /objekte/export + GET /geraete/export
  • Frontend: Upload-Dialog mit Fehlerliste-Anzeige vor endgültigem Import, Export-Button in Objektliste UND Geräte-/Materialansicht
  • Mobile: nein (Desktop-Verwaltungsaufgabe)
  • QR-Code/Seriennummer/Inventarnummer: Duplikat-Prüfung läuft darüber
  • Akte: nein direkt
  • Rechte: Administrator/Materialverantwortlicher
  • Audit: Import als Sammel-Ereignis geloggt (nicht jede Zeile einzeln)
  • Akzeptanzkriterien: Import mit teilweise fehlerhaften Zeilen zeigt genau welche Zeilen fehlschlugen, gültige Zeilen werden trotzdem übernommen (kein Alles-oder- Nichts); Export re-importierbar (Round-Trip-Test), sowohl für Objekte als auch für Geräteinstanzen; Geräte-Zeilen ohne zugehöriges Objekt werden abgelehnt (referenzielle Konsistenz).
  • Tests: Round-Trip-Test (Export→Import ergibt identischen Bestand) für beide Ebenen, Fehlerzeilen-Test (gemischt gültig/ungültig), Test für Objekt+zugehörige-Geräte in einem Import-Vorgang.
  • DoD: ✅ umgesetzt (2026-09-10) — CSV (kein XLS, Scope beim Bau bewusst auf CSV reduziert) für Objekt UND GeraetInstanz, jeweils Dry-Run+Commit, Fehlerliste pro Zeile, Round-Trip export→import getestet. backend/app/services/import_export.py, backend/app/api/v1/endpoints/import_export.py, backend/tests/test_import_export.py. Matching über Klarnamen (Objekttyp/Standort/Material name, Objekt code) statt technischer IDs — passend zum CSV-Ursprungsformat der Excel-Rucksacklisten. XLS-Unterstützung nicht umgesetzt (kein Bedarf erkennbar, CSV reicht für den Excel-Export-Case). Referenz (HiOrg-Server-Vergleich 2026-09-09): HiOrg bietet Import/Export für praktisch alle Datentypen (CSV/XLS/vCard/iCal) — MABEA hat dafür jetzt einen CSV-Mechanismus für die beiden wichtigsten Ebenen.

FOUND-008 — Generischer Fristen-Dienst (P3, neu, Schnittstelle geklärt 2026-09-09)

  • Ziel: EINE Berechnungslogik für „läuft etwas bald ab/wird etwas bald fällig" statt mehrfach separat implementiert.
  • Beschreibung: MABEA hat Ablauf-/Fälligkeitslogik aktuell an drei Stellen unabhängig voneinander gebaut: Chargen-Ablaufdatum (INV-005), Qualifikations- Ablaufwarnung (PERS-007), Wartungsfälligkeit (MAINT-002/NOTIF-003). Ein gemeinsamer Fristen-Dienst (Interface: „gib mir alle Objekte mit Frist-Typ X, die den Dringlichkeitsgrad Y erreicht haben" — siehe Schnittstelle unten) würde die Berechnungslogik einmal statt dreimal pflegen.
  • Benutzerwert: Kein direkter Nutzerwert (reines Refactoring), aber weniger Wartungsaufwand/Inkonsistenz-Risiko bei künftigen Fristen-Arten (z.B. Vertrags- laufzeiten, TÜV-Termine).
  • Abhängigkeiten: INV-005, PERS-007, MAINT-002 (bestehende Implementierungen als Migrationsbasis)
  • Datenmodell: keins neu, gemeinsame FristTyp-Enum als Kategorisierung (charge_ablauf/qualifikation_ablauf/wartung_faellig/...)
  • Schnittstelle (entschieden 2026-09-09, korrigiert 2026-09-10): „Tage bis fällig" als gemeinsamer Nenner funktioniert NICHT für km/Betriebsstunden-Fristen — MABEA verfolgt keine Verbrauchsrate über Zeit (nur den aktuellen Zählerstand), eine Tage-Schätzung wäre erfunden/unbegründet. Stattdessen: gemeinsame FristTreffer-Struktur mit Dringlichkeits-Ampel statt einheitlicher Zeiteinheit — {objekt_id, frist_typ, bezeichnung, dringlichkeit: enum (ok/bald/kritisch/ueberfaellig), rest_anzeige: str}. rest_anzeige bleibt je Typ in seiner nativen Einheit (z.B. „in 12 Tagen" bei Datum-Fristen, „noch 340 km" bei Kilometer-Fristen, „noch 15 Bh" bei Betriebsstunden) — nur die Ampel-Einstufung (ok/bald/kritisch/überfällig) ist typübergreifend einheitlich vergleichbar, die Restanzeige selbst bleibt nativ und ehrlich statt einer erfundenen Tage-Umrechnung. Jede der drei Implementierungen bringt ihre eigene Schwellenwert-Logik mit (z.B. „< 10% der Sollstrecke übrig" = kritisch bei km), fristen.py normalisiert nur auf die gemeinsame Ampel, nicht auf eine gemeinsame Zeiteinheit.
  • Backend: app/services/fristen.py als gemeinsame Schnittstelle, bestehende drei Implementierungen (app/services/dashboard.py, app/services/akte.py, app/services/personal.py, app/services/wartung.py:faellige_wartungen_fuer_objekt) schrittweise darauf umstellen (nicht big-bang) — jede liefert intern weiter ihre Rohdaten, fristen.py übernimmt nur die Normalisierung auf FristTreffer.
  • Frontend: keine Änderung sichtbar (reines Backend-Refactoring)
  • Mobile: keins
  • QR-Code/Seriennummer/Inventarnummer: nein
  • Akte: nein direkt
  • Rechte: keine Änderung
  • Audit: keine Änderung
  • Akzeptanzkriterien: alle drei bestehenden Fristen-Arten liefern nach Umstellung identische Ergebnisse wie vorher (Regressionstest), neue Fristen-Art ohne Duplikation der Kernlogik hinzufügbar.
  • Tests: Regressionstests der drei bestehenden Fristen-Berechnungen gegen die neue gemeinsame Implementierung.
  • DoD: ✅ Normalisierungsschicht umgesetzt (2026-09-10), app/services/fristen.py — fristen_fuer_chargen/fristen_fuer_qualifikationen/ fristen_fuer_wartung rufen die drei bestehenden Rohfunktionen unverändert auf und normalisieren nur das Ergebnis auf FristTreffer (Ampel + rest_anzeige). Bewusst NICHT umgesetzt: Umstellung der bestehenden Aufrufer (Dashboard-/ Akte-/Personal-Endpunkte) auf die neue Schicht — schrittweise Migration wie in der Schnittstellen-Entscheidung vorgesehen, noch nicht angegangen, aktuell reine Bibliotheksfunktion ohne eigenen Endpunkt. Wartungs-Ampel für km/ Betriebsstunden bleibt vereinfacht auf ok/überfällig (Rohfunktion liefert kein Zwischenmaß, siehe Docstring in fristen.py) — echte bald/kritisch-Abstufung bräuchte einen Umbau von faellige_wartungen_fuer_objekt, aus Scope-Gründen zurückgestellt. backend/tests/test_fristen.py als Regressionstest gegen die drei Rohfunktionen. Referenz (HiOrg-Server-Vergleich 2026-09-09): HiOrg berücksichtigt Qualifikations-Ablauf generisch bei der Diensteinteilung — technische Entsprechung ist jetzt der gemeinsame Fristen-Mechanismus.

FOUND-013 — Error-Tracking (GlitchTip, P2, geplant 2026-09-10)

  • Ziel: Unbehandelte Exceptions in Backend UND Frontend zentral erfassen, statt sie nur verstreut in journalctl/Browser-Konsole zu finden.
  • Entscheidung 2026-09-10: GlitchTip statt Sentry-Cloud — self-hosted, Sentry-API-kompatibel (beide SDKs sentry-sdk/@sentry/react funktionieren unverändert, nur DSN zeigt auf die eigene Instanz statt sentry.io). Bei Rettungsdienst-Kontext (potenzieller Art.-9-DSGVO-Bezug) bleiben Fehlerdaten damit auf eigener Infrastruktur statt bei einem US-SaaS-Anbieter.
  • Standort — bewusst nicht festgeschrieben: GlitchTip braucht nur eine DSN-URL in Backend-.env (SENTRY_DSN) und Frontend-Build (VITE_SENTRY_DSN) — läuft auf 192.168.1.238, einem separaten Server, oder später sogar GlitchTips eigenem SaaS, ohne Codeänderung. Standortwechsel ist reines Config-Ändern + Neustart, kein Redeploy-Zwang der MABEA-Business- Logik. Konkreter Zielserver noch offen — DECISION REQUIRED vor Inbetriebnahme (192.168.1.238 selbst, oder ein neuer/separater Host analog zu 192.168.1.236 für Flutter).
  • Abhängigkeiten: keine (additiv, keine bestehende Funktion betroffen)
  • Betrieb (GlitchTip selbst): Docker Compose (GlitchTip-Container + Postgres + Redis + optional Celery-Worker) — GlitchTip wird offiziell nur als Docker-Image vertrieben, kein natives Debian-Paket. Widerspricht nicht CLAUDE.md „kein Docker-Zwang" (das gilt für MABEA selbst, das bereits ohne Docker läuft) — für GlitchTip als Fremdsoftware ist Docker die einzige praktikable Betriebsart, isoliert vom restlichen Server-Setup.
  • Backend: sentry-sdk[fastapi] als Dependency, Initialisierung in app/main.py (DSN aus app_settings.py, No-Op wenn DSN leer/nicht gesetzt — Fail-Safe, kein Fehler-Tracking bricht den Start nicht ab).
  • Frontend: @sentry/react, Initialisierung in main.tsx (DSN aus VITE_SENTRY_DSN, ebenfalls No-Op wenn ungesetzt).
  • Rechte/Zugriff: GlitchTip hat eigene Benutzerverwaltung (kein MABEA-Login-Bezug) — Zugriff auf die Fehler-Übersicht separat einzurichten.
  • Datenschutz: IP-Adressen/Nutzerdaten in Fehlerberichten sind DSGVO- relevant (send_default_pii explizit auf False lassen), Payload-Scrubbing für bekannte sensible Felder (Login/Passwort-Felder) vor Rollout prüfen.
  • Akzeptanzkriterien: eine absichtlich ausgelöste Backend-Exception UND eine Frontend-Exception erscheinen in GlitchTip mit Stacktrace.
  • Tests: keine (reine Infrastruktur-/Konfigurationsintegration).
  • DoD: ✅ erledigt (2026-09-10) — Zielserver-Entscheidung durch optionales Install-Script aufgelöst statt festgeschrieben: deploy/glitchtip-install.sh installiert GlitchTip per Docker Compose auf einem BELIEBIGEN, vom Nutzer gewählten Host (MABEA-Server selbst oder separat) — nichts wird automatisch/ungefragt installiert, das Script muss bewusst ausgeführt werden. Zusätzlich, über die ursprüngliche Planung hinaus: DSN wird NICHT nur per .env gesetzt, sondern zur Laufzeit über eine neue Admin-GUI-Seite („Fehler-Tracking") ein-/ausschaltbar — Systemeinstellung-Singleton-Tabelle (Migration 0053_systemeinstellung.py), GET/PATCH /einstellungen/sentry (GET bewusst ohne Auth, DSN ist kein Geheimnis, wird ohnehin im Frontend-Bundle sichtbar), sentry_sdk.init() wird beim Setzen sofort erneut aufgerufen (kein Backend-Neustart nötig). Frontend lädt die DSN zur Laufzeit (src/monitoring/sentry.ts) und lädt @sentry/react nur dynamisch nach, wenn tatsächlich aktiv (kein Bundle- Gewicht im Normalfall). .env-Variable SENTRY_DSN bleibt als Bootstrap- Fallback bestehen, DB-Wert hat Vorrang. send_default_pii=False fest verdrahtet (DSGVO). Tests: test_found013_systemeinstellung.py.

MABEA-Ist-Stand-Abgleich: FOUND-001…006 sind in MABEA vollständig vorhanden (FastAPI-Grundgerüst, Alembic-Migrationen, JWT-Login, require_roles()-Dependency, einheitliches Fehlerformat + /docs, historie-Tabelle als Audit-Log). FOUND-007…012 (Konfiguration/Docker/CI/Backup/Tests/Doku) ebenfalls größtenteils vorhanden (app_settings.py, .env, Gitea-CI ci.yml, pytest, README/DEVLOG). FOUND-013 (Error-Tracking) als einzige echte Lücke identifiziert und geplant (2026-09-10), Umsetzung hängt an einer noch offenen Zielserver-Entscheidung.