Phase 0: Fundament der Feuerwehr-Verwaltungsplattform

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>
This commit is contained in:
Florian Wilde
2026-09-03 00:13:53 +02:00
co-authored by Claude Opus 5
commit 845942f36c
84 changed files with 21433 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
import { defineConfig, devices } from "@playwright/test";
/**
* End-to-End-Tests.
*
* Voraussetzung: der Stack läuft (docker compose up -d) und die Datenbank ist
* migriert und geseedet. Der Entwicklungsserver wird bei Bedarf selbst
* gestartet — ein bereits laufender wird wiederverwendet.
*/
const BASIS_URL = process.env.E2E_BASIS_URL ?? "http://localhost:3100";
export default defineConfig({
testDir: "./tests/e2e",
fullyParallel: true,
forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI ? [["list"], ["html", { open: "never" }]] : [["list"]],
timeout: 30_000,
expect: { timeout: 10_000 },
use: {
baseURL: BASIS_URL,
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
// Die Oberfläche ist durchgängig deutsch — auch der Browser soll es sein.
locale: "de-DE",
timezoneId: "Europe/Berlin",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
// Am Terminal im Gerätehaus steht ein Touchgerät — deshalb mitgetestet.
// Bewusst mit Chromium statt WebKit: das Terminal läuft auf einem
// Android-Tablet im Kiosk-Browser, nicht auf einem iPad.
{
name: "tablet",
use: { ...devices["Galaxy Tab S4 landscape"], browserName: "chromium" },
},
],
webServer: {
command: "npm run dev",
url: BASIS_URL,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});