Schliesst die letzte offene Luecke aus Phase 1. Bis dahin liessen sich Geraete
nur ueber den Import erfassen — fuer einen Bestand richtig, fuer ein einzelnes
nachbeschafftes Geraet umstaendlich.
Neu:
- /geraete/neu und /geraete/<id>/bearbeiten, beide ueber dieselbe Komponente
- Die Lagerung ist ein Umschalter statt zweier Auswahlfelder. Standort und
Fahrzeug schliessen sich aus; so kann der Fehler gar nicht erst entstehen,
den die Datenbank sonst abweisen wuerde. Beim Wechsel des Fahrzeugs wird die
Raumangabe zurueckgesetzt.
- Prueftermin und Lebensdauerende werden beim Tippen vorgerechnet, mit
denselben Funktionen wie auf dem Server. Beim Speichern rechnet der Server
erneut und behaelt das letzte Wort.
- Gibt es nur einen Standort, ist er beim Anlegen vorbelegt.
- Knopf "Neues Geraet" in der Liste, "Bearbeiten" auf der Detailseite
Behoben:
- Das Formular stuerzte beim ersten Rechnen ab. Die Vorgabewerte lagen in einem
"use client"-Modul und wurden von einer Server-Komponente importiert; dort
kommt nur eine Client-Referenz an, kein Objekt, und das Spreaden ergibt lauter
undefined. Die Werte liegen jetzt in geraet-formular-werte.ts ohne
"use client".
- Das Sternchen an Pflichtfeldern wurde von Sprachausgaben mitgelesen
("Bezeichnung Stern"). Es traegt jetzt aria-hidden; dass ein Feld ausgefuellt
werden muss, sagt bereits das required-Attribut.
- Fehlermeldungen in Formularen sind eindeutig ansteuerbar. Next.js haengt
einen eigenen Routen-Ansager mit der Rolle "alert" in jede Seite.
Tests:
- 7 neue Playwright-Tests zum Formular, darunter die Lagerungsregel und die
Fristenvorschau
- Zwei davon laufen nicht unter Touch-Emulation: Chromium nimmt dort keine
zeichenweise Eingabe in Datumsfelder an. Begruendet uebersprungen, die
Rechnung selbst deckt qr-und-fristen.test.ts ab.
- Insgesamt 104 Unit-Tests und 56 E2E-Tests gruen
Anleitung, Changelog, Devlog, Aufwandsschaetzung und Roadmap fortgeschrieben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
537 lines
21 KiB
Markdown
537 lines
21 KiB
Markdown
# Anleitung
|
||
|
||
Bedienung von FLORIAN, Stand Phase 1 (Version 0.2.0). Die Bilder zeigen die
|
||
Beispielwehr aus dem Seed und werden mit `npm run screenshots` neu erzeugt.
|
||
|
||
> Prüfungen, Einsätze, Atemschutz und die Kiosk-Oberfläche kommen in den
|
||
> folgenden Phasen dazu. 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. Geräte
|
||
|
||

