Files
Florian-netz/docs/superpowers/specs/2026-06-08-floriannetz-design.md
Claude c054c834d3 Fold NÖ findings into spec; fix Allrad naming (HLFA n, A infixed)
- Bundesland confirmed: Niederösterreich; spec references seed catalog
- Vorlagen list corrected to NÖ HLF system + aliases + Allrad rule
- Geräte-Kategorien derived from Beladelisten
- Allrad designation is HLFA n (A infixed), not 'HLF n A'

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:07:22 +02:00

143 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FlorianNetz — Design-Spezifikation
**Datum:** 2026-06-08
**Status:** Entwurf (zur Freigabe)
**Sprache der Anwendung:** Deutsch (österreichischer Feuerwehr-Standard, ÖBFV)
---
## 1. Zweck
FlorianNetz ist eine **ausschließlich für authentifizierte Benutzer zugängliche** Web-Plattform, auf der österreichische Freiwillige Feuerwehren ihre **Fahrzeuge** und **Geräte/Ausrüstung** erfassen. Andere Wehren können diese durchsuchen, um für die überörtliche Hilfe die **am schnellsten eintreffende** Ressource zu finden.
**Oberstes Prinzip:** Kein anonymer Zugriff — *kein einziger* Seiten- oder API-Endpunkt ist ohne gültige Sitzung erreichbar (default-deny, serverseitig erzwungen).
### Nicht-Ziele (vorerst)
- Kein In-App-Ausleih-/Anfrage-Workflow (nur Kontaktaufnahme out-of-band; für später vorgesehen, Datenmodell offen halten).
- Keine 2-Faktor-Authentifizierung in v1 (später leicht ergänzbar).
- Keine Kopplung mit `feuerwehr_dashboard` in v1 (Architektur hält die spätere Kopplung offen).
- Kein Mehrmandanten-SaaS — eine selbst gehostete Instanz mit vielen Wehren.
---
## 2. Rollen & Zugriff
| Rolle | Login über | Berechtigungen |
|---|---|---|
| **Platform-Admin** | Authentik SSO (OIDC) | Verwaltet die Taxonomie (Merkmal-Katalog, Fahrzeug-Vorlagen inkl. zugeordneter Merkmale & Auswahloptionen, Geräte-Kategorien); gibt von Wehren vorgeschlagene Merkmale frei; legt Wehren an und stellt den ersten Wehr-Admin-Login bereit; sieht Audit-Log |
| **Wehr-Admin** | App-Konto (erster Login vom Platform-Admin bereitgestellt) | Verwaltet das eigene Wehr-Profil, eigene Fahrzeuge & Geräte (Werte, Zuordnung, Status); **legt weitere Wehr-Admins und Lese-Benutzer der eigenen Wehr selbst an** |
| **Lese-Benutzer** | App-Konto (vom Wehr-Admin angelegt) | Durchsucht und sieht alle Wehren-Daten inkl. Kontaktinfo; **keine** Bearbeitung |
- Zwei Authentifizierungswege, **eine** einheitliche Sitzung mit `role` + `brigadeId`.
- Autorisierung serverseitig: Schreibzugriffe sind auf die eigene Wehr beschränkt; Lesezugriff über alle Wehren (offenes Verzeichnis).
- **Login-Seite zeigt ausschließlich den Login** — keine weiteren Inhalte.
---
## 3. Informationsarchitektur
- **Startseite (nach Login):** eine globale Suche mit prominentem „Meinen Standort verwenden" (GPS). Tabs: **Fahrzeuge · Geräte · Wehren**.
- **Pro Tab:** Namens-/Funkrufnamen-Suche + **dynamisch aus dem Merkmal-Katalog erzeugte Filter** (typabhängig: Schieberegler / Dropdown / Ja-Nein-Schalter) + Status-Filter („nur einsatzbereit").
- **Trefferliste:** sortiert nach **Eintreffzeit (Fahrzeit-ETA)**; jede Zeile zeigt Wehr, Eckdaten, ETA/Entfernung, Status und **📞 Kontakt** (Telefonnummer der Wehr).
- **Detailseiten:**
- *Fahrzeug:* Eckdaten-Raster (aus Merkmalwerten) · **Beladung** als verlinkte Geräteliste · Wehr-Karte · expliziter Button **„Wehr kontaktieren"**.
- *Gerät:* Merkmale · zugeordnetes Fahrzeug **oder** „im Gerätehaus" · Wehr-Karte · Kontakt.
- *Wehr:* Profil (Adresse, Kontakt, Funkrufnamen-Schema), eigener Fuhrpark, Kontakt.
### Design / Corporate Identity
- Richtung **„Amtlich"**: seriös, behördlich, ruhig. Navy-Primärfarbe, Signalrot als Akzent/Signal, Serifen-Überschriften, viel Weißraum, tabellarische Ziffern.
- Logo **„Netzknoten"**: vernetzte Wehren-Knoten um einen roten Mittelpunkt (Florian + Netz).
- Palette: Navy `#1B3A5B`, Signalrot `#E2231A`, Anthrazit `#1A2530`, Nebelgrau `#F6F8FA`, Grün/bereit `#1F8F5A`, Bernstein/Wartung `#B5460F`.
- Typografie: Serif-Display (Source Serif / Georgia) für Überschriften, humanistische Sans (Inter / System-UI) für UI, Tabular-Nums für Datenspalten.
- Bewusst **kein** generischer „KI-Look" (keine Verlaufsflächen, nicht alles abgerundet, keine Emoji-Dekoration).
---
## 4. Dynamisches Merkmal-System (Kernstück)
Ein starres Spaltenschema skaliert nicht — die Vielfalt an Fahrzeugen/Geräten ist zu groß. Stattdessen typisierte, vom Admin gepflegte Merkmale.
- **Merkmal-Katalog** (Admin): jedes Merkmal hat Name, Typ (`Zahl` / `Auswahl` / `Ja-Nein` / `Text`), optional Einheit; bei `Auswahl` definiert der Admin die erlaubten Optionen.
- **Fahrzeug-Vorlagen** (Admin): definieren die niederösterreichischen Standard-Bezeichnungen (HLF 1, HLF 1 W, HLF 2, HLF 3, HLF 4 inkl. HLF 4-U, VRF, VF, ALF, SSTF, WLF, MTF) und **pro Vorlage, welche Merkmale gelten** sowie deren Optionen/Vorgabewerte. Inklusive Standard-Beladung. Jede Vorlage führt **Such-Aliasse** (frühere/umgangssprachliche Namen wie RLF/RLFA 2000, KLFA, TLFA …) und eine **Allrad-Namensregel** (Einschub „A" in die Abkürzung, z. B. „HLFA 3" = `Allradantrieb = Ja`, gleiche Vorlage wie „HLF 3"). Seed-Daten siehe `docs/reference/fahrzeug-katalog-noelfv.md`.
- **Geräte-Kategorien** (Admin): je Kategorie ein eigener Merkmal-Satz. Anfangsbestand wird aus den **Beladelisten** der Fahrzeug-Richtlinien abgeleitet (Atemschutz, hydraul. Rettungsgerät, Pumpen, Schläuche/Armaturen, Strom/Licht, Schadstoff/Ölwehr …); per Hybrid-Governance erweiterbar.
- **Anlegen durch die Wehr:** Vorlage wählen (füllt Merkmale & Beladung vor, alles editierbar) **oder** „Eigenes Fahrzeug/Gerät" frei aus dem Katalog zusammenstellen.
- **Hybrid-Governance:** Ein von einer Wehr neu angelegtes Merkmal ist **sofort** am eigenen Objekt nutzbar und auf dessen Detailseite sichtbar; es wird **erst nach Admin-Freigabe** (prüfen / zusammenführen) zu einem **globalen Suchfilter**. Hält die Suche über alle Wehren konsistent.
- **Suche:** Filter werden dynamisch aus dem Katalog erzeugt; der Typ bestimmt das Eingabeelement und die Abfrage (Range / Gleichheit / Boolean).
---
## 5. Geografie & „am schnellsten eintreffend"
- Jede Wehr hat Koordinaten (aus der Adresse geokodiert).
- Suchstandort = Geräte-GPS **oder** eingegebene Adresse (geokodiert).
- Treffer werden nach **Fahrzeit-ETA** von der jeweils besitzenden Wehr zum Suchstandort sortiert.
- **Selbst gehostet** zur Wahrung der Privatsphäre (keine Standortdaten an Dritte):
- **OSRM** für Fahrzeit/Routing (Österreich-OSM-Extrakt), als Distanz-/Zeit-Matrix.
- **Nominatim** (oder Photon) für Geokodierung (Österreich-Extrakt).
- **Fallback:** Luftlinie (Haversine), falls der Routing-Dienst nicht erreichbar ist — klar gekennzeichnet.
- Optionale **MapLibre-GL**-Kartenansicht (Liste bleibt primär).
---
## 6. Datenmodell (PostgreSQL, indikativ)
> Typisierte Merkmalwerte in einer eigenen Wert-Tabelle (EAV mit typisierten Spalten) für saubere Range-/Enum-/Boolean-Indizes.
- **brigades**: `id, name, art (FF), strasse, plz, ort, bundesland, lat, lng, funkrufname_schema, telefon, email, wehrführer, aktiv, erstellt_am`
- **users**: `id, brigade_id (NULL für Platform-Admin), rolle (platform_admin | wehr_admin | wehr_read), auth_typ (authentik | local), email, name, passwort_hash (NULL bei authentik), aktiv, erstellt_von, erstellt_am`
- **merkmale** (Attributdefinitionen): `id, name, typ (number|enum|boolean|text), einheit, geltungsbereich (vehicle|equipment|both), status (active|proposed), vorgeschlagen_von_brigade_id, erstellt_am`
- **merkmal_optionen** (für `enum`): `id, merkmal_id, wert, label, reihenfolge`
- **vehicle_templates** (Vorlagen): `id, code (z. B. "TLFA 4000"), name, beschreibung`
- **vehicle_template_merkmale**: `template_id, merkmal_id, vorgabewert (NULL), pflicht (bool), reihenfolge`
- **equipment_categories**: `id, name`
- **equipment_category_merkmale**: `category_id, merkmal_id, reihenfolge`
- **vehicles**: `id, brigade_id, template_id (NULL bei Eigenbau), name, funkrufname, status (einsatzbereit|wartung|ausser_dienst), notiz, erstellt_am`
- **equipment**: `id, brigade_id, category_id, vehicle_id (NULL = im Gerätehaus), name, status, erstellt_am`
- **merkmal_values**: `id, merkmal_id, entity_typ (vehicle|equipment), entity_id, value_num, value_text, value_bool` — Indizes: `(merkmal_id, value_num)`, `(merkmal_id, value_bool)`, `(merkmal_id, value_text)`; sowie `(entity_typ, entity_id)`
- **audit_log**: `id, actor_user_id, aktion, ziel_typ, ziel_id, details (jsonb), zeitpunkt`
---
## 7. Tech-Stack
- **Next.js (App Router, TypeScript)** — Full-Stack (Server Components + Route Handlers / Server Actions), wenig Boilerplate.
- **PostgreSQL** + **Drizzle ORM** — typsicher und SQL-nah; besser geeignet als Prisma für die dynamischen Merkmal-Abfragen (Range/Enum-Filter).
- **Auth.js (NextAuth v5)** — Authentik-OIDC-Provider (Platform-Admins) + Credentials-Provider mit **argon2id** (Wehr-Konten); **default-deny Middleware** auf allen Routen; einheitliche Session mit `role` + `brigadeId`.
- **Tailwind CSS + Radix-UI-Primitives**, eigens auf die „Amtlich"/Netzknoten-Identität gethemt (volle Kontrolle über den eigenständigen Look; bewusst nicht MUI).
- **Zod** (Validierung), **MapLibre GL** (Karte), **OSRM + Nominatim** (Docker, Österreich-Extrakt).
- **Tests:** **Playwright** (E2E, insbesondere Auth-Gating-Tests) + **Vitest** (Units).
---
## 8. Sicherheit
- **Default-deny** serverseitig auf allen Seiten **und** API-Routen; keine reine Client-Absicherung.
- Rollen- + Wehr-Scoping bei **jedem** Schreibzugriff serverseitig erzwungen.
- **argon2id**-Passwort-Hashing; sichere Cookies (`httpOnly`, `secure`, `sameSite`); CSRF-Schutz; Rate-Limiting am Login; Security-Header.
- **Audit-Log** für Admin- und Bereitstellungsaktionen (Wehr/Benutzer anlegen, Merkmal-Freigaben).
- 2FA für Wehr-Konten als spätere, einfache Erweiterung vorgesehen.
---
## 9. Deployment
- **Docker Compose**: Container für `app` (Next.js), `postgres`, `osrm`, `nominatim`.
- **Reverse Proxy:** vorhandener **Traefik** (extern). Kein gebündelter Proxy. Der App-Container wird per **Traefik-Labels** (Router/Service-Port, TLS, ggf. Middlewares) am externen Traefik-Netzwerk angebunden.
- Die App muss `X-Forwarded-Proto`/`X-Forwarded-Host` von Traefik vertrauen, damit sichere Cookies und Auth.js-Callback-URLs korrekt funktionieren; vertrauenswürdige Hosts konfigurieren.
- **Authentik:** Redirect-URI der App in Authentik registrieren; Client-ID/-Secret + Issuer-URL via Umgebungsvariablen.
---
## 10. Eingaben / Abhängigkeiten
- **Bundesland: Niederösterreich** (bestätigt) — maßgeblich NÖ-LFV-Richtlinien (FA 0110) + ÖBFV FA 30; relevant fürs Funkrufnamen-Schema.
- **Standarddokumente vorhanden:** 11 Fahrzeug-Richtlinien in `docs/reference/` analysiert; Seed-Katalog (Vorlagen, Merkmal-Katalog, Aliasse) in `docs/reference/fahrzeug-katalog-noelfv.md`.
- **Geräte-Kategorien:** Anfangsbestand aus den Beladelisten abgeleitet (siehe §4); separate Geräte-Standarddokumente können später ergänzt werden.
- **Offen (nicht blockierend):** Bestätigung der noch tentativen Such-Aliasse (KLF/KLFA, TLFA, GTLF, KRF/KRFA, LAST, WLFA, MTFA) — als Aliasse jederzeit im Admin pflegbar.
---
## 11. Zukünftige Kopplung mit `feuerwehr_dashboard`
In v1 eigenständig, aber kopplungsfreundlich: gemeinsamer **Authentik**-Identitätsanbieter, kompatible **PostgreSQL**- und **Docker/Traefik**-Konventionen. Eine spätere Integration (geteilte Identitäten, Querverweise) bleibt dadurch unkompliziert.