8ef2cbe68e330d41870a3751d36e44691d736a86
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 |
| Nodemailer | Passwort-Reset, Kontaktformular | |
| 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
cp .env.example .env
docker compose up -d
Standard-Logins (nach Seed)
| Benutzer | 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
# 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_SECRETmuss in Produktion gesetzt werden (min. 32 Zeichen)- SMTP-Passwörter werden in der DB als
isSecret: truemarkiert - 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
Description
Languages
TypeScript
94.9%
JavaScript
4.1%
CSS
0.6%
Dockerfile
0.2%
Shell
0.2%