Legt den kompletten Unterbau an, auf dem die Fachmodule aufsetzen. Betrieb: - docker-compose.yml mit Traefik, App, Worker, PostgreSQL 17 + PostGIS, Redis, MinIO und Gotenberg; Entwicklungs-Override ohne Traefik, dafuer mit Mailpit und Adminer - Mehrstufiges Dockerfile: eigener Benutzer, tini als PID 1, Healthcheck - .env.example mit einem Kommentar zu jeder Variable Mandantentrennung: - Row Level Security auf allen mandantenpflichtigen Tabellen - Zwei getrennte Datenbankverbindungen: florian_app mit aktiver RLS fuer die Anwendung, Eigentuemer-Zugang nur fuer Migration, Seed und Anmeldung - fuerOrganisation() als einziger Weg zu Fachdaten Nachvollziehbarkeit: - AuditLog append-only per Trigger; UPDATE ist dauerhaft gesperrt, DELETE nur im gekennzeichneten Aufbewahrungslauf - Geheimnisse werden vor dem Schreiben entfernt Anmeldung: - OIDC gegen ein extern betriebenes authentik, Rollen aus Gruppen nur als Vorbelegung beim ersten Login - Lokaler E-Mail/Passwort-Login als Rueckfallweg - Terminal per PIN und RFID mit zwei Schranken (Geraet, dann Person) Weiteres: - lib/authz.ts mit 9 Rollen und 38 Einzelrechten - lib/storage.ts (MinIO), lib/audit.ts, lib/warteschlange.ts (BullMQ) - Integrationen fuer Mail, Telegram und LLM hinter abschaltbaren Interfaces - tRPC v11, Basis-Layout mit Seitenleiste und dunkler Darstellung - Seed mit einer Beispielwehr: 16 Personen, 3 Fahrzeuge, 40 Geraete - 32 Vitest-Tests (davon 8 gegen eine echte Datenbank), 10 Playwright-Tests - README, Anleitung mit Screenshots, Roadmap, Aufwandsschaetzung, Devlog, Versionierungsregeln und Changelog Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
259 lines
9.8 KiB
Markdown
259 lines
9.8 KiB
Markdown
# Anleitung
|
|
|
|
Bedienung von FLORIAN, Stand Phase 0 (Version 0.1.0). Die Bilder zeigen die
|
|
Beispielwehr aus dem Seed und werden mit `npm run screenshots` neu erzeugt.
|
|
|
|
> Was hier noch fehlt — Mitglieder, Geräte, Prüfungen — kommt in den
|
|
> folgenden Phasen dazu. Der Stand ist bewusst schmal: Phase 0 baut das
|
|
> Fundament, nicht die Fachmodule. Siehe [ROADMAP.md](ROADMAP.md).
|
|
|
|
---
|
|
|
|
## 1. Anmelden
|
|
|
|
Aufgerufen wird FLORIAN im Browser unter der Adresse, die der Administrator
|
|
eingerichtet hat — in der Entwicklung `http://localhost:3100`.
|
|
|
|

|
|
|
|
Die Anmeldeseite zeigt genau die Wege, die in dieser Installation eingerichtet
|
|
sind:
|
|
|
|
| Weg | Wann |
|
|
|---|---|
|
|
| **Anmeldung über authentik** | Der Regelweg, sobald authentik eingerichtet ist. Ein Klick, keine Passworteingabe in FLORIAN. |
|
|
| **E-Mail und Passwort** | Beim Erststart, und wenn authentik gerade nicht erreichbar ist. |
|
|
| **Terminal im Gerätehaus** | Am Wandtablet, mit PIN oder Ausweiskarte. Eigene Oberfläche, kommt in Phase 4. |
|
|
|
|
Beim allerersten Start gibt es genau ein Konto: das aus `SEED_ADMIN_EMAIL` und
|
|
`SEED_ADMIN_PASSWORT` in der `.env`. Vorgabe:
|
|
|
|
```
|
|
admin@beispielwehr.de
|
|
Feuerwehr2026
|
|
```
|
|
|
|
**Dieses Passwort gehört nach dem ersten Login geändert.**
|
|
|
|
### Wenn die Anmeldung nicht klappt
|
|
|
|
Nach mehreren Fehlversuchen wird das Konto vorübergehend gesperrt — voreingestellt
|
|
nach fünf Versuchen für fünf Minuten (`KIOSK_MAX_FEHLVERSUCHE`,
|
|
`KIOSK_SPERRDAUER`). Die Meldung nennt nicht, ob die E-Mail-Adresse oder das
|
|
Passwort falsch war; das ist Absicht und verhindert, dass sich vorhandene
|
|
Konten erraten lassen.
|
|
|
|
---
|
|
|
|
## 2. Die Übersicht
|
|
|
|
Nach der Anmeldung steht die Übersicht der eigenen Wehr.
|
|
|
|

