# Lageplan - Feuerwehr Krokier-App Digitale Lageplan-Applikation für Schweizer Feuerwehren zur Einsatzdokumentation und taktischen Lagedarstellung. Multi-Tenant SaaS mit Karte, Journal, Symbolbibliothek und Druckexport. ## Architektur-Übersicht ``` ┌─────────────────────────────────────────────────────────┐ │ Browser (Client) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │ │ MapView │ │ Journal │ │ Admin │ │Landing │ │ │ │(MapLibre)│ │ View │ │ Panel │ │ Page │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │ │ └──────────────┴─────────────┴─────────────┘ │ │ │ fetch() │ ├─────────────────────────┼───────────────────────────────┤ │ Next.js 14 Server │ │ ┌──────────────────────┼──────────────────────────┐ │ │ │ API Routes (/api/*) │ │ │ │ ┌────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ │ │ Auth │ │ Projects │ │ Admin (Users, │ │ │ │ │ │ (JWT) │ │ Features │ │ Tenants, Icons) │ │ │ │ │ └───┬────┘ │ Journal │ └────────┬─────────┘ │ │ │ │ │ └────┬─────┘ │ │ │ │ │ └────────────┼─────────────────┘ │ │ │ │ │ │ │ │ │ ┌────────────────┼────────────────────────┐ │ │ │ │ │ Prisma ORM + Tenant Guard │ │ │ │ │ └────────────────┼────────────────────────┘ │ │ │ └───────────────────┼─────────────────────────────┘ │ ├───────────────────────┼─────────────────────────────────┤ │ ┌────────────┐ ┌────┴───────┐ ┌──────────────────┐ │ │ │ PostgreSQL │ │ MinIO │ │ SMTP (optional) │ │ │ │ (Prisma) │ │ (S3 Icons) │ │ (Nodemailer) │ │ │ └────────────┘ └────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` ## Tech Stack | Schicht | Technologie | Zweck | |---------|------------|-------| | **Frontend** | Next.js 14, React 18, TypeScript | SSR + SPA | | **UI** | TailwindCSS, shadcn/ui, Lucide Icons | Styling + Komponenten | | **Karte** | MapLibre GL JS | OSM + Satellit Tiles | | **Drag & Drop** | react-dnd + TouchBackend | Symbol-Platzierung (Desktop + Mobile) | | **Datenbank** | PostgreSQL 16 + Prisma ORM | Relationale Daten | | **Object Storage** | MinIO (S3-kompatibel) | Icon-Upload | | **Auth** | JWT (jose) + httpOnly Cookies | Session-Management | | **Validierung** | Zod | Input-Validierung auf API-Ebene | | **E-Mail** | Nodemailer | Passwort-Reset, Kontaktformular | | **PDF** | jsPDF + html2canvas | Kartenexport | | **Container** | Docker Compose | Deployment | ## Features ### Karte - **OSM + Satellitenansicht** — Layer-Toggle oben rechts - **Zeichenwerkzeuge** — Punkt, Linie, Polygon, Rechteck, Kreis, Pfeil, Freihand, Text, Gefahrenzone - **Symbolbibliothek** — 22+ FKS/BABS-konforme Feuerwehr-Symbole in 9 Kategorien - **Drag & Drop + Tap-to-Place** — Symbole auf Karte platzieren (Desktop + Touch) - **Symbol-Bearbeitung** — Rechtsklick: Skalierung (0.3x-4x), Rotation (0-360°) - **Messwerkzeug** — Distanzmessung mit Höhenprofil (Open-Meteo API), Druckverlust-Berechnung für Feuerwehrschläuche - **GPS-Standort** — Automatische Geolocation bei App-Start - **Kartenposition persistent** — Bleibt beim Tab-Wechsel und Projekt-Erstellung erhalten - **Offline-Tiles** — Service Worker cached OSM-Tiles ### Einsatz-Journal - **Zeitprotokoll** — Einträge mit Zeitstempel, Was, Wer, Erledigt-Status - **SOMA-Checkliste** — Vordefinierte Prüfpunkte (Ja/Ok Spalten), aus Templates initialisiert - **Pendenzen** — Offene Aufgaben mit Was/Wer/Wann - **Druckansicht** — Journal als formatiertes Dokument drucken ### Einsatz-Verwaltung - **Erstellen/Laden/Löschen** — Über Menü → "Einsätze verwalten" - **Auto-Save** — Alle 30 Sekunden automatisch in DB + localStorage - **Export** — PNG, PDF (mit Metadaten-Header), GeoJSON - **Sperren** — Projekte können gesperrt werden (nur SERVER_ADMIN kann entsperren) ### Admin-Bereich (`/admin`) - **Benutzer** — CRUD, Rollen zuweisen, Passwort zurücksetzen - **Tenants** — Organisationen verwalten, Mitglieder, Abo-Status - **Symbole** — Upload (PNG/SVG/JPEG/WebP, max 5MB), Kategorien - **Schlauchtypen** — Konfigurierbare Druckverlust-Parameter - **System** — SMTP-Einstellungen, Kontakt-E-Mail, Test-E-Mail ### Sicherheit & Multi-Tenancy - **4 Rollen** — SERVER_ADMIN, TENANT_ADMIN, OPERATOR, VIEWER - **Tenant-Isolation** — Jeder Tenant sieht nur eigene Projekte/Benutzer - **JWT httpOnly Cookies** — Kein Token im localStorage - **Zod-Validierung** — Alle API-Inputs validiert - **IDOR-Schutz** — Sub-Ressourcen (Journal, CheckItems) werden gegen Projekt-Zugehörigkeit geprüft - **Passwort-Hashing** — bcrypt mit 12 Rounds - **Reset-Token** — Kryptographisch sicher (32 Bytes), 1h Ablauf, nie in API-Response exponiert ## Projekt-Struktur ``` lageplan/ ├── docker-compose.yml # PostgreSQL, MinIO, Web-App ├── Dockerfile # Multi-Stage Build (deps → builder → runner) ├── docker-entrypoint.sh # DB-Migration + Seed beim Container-Start ├── prisma/ │ ├── schema.prisma # 15 Models, 4 Enums │ └── seed.js # Demo-Daten (Tenants, Users, Symbole, Projekt) ├── public/ │ └── sw.js # Service Worker für Offline-Tile-Caching ├── src/ │ ├── app/ │ │ ├── page.tsx # Landing Page (/) │ │ ├── login/page.tsx # Login │ │ ├── register/page.tsx # Registrierung (erstellt Tenant + User) │ │ ├── reset-password/ # Passwort-Reset │ │ ├── admin/page.tsx # Admin-Panel │ │ ├── app/page.tsx # Haupt-App (Karte + Journal) │ │ └── api/ │ │ ├── auth/ # login, logout, me, register, forgot/reset-password │ │ ├── projects/ # CRUD + features, journal, export │ │ ├── admin/ # users, tenants, icons, categories, settings │ │ ├── icons/ # Public icon listing + image serving │ │ ├── hose-types/ # Schlauchtyp-Konfiguration │ │ ├── contact/ # Kontaktformular │ │ └── tenants/ # Public tenant info by slug │ ├── components/ │ │ ├── map/ │ │ │ └── map-view.tsx # MapLibre GL Karte (~1650 Zeilen) │ │ ├── journal/ │ │ │ └── journal-view.tsx # Einsatz-Journal │ │ ├── layout/ │ │ │ ├── topbar.tsx # Header mit Menü, Speichern, Einsatz-Verwaltung │ │ │ ├── left-toolbar.tsx # Zeichenwerkzeuge │ │ │ └── right-sidebar.tsx # Symbole + Karte/Journal Tabs │ │ ├── dialogs/ # ProjectDialog, TextDialog, LineLabelDialog, HoseSettings │ │ ├── providers/ │ │ │ └── auth-provider.tsx # React Context für Auth-State │ │ └── ui/ # shadcn/ui Basis-Komponenten │ └── lib/ │ ├── auth.ts # JWT erstellen/verifizieren, Login, Rollen-Checks │ ├── tenant.ts # Tenant-Filter, Projekt-Zugriffsprüfung │ ├── db.ts # Prisma Client Singleton │ ├── minio.ts # MinIO Upload/Download/Delete │ ├── email.ts # SMTP aus DB laden, E-Mail senden │ ├── validations.ts # Zod Schemas (Login, Project, Feature, Icon) │ ├── fw-symbols.ts # 22 eingebaute SVG Feuerwehr-Symbole │ └── utils.ts # Hilfsfunktionen (cn, formatDateTime) ``` ## Datenbank-Schema (Prisma) ### Kern-Models - **Tenant** — Organisation (Feuerwehr), mit Abo-Plan, Limits, Logo - **User** — E-Mail/Passwort, Rolle, Reset-Token - **TenantMembership** — User ↔ Tenant Zuordnung (M:N) - **Project** — Einsatz mit Titel, Ort, mapCenter/mapZoom, isLocked - **Feature** — GeoJSON-Zeichnung (Geometrie + Properties als JSON) ### Journal-Models - **JournalEntry** — Zeitprotokoll-Eintrag (time, what, who, done) - **JournalCheckItem** — SOMA-Checkliste (label, confirmed, ok) - **JournalPendenz** — Offene Aufgabe (what, who, whenHow, done) - **JournalCheckTemplate** — Vorlagen für Checklisten-Punkte ### Konfigurations-Models - **IconCategory** — Symbolkategorie (Feuer, Wasser, Gefahrstoffe, ...) - **IconAsset** — Hochgeladenes Symbol (fileKey → MinIO) - **HoseType** — Schlauchtyp mit Druckverlust-Parametern - **SystemSetting** — Key-Value Store (SMTP, Kontakt-E-Mail) ### Rollen & Berechtigungen | Aktion | SERVER_ADMIN | TENANT_ADMIN | OPERATOR | VIEWER | |--------|:---:|:---:|:---:|:---:| | Alle Projekte sehen | ✓ | - | - | - | | Tenant-Projekte sehen | ✓ | ✓ | ✓ | ✓ | | Zeichnen/Bearbeiten | ✓ | ✓ | ✓ | - | | Projekt erstellen | ✓ | ✓ | ✓ | - | | Projekt löschen | ✓ | ✓ | Eigene | - | | Benutzer verwalten | Alle | Eigener Tenant | - | - | | Tenants verwalten | ✓ | - | - | - | | System-Einstellungen | ✓ | - | - | - | ## Schnellstart ### Voraussetzungen - Docker & Docker Compose ### Installation ```bash cp .env.example .env docker compose up -d ``` App: **http://localhost:3000** ### Standard-Logins (nach Seed) | Benutzer | E-Mail | Passwort | Rolle | |----------|--------|----------|-------| | Admin | admin@lageplan.local | admin123 | SERVER_ADMIN | | Editor | editor@demo.local | editor123 | OPERATOR | | Viewer | viewer@demo.local | viewer123 | VIEWER | ## Umgebungsvariablen | Variable | Beschreibung | Default | |----------|-------------|---------| | `DATABASE_URL` | PostgreSQL Connection String | siehe .env.example | | `NEXTAUTH_SECRET` | JWT Secret (**min. 32 Zeichen!**) | - | | `MINIO_ENDPOINT` | MinIO Host | localhost | | `MINIO_PORT` | MinIO API Port | 9000 | | `MINIO_ACCESS_KEY` | MinIO Zugangsdaten | minioadmin | | `MINIO_SECRET_KEY` | MinIO Passwort | minioadmin123 | | `MINIO_BUCKET` | Bucket Name | lageplan-icons | | `MINIO_PUBLIC_URL` | Öffentliche MinIO URL | http://localhost:9002 | ## Docker Services | Service | Port | Beschreibung | |---------|------|-------------| | `web` | 3000 | Next.js App | | `db` | 5432 | PostgreSQL 16 | | `minio` | 9002 (API), 9003 (Console) | MinIO Object Storage | ## Produktion ```bash # 1. Sichere Secrets setzen NEXTAUTH_SECRET=$(openssl rand -base64 32) POSTGRES_PASSWORD=$(openssl rand -base64 16) # 2. .env anpassen # 3. Starten docker compose up -d # 4. Optional: Reverse Proxy (nginx/traefik) für HTTPS ``` ## Security-Hinweise - `NEXTAUTH_SECRET` **muss** in Produktion gesetzt werden (min. 32 Zeichen) - SMTP-Passwörter werden in der DB als `isSecret: true` markiert - Reset-Tokens werden **nie** in API-Responses exponiert (nur in Server-Logs wenn kein SMTP) - Alle Sub-Ressourcen-Zugriffe (Journal-Einträge, CheckItems, Pendenzen) prüfen Projekt-Zugehörigkeit - Cookie: `httpOnly`, `secure` (in Produktion), `sameSite: lax` - Datei-Uploads: Nur PNG/SVG/JPEG/WebP, max 5MB, UUID-Dateinamen