|
||
|
||
Die Geräteliste ist die Ansicht, die am häufigsten offen ist. Von links nach
|
||
rechts steht dort, was man beim Suchen braucht: Inventarnummer, was es ist, von
|
||
wem, wo es liegt, in welchem Zustand und wann es wieder geprüft werden muss.
|
||
|
||
### Suchen und filtern
|
||
|
||
**Das Suchfeld** durchsucht Inventarnummer, Bezeichnung, Hersteller, Modell und
|
||
Seriennummer gleichzeitig. Es genügt ein Teil — „press" findet alle
|
||
Pressluftatmer. Gesucht wird erst, wenn man kurz aufhört zu tippen; das
|
||
verhindert eine Abfrage bei jedem Anschlag.
|
||
|
||
**Alle Zustände** schränkt auf einen Status ein. Praktisch, wenn man wissen
|
||
will, was gerade fällig oder gesperrt ist.
|
||
|
||
**Spalten** blendet Spalten ein und aus. Zwölf stehen zur Auswahl, sechs sind
|
||
voreingestellt. Die Auswahl bleibt im Browser gespeichert — der Gerätewart
|
||
schaut auf Prüftermine, der Kassier auf Anschaffungspreise, und beide finden
|
||
ihre Einstellung beim nächsten Mal wieder.
|
||
|
||
**Sortiert** wird durch einen Klick auf die Spaltenüberschrift, ein zweiter
|
||
Klick dreht die Richtung um.
|
||
|
||
### Farben und Fristen
|
||
|
||
| Farbe | Bedeutung |
|
||
|---|---|
|
||
| grün | einsatzbereit |
|
||
| gelb | Prüfung fällig, oder eine Frist läuft in den nächsten 30 Tagen ab |
|
||
| rot | gesperrt, ausgesondert oder überfällig |
|
||
| grau | in Reparatur |
|
||
|
||
Hinter jedem Datum steht, wie viele Tage noch bleiben — „in 12 Tagen" sagt mehr
|
||
als ein Datum, das man erst im Kopf ausrechnen muss. Was vorbei ist, steht als
|
||
„seit 62 Tagen" da.
|
||
|
||
Die Farbe allein trägt nie die Information: daneben steht immer Klartext.
|
||
|
||
### Mehrere Geräte auf einmal
|
||
|
||
Über die Kästchen links lassen sich Geräte auswählen. Sobald mindestens eines
|
||
ausgewählt ist, erscheint eine Leiste mit zwei Möglichkeiten:
|
||
|
||
- **Etiketten drucken** erzeugt den Etikettenbogen als PDF.
|
||
- **Status setzen auf …** ändert den Zustand aller ausgewählten Geräte. Der
|
||
häufigste Handgriff überhaupt: Nach einer Sammelprüfung stehen zwanzig Geräte
|
||
auf einmal wieder auf einsatzbereit.
|
||
|
||
Jede Änderung wird einzeln protokolliert — am jeweiligen Gerät, nicht als
|
||
Sammelvorgang, den später niemand mehr zuordnen kann.
|
||
|
||
---
|
||
|
||
## 5. Ein einzelnes Gerät
|
||
|
||

|
||
|
||
### Woher das Prüfintervall kommt
|
||
|
||
Steht am Gerät kein eigenes Intervall, gilt die Vorgabe des Gerätetyps. Das
|
||
Bild zeigt es: „alle 12 Monate (aus dem Gerätetyp)". So muss man bei vierzig
|
||
Pressluftatmern nicht vierzigmal dasselbe eintragen. Schreibt der Hersteller
|
||
für eine bestimmte Charge etwas anderes vor, wird der Wert am Gerät gesetzt und
|
||
gewinnt dann.
|
||
|
||
Dasselbe gilt für die Lebensdauer. Ob sie ab Herstellung oder ab
|
||
Indienststellung zählt, hängt von der Vorschrift ab: Seile und Druckbehälter
|
||
altern ab Herstellung, Schutzkleidung meist ab Indienststellung.
|
||
|
||
### QR-Code
|
||
|
||
Rechts steht der QR-Code des Geräts. **Etikett drucken** erzeugt daraus einen
|
||
Aufkleber für den Geräteraum.
|
||
|
||
Der Code zeigt auf `.../g/<Inventarnummer>`. Wer ihn scannt und angemeldet ist,
|
||
landet direkt auf dieser Seite. Wer nicht angemeldet ist, landet zuerst auf der
|
||
Anmeldung und danach hier — **der Aufkleber ist keine Berechtigung.** Wer ihn
|
||
fotografiert, bekommt dadurch keinen Zugang.
|
||
|
||
Bewusst steht die Inventarnummer im Code und nicht die interne Kennung: Eine
|
||
UUID wäre 36 Zeichen lang, das Muster entsprechend fein, und auf einem
|
||
38 × 21 mm großen Aufkleber im staubigen Geräteraum wird das unzuverlässig.
|
||
|
||
### Verlauf
|
||
|
||
Unten steht, was mit dem Gerät passiert ist — direkt aus dem Audit-Log. Es gibt
|
||
bewusst keine zweite Historie: zwei Quellen könnten auseinanderlaufen.
|
||
|
||
---
|
||
|
||
## 5a. Ein Gerät anlegen oder ändern
|
||
|
||