|
|
|
|
Von oben nach unten:
|
|
|
|
**Kopfzeile.** Name der Wehr und darunter, wer angemeldet ist und mit welchen
|
|
Rollen. Wer mehrere Rollen trägt — der Gerätewart ist oft auch Ausbilder —
|
|
sieht alle.
|
|
|
|
**Sicherheitshinweis.** Der gelbe Kasten bleibt stehen, solange die Wehr die
|
|
Anwendung nicht ausdrücklich freigegeben hat. Er ist keine Formalität:
|
|
Atemschutzüberwachung und Alarmmonitor sind sicherheitsrelevant, und bis zur
|
|
Freigabe bleibt die analoge Überwachung führend.
|
|
|
|
**Kennzahlen.** Vier Kacheln mit aktiven Mitgliedern, Fahrzeugen, Geräten und
|
|
fälligen Prüfungen. Sie zählen nur die eigene Wehr — was eine andere Wehr auf
|
|
derselben Installation hat, ist nicht sichtbar und nicht abfragbar.
|
|
|
|
**Stand des Aufbaus.** Welche Phasen fertig sind und was als Nächstes kommt.
|
|
|
|
### Die Seitenleiste
|
|
|
|
Links stehen alle Module. Was noch nicht existiert, ist ausgegraut und trägt
|
|
rechts sein Phasenkürzel — `P2` heißt „kommt in Phase 2".
|
|
|
|
Was jemand überhaupt sieht, hängt an seinen Rechten. Ein einfaches Mitglied
|
|
sieht die Mitgliederliste, aber nicht das Protokoll. Wichtig zum Verständnis:
|
|
das Ausblenden ist nur Bequemlichkeit. Der eigentliche Schutz sitzt auf dem
|
|
Server — wer eine Adresse direkt aufruft, für die ihm das Recht fehlt, bekommt
|
|
trotzdem nichts.
|
|
|
|
---
|
|
|
|
## 3. Helle und dunkle Darstellung
|
|
|
|
Oben rechts wird zwischen hell und dunkel umgeschaltet. Voreingestellt ist die
|
|
Einstellung des Betriebssystems.
|
|
|
|

