FleetFlow — Projektdokumentation
Requirements Engineering · Architektur- und Datenmodell · Sicherheitskonzept · Modelle
| Projekt | FleetFlow — Flottenmanagement für Autovermietungen |
| Autoren | Felix, Marvin, Pit, Simon |
| Repository | https://github.com/GartmannPit/FleetFlow |
| Live-System | https://fsse-fleetflow.de/ |
| Stand | 30.07.2026 (main, inkl. Versicherungsmodul PR #57) |
Die zugehörige Testdokumentation liegt als eigenes Dokument vor: Testdokumentation.md. Eine vollständige API-Referenz aller Endpunkte: API-Referenz.md. Das technische Detailkonzept zur Zwei-Faktor-Authentifizierung: 2fa-technisches-konzept.md.
1. Projektübersicht
FleetFlow ist ein webbasiertes Flottenmanagementsystem für eine Autovermietung. Ziel des Projekts ist die Digitalisierung der bislang manuellen Prozesse rund um Fahrzeugverwaltung, Kundenverwaltung, Buchungsabwicklung, Fahrzeugübergabe, Werkstattabwicklung, Versicherungsverwaltung und Abrechnung — einschließlich einer KI-gestützten Erkennung von Fahrzeugschäden anhand von Fotos und einer dynamischen, auslastungsabhängigen Preisberechnung.
Zentrale konzeptionelle Bestandteile des Projekts sind darüber hinaus:
- ein dynamisches Rollen- und Berechtigungssystem, das über die drei ursprünglich vorgesehenen Rollen (Admin, Mitarbeiter, Kunde) hinaus beliebige weitere Rollen mit granular einstellbaren Rechten erlaubt — konkret umgesetzt am Beispiel einer eigenen Werkstatt-Rolle mit echtem Übergabe-Workflow zwischen Mitarbeiter und Werkstatt,
- eine verpflichtende Zwei-Faktor-Authentifizierung (TOTP) für alle Konten, durchgesetzt in der Middleware vor jedem Request,
- ein Versicherungsmodul mit Policen-Historie (PDF-Upload), Ablauf-Warnungen und Schadensfall-Verwaltung.
FleetFlow ist bewusst ein reines Mitarbeiter-System (staff-only): Es gibt keine kundenseitigen Zugänge (siehe Abschnitt 2.5).
2. Requirements Engineering
2.1 Vorgehen
Die Anforderungen wurden modulweise erhoben und als GitHub-Issues verwaltet; jedes Issue durchlief den Zyklus Anforderung → Implementierung auf Feature-Branch → Pull Request → Review → Merge. Größere bzw. zunächst unklare Anforderungen (z. B. Fahrzeugaufbereitung, #29) wurden vor der Umsetzung in einer kurzen Klärungsrunde präzisiert (Trigger, Durchsetzung, Rücksetz-Verhalten). Der Anforderungsstand wurde iterativ um im Projektverlauf entstandene Zusatzanforderungen erweitert (dynamisches Rechtesystem #38–#41, Werkstatt-Workflow #42, 2FA #44, Versicherungsmodul #55). Entscheidungen und Begründungen sind pro Feature im FEATURE_LOG.md nachvollziehbar dokumentiert.
2.2 Stakeholder und Rollen
| Stakeholder | Interesse / Aufgaben |
|---|---|
| Admin | Firmendaten, Preis- und Aufbereitungseinstellungen, Benutzerverwaltung, Definition von Rollen und Berechtigungen |
| Mitarbeiter | Operatives Tagesgeschäft: Fahrzeuge, Kunden, Buchungen, Übergaben, Rechnungen, Versicherungen |
| Werkstatt | Bearbeitet ausschließlich die ihr übergebenen Reparatur-/Wartungsaufträge; kein Zugriff auf Kunden-, Buchungs- oder die vollständige Fahrzeugliste |
| Kunde | Im Erstentwurf als eigene Rolle vorgesehen; im RE-Prozess wurde entschieden, dass FleetFlow keine kundenseitigen Zugänge anbietet (Abschnitt 2.5). Die Rolle existiert aus historischen Gründen im Datenmodell weiter, wird aber nicht mit Konten befüllt |
2.3 Funktionale Anforderungen (je Modul)
Fahrzeugverwaltung
- Anlegen, Bearbeiten und Löschen von Fahrzeugen mit Stammdaten, Versicherung, HU/TÜV- und Service-Terminen.
- Statusverwaltung (verfügbar, vermietet, in Wartung, ausgemustert) inkl. automatischer Statusübergänge durch Buchungen und Werkstattaufträge.
- Fälligkeitswarnungen für HU/TÜV, Service, Fahrzeugaufbereitung und Versicherungsablauf — als reiner Hinweis, ohne Buchungen zu blockieren.
- Freitextsuche, Filter nach Status/Typ, serverseitige Pagination (50 Zeilen/Seite).
Kundenverwaltung
- Verwaltung von Privat- und Firmenkunden mit jeweils passenden Pflichtfeldern (Privat: Vor-/Nachname, Führerschein; Firma: Firmenname, Ansprechpartner, USt-IdNr.).
- Anzeige der vollständigen Buchungshistorie je Kunde.
Buchungsverwaltung
- Status-Workflow Anfrage → Angebot → Bestätigt → Aktiv → Zurückgegeben → Abgeschlossen (+ Storniert jederzeit).
- Verfügbarkeitsprüfung gegen bestehende, blockierende Buchungen desselben Fahrzeugs — doppelt abgesichert (Anwendungsprüfung + Datenbank-Constraint, Abschnitt 3.6).
- Dynamische Preisberechnung anhand von Saison, Wochentag, Flottenauslastung und Buchungsdauer, mit vom Admin konfigurierbaren Faktoren.
- Automatisierte Extraktion von Buchungsdaten aus Kunden-E-Mails per KI (Gemini) mit Vorbefüllung des Formulars.
- Generierung und Versand von Angebot und Auftragsbestätigung als PDF per E-Mail.
Fahrzeugübergabe
- Protokollierung von Ausgabe und Rückgabe je Buchung inkl. Kilometerstand, Tankstand, Fahrzeugzustand innen/außen und mitgegebenen Gegenständen (Checkliste).
- Foto-Upload mit automatischer KI-Schadenserkennung; mehrere Fotos parallel wählbar, jedes wird unabhängig hochgeladen und analysiert (Batch-Upload mit Hintergrund-Analyse).
- Druckbares Übergabeprotokoll.
- Eigenständige, buchungsunabhängige Schadensmeldungen direkt am Fahrzeug.
Rechnungswesen
- Rechnungserstellung aus abgeschlossenen Buchungen mit automatischer, fortlaufender Rechnungsnummer und Netto-/MwSt.-/Brutto-Berechnung (Standard 19 %, Bruttopreis als Ausgangswert).
- Statuspflege Entwurf → Versendet → Bezahlt (oder Storniert) mit gültigen Statusübergängen und Überfälligkeits-Erkennung.
- PDF-Erzeugung (mit Caching im Storage) und E-Mail-Versand.
Berichte
- Monatlicher Umsatzverlauf (realisiert vs. offene Forderungen), Flottenauslastung je Fahrzeug (30-/90-Tage-Fenster), Top-Fahrzeuge/-Kunden nach Umsatz, Buchungs-Trichter nach Status, Kennzahlen-Karten (u. a. Stornoquote).
Rollen- und Berechtigungssystem
- Ablösung des festen Rollen-Enums durch ein dynamisches Modell: Admins können eigene Rollen anlegen, umbenennen und löschen (außer den drei geschützten Grundrollen).
- Pro Rolle und Funktionsbereich (11 Ressourcen) einzeln einstellbare Rechte: Sehen, Bearbeiten, Löschen.
- Durchsetzung auf zwei Ebenen: Datenbank (Row-Level-Security) und Anwendung (serverseitige Prüfung vor jeder schreibenden Aktion) — Rechte lassen sich nicht über direkte API-Aufrufe umgehen.
- Rollenabhängige Navigation: nicht zugängliche Bereiche werden in der Seitenleiste ausgeblendet.
- Benutzerverwaltung inkl. nachträglicher Rollenzuweisung zu bestehenden Konten.
Werkstatt-Workflow
- Eigene, vordefinierte (aber anpassbare) Rolle „Werkstatt" mit minimalem Zugriff — nur eigene Aufträge und Schadensfotos.
- Echter Übergabe-Workflow mit Zustandsautomat Offen → In Bearbeitung → Abgeschlossen.
- Automatische Synchronisation des Fahrzeugstatus mit dem Werkstattauftrag (Übergabe → „in Wartung", Abschluss → „verfügbar") über einen Datenbank-Trigger.
- Eigenes Werkstatt-Dashboard als Startseite für diese Rolle.
Versicherungsmodul
- Verwaltung mehrerer Versicherungspolicen je Fahrzeug (Haftpflicht/Teilkasko/Vollkasko) mit Gültigkeitszeitraum und PDF-Upload in einen privaten Storage-Bucket; abgelaufene Policen bleiben als Historie erhalten.
- Ablauf-Warnung bei bald ablaufender oder abgelaufener Police (Fahrzeugdetail + Dashboard).
- Schadensfall-Verwaltung mit Status Offen → In Bearbeitung → Abgeschlossen; Schadensfotos (aus Übergabe oder Schadensmeldung) können zusätzlich einem Schadensfall zugeordnet werden.
Authentifizierung & Onboarding
- Verpflichtende Zwei-Faktor-Authentifizierung (TOTP) für jedes Konto: Zwangs-Einrichtung beim ersten Login, Code-Abfrage bei jedem weiteren Login, Faktor-Reset in den Sicherheitseinstellungen.
- Einzelmandanten-Onboarding: Die Firma registriert sich genau einmal (Firmendaten + Admin-Konto in einem Schritt); danach ist die öffentliche Registrierung gesperrt. Mitarbeiterkonten legt ausschließlich der Admin über die Benutzerverwaltung an.
2.4 Nicht-funktionale Anforderungen
| Kategorie | Anforderung |
|---|---|
| Sicherheit | Zugriffskontrolle auf zwei Ebenen (RLS + Anwendungslogik); verpflichtende 2FA; Service-Role-Schlüssel ausschließlich serverseitig; Schutz vor Selbst-Rechteausweitung per DB-Trigger |
| Nachvollziehbarkeit | Sicherheitsrelevante Änderungen (z. B. Rollenwechsel) nur mit entsprechendem Recht; Feature-Entscheidungen im FEATURE_LOG dokumentiert |
| Erweiterbarkeit | Neue Funktionsbereiche lassen sich in die Berechtigungsmatrix aufnehmen, ohne bestehende Rollen anzufassen; Dokument-Pipeline (PDF + E-Mail) als wiederverwendbares Muster |
| Ausfalltoleranz der KI/E-Mail-Integration | Schadenserkennung, E-Mail-Extraktion und E-Mail-Versand funktionieren ohne konfigurierte API-Schlüssel über deterministische Ersatzmechanismen (Mock/Simulation) weiter |
| Benutzbarkeit | Rollenabhängige, aufgeräumte Oberfläche; Erfolgs-Toasts; Warnungen statt Blockaden; Deep-Links vom Dashboard in vorgefilterte Listen |
| Performance | Serverseitige Pagination und Suche; Server Components für Datenzugriffe; Realtime-Updates nur wo fachlich sinnvoll (Buchungsliste) |
2.5 Im RE-Prozess verworfene Anforderung: Kundenportal
Ein Kundenportal (eigener Log-in für Kunden mit Einblick in die eigenen Buchungen, Issue #14) wurde vollständig implementiert, in der Verifikation jedoch wieder verworfen — aus zwei Gründen:
- Ein End-to-End-Test mit einem Wegwerf-Testkonto deckte eine Sicherheitslücke in den RLS-Policies auf: Das Kundenkonto war zwar korrekt auf die eigenen Buchungen beschränkt, konnte aber sämtliche Kunden- und Fahrzeugdaten lesen.
- Parallel wurde im Projektverlauf die Produktentscheidung getroffen, dass FleetFlow grundsätzlich keine kundenseitigen Konten vorsieht.
Die Funktion wurde daraufhin vollständig zurückgebaut (Datenbankänderungen, Oberfläche, Weiterleitungslogik) und das Issue als „won't do" geschlossen. Der Vorgang ist im FEATURE_LOG.md (#14) vollständig dokumentiert — bewusst auch als Absicherung dagegen, dass die Anforderung später unbeabsichtigt erneut umgesetzt wird.
3. Architektur- und Datenmodell
3.1 Technologiestack
| Schicht | Technologie |
|---|---|
| Framework | Next.js 14 (App Router), React 18, TypeScript (strict) |
| Oberfläche | Tailwind CSS 3, shadcn/ui (Radix-Primitives), lucide-react Icons |
| Karte | Leaflet.js + react-leaflet + OpenStreetMap |
| Datenbank | Supabase (PostgreSQL) mit Row-Level-Security |
| Authentifizierung | Supabase Auth (JWT, SSR-Cookies via @supabase/ssr) inkl. MFA/TOTP |
| Storage | Supabase Storage (Buckets: damage-photos, Rechnungs-PDFs, insurance-policies) |
| Realtime | Supabase Realtime (Buchungs-/Fahrzeugtabellen) |
| KI | Google Gemini (gemini-2.5-flash): Vision für Schadenserkennung, Textmodell für E-Mail-Extraktion — jeweils mit deterministischem Mock-Fallback |
@react-pdf/renderer (Rechnung, Angebot, Auftragsbestätigung) | |
| Resend-API mit Simulations-Fallback ohne API-Schlüssel | |
| Diagramme | recharts (Berichte-Modul) |
| Deployment | Vercel (App) + Supabase Cloud (Datenbank/Auth/Storage) |
3.2 Architekturüberblick
Architekturprinzipien:
- Lesen über Server Components, Schreiben über API-Routen. Seiten unter
src/app/(app)/sind asynchrone Server Components und lesen direkt über den serverseitigen Supabase-Client. Alle Mutationen (Anlegen/Ändern/Löschen) laufen über Next.js-API-Routen, die vor jeder Aktion die Berechtigung prüfen. - Middleware als Sicherheits-Chokepoint.
src/middleware.tsläuft vor praktisch jedem Request: Session-Refresh plus 2FA-Durchsetzung (Abschnitt 4.3). Neue geschützte Bereiche sind damit automatisch abgesichert. - Graceful Degradation externer Dienste. Gemini und Resend sind optionale Abhängigkeiten; ohne Schlüssel greifen deterministische Fallbacks (Mock-Analyse, Regex-Extraktion, E-Mail-Simulation mit Log-Ausgabe).
3.3 Projektstruktur (Auszug)
src/
app/
(app)/ # geschützte Seiten (Dashboard, vehicles, customers, bookings,
# invoices, reports, werkstatt, team, settings)
api/ # REST-Routen (siehe API-Referenz.md)
login/ # Login + Firmen-Registrierung (Einzelmandant)
mfa/ # 2FA: enroll (Ersteinrichtung), challenge (Code-Abfrage)
components/ # Formulare, Tabellen, Karte, Rollen-/Team-Manager, UI-Primitives
lib/
supabase/ # Browser-/Server-/Admin-/Middleware-Clients
ai/ # Gemini-Integration (Vision + Text) mit Mock-Fallback
pdf/ # Rechnung / Angebot / Auftragsbestätigung
email/ # Resend-Wrapper mit Simulations-Fallback
pricing.ts # Dynamic Pricing (konfigurierbare Faktoren)
availability.ts # Verfügbarkeits-/Kollisionsprüfung
bookings.ts # Status-Workflow + Fahrzeugstatus-Mapping
invoices.ts # Rechnungsbeträge, Statusübergänge, Überfälligkeit
warnings.ts # HU/Service/Führerschein/Aufbereitung/Versicherungs-Warnungen
reports.ts # Aggregationen für das Berichte-Modul
supabase/migrations/ # 21 SQL-Migrationen (Schema, RLS, Trigger, Seed)
docs/ # diese Dokumentation
3.4 Datenmodell
| Tabelle | Zweck |
|---|---|
profiles | Verknüpft ein Auth-Konto mit Rolle und Firma; automatisch per Trigger beim Anlegen eines Users erzeugt |
roles | Frei definierbare Rollen; die drei Grundrollen (admin, mitarbeiter, kunde) sind als Systemrollen geschützt (nicht lösch-/umbenennbar) |
permissions | Berechtigungsmatrix: je Rolle × Ressource ein Datensatz mit can_read / can_write / can_delete |
companies | Firmendaten der Vermietung (Absenderdaten auf allen PDF-Dokumenten); Fundament des Einzelmandanten-Onboardings |
settings | Singleton-Zeile (per Check-Constraint garantiert genau eine Zeile) mit globalen Einstellungen: Aufbereitungsintervall, Preisfaktoren |
vehicles | Fahrzeugstammdaten, Status, HU/Service-Termine, GPS-Position, Tagespreis, letzte Aufbereitung |
customers | Privat-/Firmenkunden inkl. Führerschein- bzw. USt-IdNr.-/Ansprechpartner-Feldern |
bookings | Verknüpft Fahrzeug + Kunde, Zeitraum (inklusiv), Status, Gesamtpreis |
handovers | Übergabe-/Rückgabeprotokoll je Buchung: km, Tankstand, Zustand innen/außen, Gegenstände-Checkliste |
damage_photos | Schadensfotos mit KI-Analysetext; per Check-Constraint entweder einer Übergabe oder direkt einem Fahrzeug zugeordnet (nie beidem), optional zusätzlich einem Versicherungsfall |
invoices | Rechnung zu einer abgeschlossenen Buchung: fortlaufende Nummer, Netto/MwSt./Brutto, Status, Fälligkeit |
werkstattauftraege | Werkstatt-Übergabeaufträge je Fahrzeug mit Status offen / in_bearbeitung / abgeschlossen |
versicherungspolicen | Policen-Historie je Fahrzeug (Anbieter, Police-Nr., Typ, Gültigkeitszeitraum, PDF im Storage) |
versicherungsfaelle | Versicherungs-Schadensfälle je Fahrzeug mit Status-Workflow |
Das vollständige UML-Klassendiagramm des Datenmodells:
Ergänzend liegt im Repository ein Use-Case-Diagramm (use-case-diagram.drawio, draw.io-Quelldatei) vor.
3.5 Rollen- und Berechtigungsmodell
Anstelle eines starren, im Code festgelegten Rollen-Enums verwendet FleetFlow ein datengetriebenes Modell: Jede Rolle ist eine Zeile in roles, jede Berechtigung eine Zeile in permissions mit drei booleschen Feldern (Sehen/Bearbeiten/Löschen) je Funktionsbereich. Die Matrix umfasst elf Ressourcen:
vehicles, customers, bookings, handovers, damage_photos, invoices, settings, companies, users, werkstatt, insurance
Eine zentrale Datenbankfunktion has_permission(resource, action) prüft für den angemeldeten Benutzer, ob das jeweilige Recht vorliegt. Sie wird an zwei Stellen verwendet:
- in den RLS-Policies jeder betroffenen Tabelle (und der Storage-Buckets) — als letzte, nicht umgehbare Kontrollinstanz,
- in der Anwendungslogik vor jeder schreibenden Aktion — damit fehlende Rechte sofort mit einer klaren Fehlermeldung (HTTP 403) beantwortet werden statt mit einem unspezifischen Datenbankfehler.
Zusätzlich verhindert eine Datenbank-Triggerfunktion, dass ein Benutzer ohne das entsprechende Recht die eigene Rolle oder Firmenzuordnung ändert — auch bei direktem Zugriff auf die Datenbank-API unter Umgehung der Oberfläche (Anti-Privilege-Escalation, Migrationen 0014/0018).
Die Navigation blendet Bereiche ohne Leserecht aus; Änderungen an der Berechtigungsmatrix wirken sofort — sowohl sichtbar (Navigation, deaktivierte Buttons mit Tooltip) als auch tatsächlich (RLS + API).
3.6 Buchungs-Statusmodell und Verfügbarkeitsprüfung
Nur Bestätigt und Aktiv blockieren das Fahrzeug für den gebuchten Zeitraum. Der Wechsel nach Aktiv setzt den Fahrzeugstatus automatisch auf „vermietet", der Wechsel zu Zurückgegeben zurück auf „verfügbar" (Mapping in src/lib/bookings.ts).
Die Verfügbarkeit wird doppelt abgesichert:
- Anwendungsebene:
checkAvailability()prüft vor dem Speichern gegen bestehende blockierende Buchungen und liefert eine verständliche Fehlermeldung. - Datenbankebene: Ein
btree_gist-Exclusion-Constraint verhindert überlappende Zeiträume (daterange-Überschneidung) für dasselbe Fahrzeug bei blockierendem Status — als Sicherheitsnetz auch gegen Race-Conditions bei gleichzeitigen Anfragen.
3.7 Dynamische Preisberechnung
calculatePrice() berechnet Tagespreis × Tage und wendet multiplikative Faktoren an. Alle Faktoren sind vom Admin unter Einstellungen konfigurierbar (Migration 0014_dynamic_pricing_settings); die Standardwerte:
| Faktor | Auslöser | Multiplikator (Standard) |
|---|---|---|
| Hochsaison Sommer | Startmonat Juni–August | × 1,15 |
| Feiertage Dezember | Startmonat Dezember | × 1,10 |
| Nebensaison Winter | Startmonat Januar–Februar | × 0,90 |
| Wochenend-Zuschlag | Zeitraum enthält Sa/So | × 1,05 |
| Hohe Auslastung | Flotte > 80 % vermietet | × 1,20 |
| Mittlere Auslastung | Flotte > 60 % vermietet | × 1,10 |
| Wochenrabatt | ≥ 7 Tage | × 0,90 |
| Langzeitrabatt | ≥ 28 Tage | × 0,80 |
Das Buchungsformular zeigt eine Live-Vorschau der Preisaufschlüsselung (POST /api/pricing/quote).
3.8 Werkstatt-Übergabe-Workflow
Der Workflow bildet einen echten zweiseitigen Austausch zwischen Mitarbeiter und Werkstatt ab. Der Fahrzeugstatus wird ausschließlich über eine security definer-Triggerfunktion synchronisiert: Die Werkstatt-Rolle benötigt dadurch kein allgemeines Schreibrecht auf die Fahrzeugtabelle (und könnte somit auch keine Stammdaten ändern). Sie besitzt nur Rechte auf Werkstattaufträge und Schadensfotos — bewusst kein allgemeines Leserecht auf Fahrzeuge; die für einen Auftrag nötigen Fahrzeugdaten werden serverseitig gezielt nur für das zugeordnete Fahrzeug nachgeladen. Für die Werkstatt-Rolle ist ein eigenes Dashboard (neue, laufende, kürzlich abgeschlossene Aufträge) die Startseite.
3.9 KI-Integration
Beide KI-Funktionen liegen in src/lib/ai/index.ts und folgen demselben Muster — echter API-Aufruf mit deterministischem Fallback:
| Funktion | Modell | Fallback ohne GEMINI_API_KEY |
|---|---|---|
analyzeDamage(image) — Schadenserkennung aus Foto | Gemini Vision (gemini-2.5-flash), mit Konfidenz-Schwellwerten gegen übersehene leichte Kratzer (#30) | Statische Mock-Analyse |
extractBookingFromEmail(text) — Buchungsdaten aus E-Mail-Text | Gemini Textmodell mit strukturiertem JSON-Output | Regex-/Heuristik-Extraktion |
Der Foto-Upload ist als Batch-Verarbeitung implementiert (use-photo-upload.ts): Jedes Foto hat einen eigenen Upload-/Analyse-Status, läuft unabhängig im Hintergrund, und das Formular bleibt währenddessen voll bedienbar; abgesendet werden nur vollständig verarbeitete Fotos.
3.10 Dokumenten-Pipeline (PDF + E-Mail)
Rechnung, Angebot und Auftragsbestätigung teilen sich eine gemeinsame Render-Pipeline (@react-pdf/renderer) mit gemeinsamen Absenderdaten aus der companies-Tabelle. Unterschiede im Lebenszyklus sind bewusst modelliert: Rechnungs-PDFs werden im Storage gecacht (eine Rechnung ist nach Erstellung faktisch eingefroren), Angebots-/Auftragsbestätigungs-PDFs werden bei jedem Abruf frisch gerendert (Buchungsdaten können sich in diesen Status noch ändern). Der E-Mail-Versand läuft über einen generischen Resend-Wrapper, der ohne API-Schlüssel transparent in eine geloggte Simulation ausweicht.
4. Sicherheitskonzept
4.1 Zwei Verteidigungsebenen
Jeder Zugriff wird zweistufig kontrolliert:
- Row-Level-Security (Datenbank) — letzte, nicht umgehbare Instanz. Alle Tabellen und Storage-Buckets haben Policies auf Basis von
has_permission(). Selbst ein direkter Zugriff mit dem öffentlichen Anon-Key kann nur, was die Rolle des angemeldeten Benutzers erlaubt. - Anwendungsebene (API-Routen) — identische Prüfung vor jeder schreibenden Aktion, damit Fehler sofort und verständlich (HTTP 403 mit Meldung) statt als generischer DB-Fehler auftreten.
4.2 Härtung gegen Rechteausweitung
Im Projektverlauf wurden zwei reale RLS-Lücken gefunden und geschlossen (Issue #36, Migrationen 0014/0018): Eine profiles-Update-Policy ohne WITH CHECK hätte es erlaubt, per Anon-Key die eigene Rolle zu ändern. Heute verhindert ein Datenbank-Trigger jede Änderung der eigenen Rolle/Firmenzuordnung ohne entsprechendes Recht — unabhängig vom Zugriffsweg. Zusätzlich wurden die Warnungen des Supabase Security Advisors abgearbeitet (Migration 0020), u. a. Entfernung altlastiger, zu permissiver Policies.
4.3 Verpflichtende Zwei-Faktor-Authentifizierung
2FA (TOTP nach RFC 6238, kompatibel mit Google Authenticator, 1Password, Authy, …) ist für jedes Konto verpflichtend — es gibt keinen Codepfad, der eine Session ohne bestandenen zweiten Faktor zu geschützten Seiten durchlässt. Die Durchsetzung sitzt in der Middleware und basiert auf dem Authenticator Assurance Level (AAL) des Session-JWT:
- kein Faktor eingerichtet → Zwangs-Enrollment (
/mfa/enroll, QR-Code), - Faktor vorhanden, Session noch nicht verifiziert → Code-Abfrage (
/mfa/challenge), - vollständig authentifiziert (
aal2) → Zugriff.
2FA lässt sich nicht deaktivieren, nur zurücksetzen (z. B. bei Geräteverlust); danach erzwingt die Middleware automatisch die Neueinrichtung. Details, Architekturentscheidungen und zwei in der Implementierung gefundene Fallstricke: 2fa-technisches-konzept.md.
4.4 Weitere Maßnahmen
- Einzelmandanten-Registrierung:
/api/registerlegt Firma + Admin-Konto in einem Schritt an und ist gesperrt, sobald eine Firma existiert. Kein öffentliches Mitarbeiter-Signup; Konten legt der Admin an. - Schlüsseltrennung: Der Service-Role-Key wird ausschließlich serverseitig (Admin-Client) verwendet und nie an den Browser ausgeliefert; clientseitig kommt nur der RLS-geschützte Anon-Key zum Einsatz.
- Gezielte, schmale Ausnahmen: Wo erhöhte Rechte nötig sind (Fahrzeugstatus-Sync im Werkstatt-Workflow), werden eng umrissene
security definer-Funktionen eingesetzt statt pauschal weitreichender Rollenrechte.
5. Test- und Qualitätssicherung (Zusammenfassung)
Für jedes Feature wurden mehrere Prüfebenen kombiniert: statische Prüfung (TypeScript strict, ESLint), Build-Prüfung (fehlerfreier Produktionsbuild als Definition-of-Done), direkte Datenverifikation nach jeder Migration, isolierte Funktionstests der Kernlogik (Preisberechnung, Verfügbarkeit, Auslastung) mit konkreten Beispieldaten sowie ein ausführlicher manueller Testplan je Modul inkl. Rollen-/Rechte- und Sicherheits-Testfällen (u. a. direkter API-Aufruf ohne Recht → 403).
Der vollständige Testplan mit allen Testfällen, Vorbedingungen und Sollergebnissen ist ausgelagert: Testdokumentation.md.
6. Bekannte Einschränkungen
- Kundenportal: bewusst nicht (mehr) umgesetzt — Produktentscheidung, siehe Abschnitt 2.5.
- E-Mail-Versand läuft ohne konfigurierten
RESEND_API_KEYim Simulationsmodus (Inhalt + Anhang werden serverseitig geloggt, nicht versendet) — für den Prototyp-Betrieb ausreichend, für den Produktivbetrieb ist ein Schlüssel zu hinterlegen. - Buchungs-Freitextsuche filtert (anders als bei Fahrzeugen/Kunden) clientseitig auf der aktuellen Seite, da die Suchfelder auf verknüpften Tabellen liegen; die Statusfilterung und Pagination laufen serverseitig.
- Die Typen in
src/lib/types.tswerden von Hand parallel zum SQL-Schema gepflegt; in einem Produktiv-Setup würdesupabase gen types typescriptverwendet.
7. Glossar
| Begriff | Bedeutung |
|---|---|
| AAL (Authenticator Assurance Level) | Vertrauensstufe einer Session: aal1 = nur Passwort, aal2 = Passwort + TOTP-Code bestätigt. Grundlage der 2FA-Durchsetzung in der Middleware |
| Anon-Key | Öffentlicher Supabase-API-Schlüssel des Browsers; jede damit ausgeführte Abfrage unterliegt der Row-Level-Security |
| App Router | Routing-Modell von Next.js 14: Seiten als Server Components, Layouts und Route-Gruppen unter src/app/ |
| Exclusion-Constraint | PostgreSQL-Constraint, das überlappende Buchungszeiträume desselben Fahrzeugs auf Datenbankebene verhindert (btree_gist) |
| Handover | Übergabe- bzw. Rückgabeprotokoll einer Buchung (km, Tankstand, Zustand, Gegenstände, Fotos) |
| Middleware | Next.js-Code, der vor jedem Request läuft — in FleetFlow: Session-Refresh und 2FA-Durchsetzung |
| RLS (Row-Level-Security) | Zugriffsregeln direkt auf Datenbankzeilen-Ebene; letzte, nicht umgehbare Kontrollinstanz |
| Realtime | Supabase-Mechanismus, der Datenbankänderungen live in den Browser überträgt (z. B. Buchungsliste) |
| security definer | PostgreSQL-Funktionsattribut: Funktion läuft mit den Rechten ihres Erstellers — genutzt für eng umrissene, kontrollierte Ausnahmen (Fahrzeugstatus-Sync im Werkstatt-Workflow) |
| Server Component | React-Komponente, die ausschließlich auf dem Server rendert und dort direkt Daten laden kann |
| Service-Role-Key | Supabase-Schlüssel mit Vollzugriff (umgeht RLS); ausschließlich serverseitig für Admin-Aufgaben (Registrierung, Kontoanlage) verwendet |
| TOTP (Time-based One-Time Password) | Zeitbasierter Einmalcode nach RFC 6238, erzeugt von einer Authenticator-App; zweiter Faktor der Anmeldung |