|
||
|
||
Über **Neues Gerät** in der Geräteliste, oder über **Bearbeiten** auf der
|
||
Detailseite eines vorhandenen Geräts. Für größere Bestände ist der Import der
|
||
schnellere Weg; dieses Formular ist für den Neuzugang zwischendurch.
|
||
|
||
Pflicht sind nur zwei Angaben: **Inventarnummer** und **Bezeichnung**. Alles
|
||
andere kann später nachgetragen werden.
|
||
|
||
### Der Gerätetyp nimmt Arbeit ab
|
||
|
||
Wird oben ein Gerätetyp gewählt, gelten dessen Prüfintervall, Prüfvorschrift
|
||
und Lebensdauer — man muss sie nicht eintragen. Die Felder darunter bleiben
|
||
trotzdem änderbar: Was dort steht, überschreibt die Vorgabe für dieses eine
|
||
Gerät. Das braucht man, wenn der Hersteller für eine bestimmte Charge etwas
|
||
anderes vorschreibt.
|
||
|
||
Ist die Bezeichnung noch leer, wird sie aus dem Typ übernommen.
|
||
|
||
### „Daraus ergibt sich"
|
||
|
||
Der graue Kasten zeigt, was aus den Angaben folgt: wann die nächste Prüfung
|
||
fällig wird und wann die Nutzungsdauer endet. Er rechnet beim Tippen mit,
|
||
sodass man vor dem Speichern sieht, was herauskommt. Gerechnet wird mit
|
||
denselben Regeln wie auf dem Server — beim Speichern rechnet dieser erneut und
|
||
behält das letzte Wort.
|
||
|
||
Steht dort „keine", fehlt entweder das Prüfintervall oder die Indienststellung.
|
||
Das ist kein Fehler: Für Geräte ohne Prüfpflicht ist es der Normalfall.
|
||
|
||
### Wo liegt es?
|
||
|
||
Entweder an einem Standort **oder** auf einem Fahrzeug. Der Umschalter lässt
|
||
nur eines von beidem zu — deshalb kann hier gar nicht erst der Fehler
|
||
entstehen, den die Datenbank sonst abweisen würde.
|
||
|
||
Bei „Auf einem Fahrzeug" lässt sich zusätzlich der Geräteraum angeben. Er darf
|
||
leer bleiben: Manches liegt lose in der Kabine. Wird das Fahrzeug gewechselt,
|
||
wird die Raumangabe zurückgesetzt — der Raum gehört zum Fahrzeug.
|
||
|
||
Gibt es in der Wehr nur einen Standort, ist er beim Anlegen schon ausgewählt.
|
||
|
||
> Jede Änderung landet im Verlauf des Geräts. Wer wissen will, wann ein
|
||
> Prüftermin verschoben wurde und von wem, findet es dort.
|
||
|
||
---
|
||
|
||
## 6. Etiketten drucken
|
||
|
||
Der Bogen ist auf das gängige Format mit **65 Etiketten je DIN-A4-Seite**
|
||
(38,1 × 21,2 mm, 5 Spalten × 13 Zeilen) ausgelegt — den gibt es in jedem
|
||
Schreibwarenladen und er passt in jeden Laserdrucker.
|
||
|
||
Auf jedem Etikett stehen drei Dinge: der QR-Code, die Inventarnummer zum
|
||
Ablesen ohne Handy, und die Bezeichnung. Bei verlasteten Geräten kommt der
|
||
Funkrufname des Fahrzeugs dazu.
|
||
|
||
Der Druck kann an einer beliebigen Position des Bogens beginnen. Wer drei
|
||
Etiketten braucht, muss so keinen ganzen Bogen opfern.
|
||
|
||
> Erzeugt wird das PDF von Gotenberg, einem eigenen Container im Stack. Kommt
|
||
> die Meldung „Der Etikettenbogen ließ sich nicht erzeugen", läuft er nicht:
|
||
> `docker compose ps` zeigt es.
|
||
|
||
---
|
||
|
||
## 7. Gerät scannen
|
||
|
||
Über **Scannen** in der Geräteliste öffnet sich der QR-Scanner. Der Browser
|
||
fragt beim ersten Mal nach der Kamerafreigabe. Das Bild verlässt das Gerät
|
||
nicht — ausgewertet wird direkt im Browser.
|
||
|
||
Zwei Dinge sind bewusst so gebaut:
|
||
|
||
- **Es geht immer auch ohne Kamera.** Darunter steht ein Eingabefeld für die
|
||
Inventarnummer. Wenn die Freigabe verweigert wurde oder das Etikett zerkratzt
|
||
ist, tippt man sie ein.
|
||
- **Fremde Codes brechen nichts ab.** Der Barcode des Herstellers klebt oft
|
||
direkt daneben. Wird er erkannt, kommt ein Hinweis statt einer Fehlermeldung.
|
||
|
||
---
|
||
|
||
## 8. Fahrzeuge
|
||
|
||