|
|
|
|
Die dunkle Darstellung ist bewusst kein invertiertes Schwarz, sondern ein
|
|
warmes Grau. Wer abends vom Fahrzeug hereinkommt und ans Terminal tritt, soll
|
|
nicht geblendet werden.
|
|
|
|
---
|
|
|
|
## 4. Rollen und was sie dürfen
|
|
|
|
Neun Rollen, jede mit einem festen Satz Rechte. Die Zuordnung steht zentral in
|
|
`src/lib/authz.ts` und wird beim Seed in die Datenbank gespiegelt — eine Wehr
|
|
kann sie also nachträglich zuschneiden, ohne dass jemand Code ändert.
|
|
|
|
| Rolle | Darf im Kern |
|
|
|---|---|
|
|
| **Administrator** | alles, auch über Wehren hinweg |
|
|
| **Kommandant** | alles innerhalb der eigenen Wehr, einschließlich Rollen vergeben und Protokoll einsehen |
|
|
| **Gerätewart** | Fahrzeuge und Geräte anlegen, ändern, aussondern; Prüfungen planen und durchführen |
|
|
| **Atemschutzgerätewart** | zusätzlich Atemschutzgeräte, Träger und die Überwachung nach FwDV 7 |
|
|
| **Kassier** | Buchungen und Kassenabschluss (Modul noch nicht geplant, siehe Roadmap) |
|
|
| **Schriftführer** | Mitglieder pflegen, Einsätze und Berichte erfassen |
|
|
| **Ausbilder** | Übungen planen, Teilnahme und Qualifikationen erfassen |
|
|
| **Mitglied** | eigene Daten, Listen lesen |
|
|
| **Terminal** | nur anmelden und erfassen — nie verwalten |
|
|
|
|
Zwei Dinge sind bewusst so gebaut:
|
|
|
|
**Der Gerätewart kommt nicht an die Kasse, der Kassier nicht an die Geräte.**
|
|
Trennung nach Verantwortung, nicht nach Bequemlichkeit.
|
|
|
|
**Das Terminal darf fast nichts.** Wer am Wandtablet arbeitet, meldet sich
|
|
zusätzlich mit PIN oder Karte an und handelt dann unter seiner eigenen Person.
|
|
Das Terminal selbst ist nur die Tür.
|
|
|
|
---
|
|
|
|
## 5. Anmeldung über authentik einrichten
|
|
|
|
authentik läuft **getrennt** von FLORIAN und ist nicht Teil dieses Stacks.
|
|
|
|
### In authentik
|
|
|
|
1. Neuen Provider anlegen, Typ **OAuth2/OpenID Provider**
|
|
2. Client-Typ: **Confidential**
|
|
3. Redirect-URI genau so eintragen:
|
|
```
|
|
https://<ihre-florian-adresse>/api/auth/callback/authentik
|
|
```
|
|
4. Scopes: `openid`, `profile`, `email`
|
|
5. Für die Rollenvorbelegung zusätzlich ein Scope-Mapping, das die Gruppen des
|
|
Benutzers als Claim `groups` ausliefert
|
|
6. Eine Application anlegen und mit dem Provider verbinden. Der Slug der
|
|
Application ist der letzte Teil der Issuer-URL.
|
|
|
|
### In der `.env`
|
|
|
|
```bash
|
|
AUTH_OIDC_ENABLED=true
|
|
AUTH_OIDC_ISSUER=https://auth.beispiel.de/application/o/florian/
|
|
AUTH_OIDC_CLIENT_ID=<aus authentik>
|
|
AUTH_OIDC_CLIENT_SECRET=<aus authentik>
|
|
AUTH_OIDC_GROUP_CLAIM=groups
|
|
AUTH_OIDC_GROUP_MAPPING=feuerwehr-kommandant:KOMMANDANT,feuerwehr-geraetewart:GERAETEWART
|
|
AUTH_OIDC_AUTO_PROVISION=true
|
|
AUTH_OIDC_STANDARD_ORGANISATION=ff-musterhausen
|
|
```
|
|
|
|
Danach die Anwendung neu starten. Auf der Anmeldeseite erscheint der Knopf für
|
|
authentik.
|
|
|
|
### Wie die Rollenvorbelegung wirkt
|
|
|
|
Die Zuordnung greift **nur beim allerersten Login** einer Person. Danach ist
|
|
die in FLORIAN gepflegte Rolle führend, und authentik überschreibt sie nicht
|
|
mehr. Wer also einem Mitglied in FLORIAN die Rolle „Gerätewart" gibt, behält
|
|
sie — auch wenn die authentik-Gruppe etwas anderes sagt.
|
|
|
|
Steht `AUTH_OIDC_AUTO_PROVISION=false`, muss die Person vorher in FLORIAN
|
|
angelegt sein. Verknüpft wird über die E-Mail-Adresse.
|
|
|
|
### Wenn authentik ausfällt
|
|
|
|
Solange `AUTH_LOCAL_ENABLED=true` steht, bleibt der Passwort-Login als
|
|
Rückfallweg erreichbar. Für den Wirkbetrieb ist es sinnvoll, mindestens ein
|
|
Konto mit Passwort zu behalten — sonst sperrt ein Ausfall von authentik die
|
|
ganze Wehr aus.
|
|
|
|
---
|
|
|
|
## 6. Terminal-Anmeldung im Gerätehaus
|
|
|
|
Die Oberfläche dazu kommt in Phase 4; der Anmeldeweg steht bereits.
|
|
|
|
Am Terminal wird zweifach geprüft:
|
|
|
|
1. **Das Gerät** weist sich mit `KIOSK_DEVICE_SECRET` aus. Ohne diesen Wert
|
|
nimmt der Server keine PIN und keine Karte an.
|
|
2. **Die Person** meldet sich mit einer sechsstelligen PIN (zusammen mit ihrer
|
|
Mitgliedsnummer) oder mit einer RFID-Karte an.
|
|
|
|
Beides wird nur als Hash gespeichert — weder die PIN noch die Kartenkennung
|
|
liegen im Klartext in der Datenbank.
|
|
|
|
PINs, die sich zu leicht raten lassen, weist FLORIAN ab: lauter gleiche
|
|
Ziffern und fortlaufende Folgen wie `123456`.
|
|
|
|
Aus dem Seed lässt sich das ausprobieren: Mitgliedsnummer `0001` (Andreas
|
|
Huber), PIN `471203`.
|
|
|
|
---
|
|
|
|
## 7. Was protokolliert wird
|
|
|
|
Jede schreibende Änderung erzeugt einen Eintrag mit Zeitpunkt, Person,
|
|
betroffenem Datensatz und den geänderten Feldern im Vorher-Nachher-Vergleich.
|
|
Auch jede Anmeldung — erfolgreich wie gescheitert.
|
|
|
|
Zwei Eigenschaften sind wichtig:
|
|
|
|
**Der Log lässt sich nicht ändern.** Nicht über die Oberfläche, nicht über die
|
|
Datenbank, auch nicht vom Administrator. Die Datenbank selbst verhindert es.
|
|
|
|
**Er lässt sich nur im Aufbewahrungslauf kürzen.** Nach der eingestellten
|
|
Frist (`AUDIT_AUFBEWAHRUNG_TAGE`, Vorgabe 10 Jahre) sichert der
|
|
Hintergrundprozess die alten Einträge und entfernt sie danach. Das ist der
|
|
einzige Weg, auf dem etwas aus dem Log verschwindet.
|
|
|
|
**Passwörter, PINs und Kartenkennungen stehen nie im Log** — sie werden vor
|
|
dem Schreiben herausgenommen.
|
|
|
|
Einsehbar ist der Log mit dem Recht `system.auditlog`, das Kommandant und
|
|
Administrator haben. Die Ansicht dazu kommt in Phase 1.
|
|
|
|
---
|
|
|
|
## 8. Wenn etwas nicht geht
|
|
|
|
| Beobachtung | Wahrscheinliche Ursache | Was hilft |
|
|
|---|---|---|
|
|
| Die Anwendung startet nicht und meldet fehlende Variablen | `.env` unvollständig | Die Meldung nennt jede fehlende Variable. Erklärungen dazu stehen in `.env.example`. |
|
|
| Anmeldung schlägt immer fehl, obwohl das Passwort stimmt | Konto nach Fehlversuchen gesperrt | Fünf Minuten warten, oder `gesperrtBis` in der Datenbank zurücksetzen |
|
|
| Listen sind leer, obwohl Daten da sind | Kein Mandantenkontext gesetzt | Das ist der Schutzmechanismus. Der Zugriff muss über `fuerOrganisation()` laufen. |
|
|
| Dateien lassen sich nicht hochladen | MinIO nicht erreichbar oder Bucket fehlt | `http://localhost:9011` öffnen; `docker compose logs minio-init` |
|
|
| Der Knopf für authentik fehlt | Issuer, Client-ID oder Secret nicht gesetzt | Alle drei sind nötig, sonst wird der Weg ausgeblendet |
|
|
| `docker compose up` startet Traefik mit | Override-Datei wurde nicht mitgeladen | In Produktion `-f docker-compose.yml` angeben |
|
|
|
|
Zustand aller Randsysteme auf einen Blick:
|
|
|
|
```
|
|
http://localhost:3100/api/health
|
|
```
|
|
|
|
Antwortet mit `200` und `"status": "ok"`, wenn Datenbank, Objektspeicher und
|
|
Warteschlange erreichbar sind — sonst mit `503` und der Angabe, welcher Dienst
|
|
fehlt.
|
|
|
|
---
|
|
|
|
## Bilder erneuern
|
|
|
|
```bash
|
|
docker compose up -d
|
|
npm run dev
|
|
npm run screenshots
|
|
```
|
|
|
|
Die Bilder entstehen in `docs/screenshots/`, jeweils in heller und dunkler
|
|
Darstellung, und werden mit eingecheckt — die Anleitung soll auch dann lesbar
|
|
sein, wenn gerade kein Stack läuft.
|