|
||
|
||
Die Übersicht zeigt die Fahrzeuge als Kacheln — eine Wehr hat drei bis zehn,
|
||
nicht dreihundert. Auf jeder Kachel stehen der nächste TÜV-Termin und die Zahl
|
||
der verlasteten Geräte.
|
||
|
||
Auf der Detailseite steht die **Beladung nach Geräteräumen**, in der Reihenfolge
|
||
der Beladeordnung: G1 bis G6, Heck, Dach, Mannschaftsraum. Genau so sucht der
|
||
Gerätewart — nicht „wo ist die Motorsäge", sondern „was ist in G3".
|
||
|
||
Beim Anlegen eines neuen Fahrzeugs werden diese Räume auf Wunsch gleich
|
||
mitangelegt. Ein Mannschaftstransportwagen bekommt weniger Fächer als ein
|
||
Löschfahrzeug.
|
||
|
||
Ein Gerät liegt **entweder** an einem Standort **oder** auf einem Fahrzeug.
|
||
Beides gleichzeitig lässt die Datenbank nicht zu — das ist keine Formalität,
|
||
sondern verhindert, dass ein Gerät zweimal gezählt wird.
|
||
|
||
Was verlastet ist, aber keinem Raum zugeordnet wurde, erscheint unter „Ohne
|
||
Raumangabe" statt stillschweigend zu verschwinden.
|
||
|
||
---
|
||
|
||
## 9. Mitglieder und Qualifikationen
|
||
|
||

|
||
|
||
Die Mitgliederliste ist nach Abteilung gruppiert — Aktive, Jugendfeuerwehr,
|
||
Passive. Eine durchlaufende alphabetische Liste vermischt Gruppen, die in der
|
||
Wehr nichts miteinander zu tun haben.
|
||
|
||
Auf der Seite **Qualifikationen** steht oben, was abgelaufen ist oder demnächst
|
||
abläuft. Das ist der Grund, warum jemand diese Seite öffnet.
|
||
|
||
**Gewarnt wird mit der Vorwarnzeit des jeweiligen Typs**, nicht mit einer
|
||
pauschalen Frist. Eine G26.3-Untersuchung braucht 120 Tage Vorlauf, weil erst
|
||
ein Termin beim Betriebsarzt gefunden werden muss; eine jährliche Unterweisung
|
||
kommt mit deutlich weniger aus. Einsatzrelevante Qualifikationen sind
|
||
gekennzeichnet.
|
||
|
||
Das Ablaufdatum wird beim Speichern aus der Gültigkeitsdauer des Typs
|
||
gerechnet — lässt sich aber überschreiben. Auf einer Urkunde steht manchmal
|
||
etwas anderes, als die Regel hergäbe.
|
||
|
||
---
|
||
|
||
## 10. Daten importieren
|
||
|
||

|
||
|
||
Der Import geht in drei Schritten, und der mittlere ist der eigentliche Punkt.
|
||
|
||
### Schritt 1: Datei wählen
|
||
|
||
CSV oder Excel (`.xlsx`). Die erste Zeile muss die Spaltenüberschriften
|
||
enthalten. Höchstens 10 MB und 5.000 Zeilen.
|
||
|
||
Erkannt werden die Formate, die in der Praxis herauskommen:
|
||
|
||
| Fall | Wird verstanden |
|
||
|---|---|
|
||
| Trennzeichen | Semikolon, Komma, Tabulator, senkrechter Strich |
|
||
| Zeichensatz | UTF-8 (auch mit BOM) und Windows-1252 |
|
||
| Datum | 02.09.2026, 2.9.26, 2026-09-02, und die Seriennummer aus Excel |
|
||
| Zahlen | 1.234,56 und 1,234.56, mit und ohne Eurozeichen |
|
||
| Ja/Nein | ja, j, x, 1, wahr — und die Gegenstücke |
|
||
|
||
### Schritt 2: Zuordnung prüfen
|
||
|
||
FLORIAN rät die Zuordnung anhand der Überschriften. „Inv.-Nr." landet bei
|
||
Inventarnummer, „Fabrikat" bei Hersteller. Jede Zuordnung lässt sich ändern,
|
||
und die Vorschau passt sich sofort an.
|
||
|
||
**Die Vorschau ist der Grund, warum es diesen Zwischenschritt gibt.** Sie zeigt
|
||
Zeile für Zeile, was übernommen würde, und markiert rot, was nicht stimmt:
|
||
fehlende Pflichtfelder, unlesbare Daten, Inventarnummern, die in der Datei
|
||
doppelt vorkommen. Eine falsch zugeordnete Spalte verteilt sonst Seriennummern
|
||
auf Inventarnummern, und das merkt man erst Wochen später am Regal.
|
||
|
||
### Schritt 3: Übernehmen
|
||
|
||
Übernommen wird nur, was fehlerfrei ist. Zeilen mit Fehlern bleiben in der
|
||
Datei und lassen sich nach der Korrektur erneut einlesen.
|
||
|
||
**Vorhandene Datensätze aktualisieren** entscheidet, was mit Datensätzen
|
||
passiert, die es schon gibt: überspringen (Vorgabe) oder überschreiben.
|
||
Erkannt werden sie über die Inventarnummer, bei Mitgliedern über die
|
||
Mitgliedsnummer oder die E-Mail-Adresse.
|
||
|
||
Zum Schluss steht da, wie viele Datensätze angelegt, aktualisiert und
|
||
übersprungen wurden. Der ganze Vorgang steht auch im Protokoll.
|
||
|
||
---
|
||
|
||
## 11. 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.
|
||
|
||
---
|
||
|
||
## 12. 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.
|
||
|
||
---
|
||
|
||
## 13. 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`.
|
||
|
||
---
|
||
|
||
## 14. 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. Am Gerät und am Fahrzeug ist der Verlauf bereits auf der
|
||
jeweiligen Detailseite sichtbar.
|
||
|
||
---
|
||
|
||
## 15. 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 |
|
||
| Etikettenbogen lässt sich nicht erzeugen | Gotenberg läuft nicht | `docker compose ps`, dann `docker compose up -d gotenberg` |
|
||
| Kamera bleibt schwarz | Freigabe abgelehnt, oder die Seite läuft nicht über HTTPS | Der Browser fragt nur bei HTTPS oder localhost. Sonst die Inventarnummer eintippen |
|
||
| Import meldet fehlende Pflichtfelder | Keine Spalte ist Inventarnummer oder Bezeichnung zugeordnet | In Schritt 2 die Zuordnung von Hand setzen |
|
||
| Umlaute im Import verstümmelt | Die Datei ist weder UTF-8 noch Windows-1252 | In Excel als „CSV UTF-8" speichern |
|
||
| Gerät lässt sich nicht speichern | Standort **und** Fahrzeug gesetzt, oder keines von beidem | Genau eines von beiden angeben |
|
||
| `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.
|