Files
FireAdministration/.env.example
T
Florian WildeandClaude Opus 5 91954c402b Phase 3: Einsatz- und Uebungserfassung
Schnellerfassung im Alarmfall, sechs Zeitstempel mit Ausrueckzeit, Anfahrt
und Hilfsfrist, Staerke nach FwDV 3, Personalvorschlag mit
Qualifikationsstand, Einsatzbericht als PDF, Jahresexport fuer die Gemeinde,
Statistik und Uebungen mit Anwesenheitsliste.

Dazu das Einsatzportal fuer Fremdwehren: ein signierter Link ohne Anmeldung,
zeitlich und mengenbegrenzt, der Stichwort, Ort, Zeiten und Lagemeldungen
zeigt und Meldungen mit Foto annimmt. Es sind die ersten oeffentlich
erreichbaren Endpunkte in FLORIAN.

Acht neue Tabellen, alle mit organisationId und Row Level Security. Ein
freigegebener Einsatzbericht ist unveraenderlich; ein Datenbanktrigger setzt
das durch. Koordinaten liegen doppelt — als Dezimalgrad und als
PostGIS-Punkt, zusammengehalten von einem Trigger.

57 neue Tests (32 Unit, 14 gegen eine echte Datenbank, 11 E2E).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:50:58 +02:00

265 lines
13 KiB
Bash

# =============================================================================
# FLORIAN — Feuerwehr-Verwaltungsplattform
# Kopiere diese Datei nach ".env" und passe die Werte an:
# cp .env.example .env
# Alles, was hier steht, wird von docker-compose.yml und von lib/env.ts gelesen.
# lib/env.ts validiert beim Start mit Zod — fehlt eine Pflichtvariable, startet
# die App gar nicht erst, statt später halb zu funktionieren.
# =============================================================================
# -----------------------------------------------------------------------------
# Allgemein
# -----------------------------------------------------------------------------
# development | production | test — steuert Logging, Fehlerseiten und Caching.
NODE_ENV=development
# Öffentliche Basis-URL der Anwendung, ohne abschließenden Schrägstrich.
# Muss exakt der Adresse entsprechen, unter der Nutzer die App aufrufen —
# Auth.js baut daraus die OIDC-Redirect-URI.
APP_URL=http://localhost:3100
# Zeitzone für alle serverseitigen Fristen- und Terminberechnungen.
# Nicht ändern, außer die Wehr liegt außerhalb Deutschlands.
TZ=Europe/Berlin
# Host-Ports fuer die Entwicklung. Bewusst nicht die Standardports,
# damit FLORIAN neben anderen lokalen Stacks laufen kann.
APP_PORT=3100
GOTENBERG_PORT=3101
MAILPIT_UI_PORT=8125
MAILPIT_SMTP_PORT=1125
ADMINER_PORT=8081
# -----------------------------------------------------------------------------
# Traefik / Domains (nur für den Produktionsbetrieb relevant)
# In der Entwicklung greift docker-compose.override.yml und veröffentlicht die
# Dienste direkt auf localhost — Traefik wird dort nicht gebraucht.
# -----------------------------------------------------------------------------
# Hostname der Anwendung hinter Traefik, z. B. florian.beispielwehr.de
APP_DOMAIN=florian.localhost
# Hostname der MinIO-S3-API (Datei-Uploads, presignte URLs).
MINIO_DOMAIN=s3.florian.localhost
# Hostname der MinIO-Weboberfläche (Administration der Buckets).
MINIO_CONSOLE_DOMAIN=minio.florian.localhost
# Hostname des Traefik-Dashboards. Leer lassen, um es nicht zu veröffentlichen.
TRAEFIK_DASHBOARD_DOMAIN=traefik.florian.localhost
# E-Mail-Adresse für die ACME-Zertifikate (Let us Encrypt).
ACME_EMAIL=kommandant@beispielwehr.de
# Traefik-Entrypoint für HTTPS. "websecure" ist der Standard aus dieser Datei.
TRAEFIK_ENTRYPOINT=websecure
# -----------------------------------------------------------------------------
# PostgreSQL 17 + PostGIS
# -----------------------------------------------------------------------------
POSTGRES_USER=florian
# Vor dem ersten Start ersetzen: openssl rand -base64 24
POSTGRES_PASSWORD=bitte_aendern_postgres
POSTGRES_DB=florian
# Port, unter dem Postgres auf dem Host erreichbar ist (nur Entwicklung).
POSTGRES_PORT=5442
# Verbindungszeichenfolge der Anwendung. Läuft über den Datenbankbenutzer
# "florian_app", für den Row Level Security AKTIV ist — dieser Zugang sieht nur
# Daten der Organisation, die per SET LOCAL app.current_organisation gesetzt
# wurde (siehe src/lib/db.ts).
DATABASE_URL=postgresql://florian_app:bitte_aendern_app@postgres:5432/florian?schema=public
# Passwort des Anwendungsbenutzers. Muss zu DATABASE_URL passen und wird beim
# ersten Start von docker/postgres/10-init-app-role.sh gesetzt.
POSTGRES_APP_PASSWORD=bitte_aendern_app
# Verbindung für Migrationen und Seed. Nutzt den Eigentümer-Zugang, der RLS
# umgehen darf. Niemals im Anwendungscode verwenden.
DIRECT_DATABASE_URL=postgresql://florian:bitte_aendern_postgres@postgres:5432/florian?schema=public
# -----------------------------------------------------------------------------
# Redis (Warteschlangen für BullMQ, Rate-Limiting)
# -----------------------------------------------------------------------------
# Vor dem ersten Start ersetzen: openssl rand -base64 24
REDIS_PASSWORD=bitte_aendern_redis
REDIS_URL=redis://:bitte_aendern_redis@redis:6379
# Port auf dem Host (nur Entwicklung).
REDIS_PORT=6389
# Anzahl paralleler Jobs je Worker-Prozess.
WORKER_CONCURRENCY=5
# -----------------------------------------------------------------------------
# MinIO (S3-kompatibler Objektspeicher — ALLE Dateien liegen hier, nie im FS)
# -----------------------------------------------------------------------------
MINIO_ROOT_USER=florian
# Mindestens 8 Zeichen, vor dem ersten Start ersetzen.
MINIO_ROOT_PASSWORD=bitte_aendern_minio
# Endpunkt, den App und Worker intern ansprechen — im Compose-Netz der
# Dienstname, außerhalb der externe Hostname.
S3_ENDPOINT=minio
S3_PORT=9000
# true, sobald MinIO hinter Traefik mit TLS läuft.
S3_USE_SSL=false
S3_ACCESS_KEY=florian
S3_SECRET_KEY=bitte_aendern_minio
S3_REGION=eu-central-1
# Bucket für alle Nutzdateien (Prüfprotokolle, Fotos, Rechnungen, Exporte).
S3_BUCKET=florian
# Öffentlich erreichbare Basis-URL für presignte Download-Links. In der
# Entwicklung http://localhost:9010, in Produktion https://$MINIO_DOMAIN
S3_PUBLIC_URL=http://localhost:9010
# Gültigkeitsdauer presignter Links in Sekunden (Standard: 15 Minuten).
S3_URL_TTL=900
# Ports auf dem Host (nur Entwicklung): API und Weboberfläche.
MINIO_API_PORT=9010
MINIO_CONSOLE_PORT=9011
# -----------------------------------------------------------------------------
# Gotenberg (PDF-Erzeugung für Prüfprotokolle, Berichte, Rechnungen)
# -----------------------------------------------------------------------------
GOTENBERG_URL=http://gotenberg:3000
# Zeitlimit je PDF-Auftrag in Sekunden.
GOTENBERG_TIMEOUT=30
# -----------------------------------------------------------------------------
# Auth.js — Sitzungsverwaltung
# -----------------------------------------------------------------------------
# Signiert die Sitzungs-Cookies. Zwingend erforderlich, pro Installation neu:
# openssl rand -base64 32
AUTH_SECRET=bitte_aendern_mindestens_32_zeichen_lang
# Muss hinter einem Reverse Proxy (Traefik) auf true stehen, damit Auth.js die
# X-Forwarded-Header auswertet und korrekte Redirect-URIs erzeugt.
AUTH_TRUST_HOST=true
# Lebensdauer einer Sitzung in Sekunden (Standard: 8 Stunden).
AUTH_SESSION_MAX_AGE=28800
# -----------------------------------------------------------------------------
# OIDC gegen authentik
# Die authentik-Instanz wird SEPARAT betrieben und ist nicht Teil dieses Stacks.
# In authentik anzulegen: Provider "OAuth2/OpenID" (Client-Typ: confidential),
# Redirect-URI exakt $APP_URL/api/auth/callback/authentik
# Scopes: openid profile email — plus einen Gruppen-Scope, wenn Rollen aus
# authentik-Gruppen vorbelegt werden sollen (siehe AUTH_OIDC_GROUP_CLAIM).
# -----------------------------------------------------------------------------
# false blendet den OIDC-Knopf aus; dann bleibt nur der lokale Login. Sinnvoll
# für den Erststart, bevor authentik konfiguriert ist.
AUTH_OIDC_ENABLED=false
# Issuer-URL des Providers, OHNE /.well-known/openid-configuration.
# Bei authentik: https://<authentik-host>/application/o/<application-slug>/
AUTH_OIDC_ISSUER=https://auth.beispiel.de/application/o/florian/
AUTH_OIDC_CLIENT_ID=
AUTH_OIDC_CLIENT_SECRET=
# Beschriftung des Anmeldeknopfes in der Oberfläche.
AUTH_OIDC_NAME=Anmeldung über authentik
# Claim, aus dem die Gruppenzugehörigkeit gelesen wird. In authentik über ein
# Scope-Mapping bereitzustellen. Leer lassen, um das Mapping abzuschalten —
# dann werden Rollen ausschließlich in FLORIAN gepflegt.
AUTH_OIDC_GROUP_CLAIM=groups
# Zuordnung authentik-Gruppe -> FLORIAN-Rolle, kommagetrennt als "gruppe:ROLLE".
# Wirkt nur als VORBELEGUNG beim ersten Login; danach ist die in FLORIAN
# gepflegte Rolle führend und wird nicht mehr überschrieben.
AUTH_OIDC_GROUP_MAPPING=feuerwehr-kommandant:KOMMANDANT,feuerwehr-geraetewart:GERAETEWART,feuerwehr-atemschutz:ATEMSCHUTZGERAETEWART,feuerwehr-kassier:KASSIER,feuerwehr-schriftfuehrer:SCHRIFTFUEHRER,feuerwehr-ausbilder:AUSBILDER,feuerwehr-mitglied:MITGLIED
# true legt einen in authentik bekannten Nutzer beim ersten Login automatisch
# als Person an. false verlangt, dass die Person vorher in FLORIAN existiert
# und über die E-Mail-Adresse verknüpft wird.
AUTH_OIDC_AUTO_PROVISION=true
# Organisation (Slug), der automatisch angelegte Personen zugeordnet werden.
AUTH_OIDC_STANDARD_ORGANISATION=ff-musterhausen
# -----------------------------------------------------------------------------
# Lokaler E-Mail/Passwort-Login (Rückfalloption für Erststart und Notbetrieb)
# -----------------------------------------------------------------------------
# Nach der Einrichtung von authentik auf false setzen, damit nur noch OIDC geht.
AUTH_LOCAL_ENABLED=true
# Zugangsdaten des ersten Administrators, angelegt vom Seed-Skript.
# Nach dem ersten Login das Passwort in der Oberfläche ändern.
SEED_ADMIN_EMAIL=admin@beispielwehr.de
SEED_ADMIN_PASSWORT=Feuerwehr2026
# bcrypt-Kostenfaktor. 12 ist ein guter Kompromiss aus Sicherheit und Tempo.
AUTH_BCRYPT_ROUNDS=12
# -----------------------------------------------------------------------------
# Terminal-/Kiosk-Anmeldung im Gerätehaus (PIN und RFID)
# -----------------------------------------------------------------------------
# Schaltet den Kiosk-Modus samt Terminal-Login frei.
KIOSK_ENABLED=true
# Gemeinsames Geheimnis, mit dem sich ein Terminal-Gerät gegenüber dem Server
# ausweist, bevor überhaupt eine PIN oder ein Token akzeptiert wird:
# openssl rand -hex 32
KIOSK_DEVICE_SECRET=bitte_aendern_kiosk_geheimnis
# Länge der Personen-PIN. 4 ist bequem, 6 ist sicherer.
KIOSK_PIN_LAENGE=6
# Nach wie vielen Fehlversuchen eine PIN gesperrt wird.
KIOSK_MAX_FEHLVERSUCHE=5
# Sperrdauer nach zu vielen Fehlversuchen, in Sekunden.
KIOSK_SPERRDAUER=300
# Automatische Abmeldung am Terminal nach Untätigkeit, in Sekunden.
KIOSK_IDLE_TIMEOUT=120
# -----------------------------------------------------------------------------
# E-Mail-Versand (optional, hinter lib/integrations/mail.ts abstrahiert)
# Ohne MAIL_ENABLED=true wird nur ins Log geschrieben — praktisch für Tests.
# -----------------------------------------------------------------------------
MAIL_ENABLED=false
MAIL_HOST=smtp.beispiel.de
MAIL_PORT=587
MAIL_SECURE=false
MAIL_USER=
MAIL_PASSWORD=
MAIL_FROM=FLORIAN <florian@beispielwehr.de>
# -----------------------------------------------------------------------------
# Telegram-Benachrichtigungen (optional, abschaltbar)
# -----------------------------------------------------------------------------
TELEGRAM_ENABLED=false
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
# -----------------------------------------------------------------------------
# LLM-Anbindung (optional; nur selbstgehostete Endpunkte, kein Cloud-Zwang)
# -----------------------------------------------------------------------------
LLM_ENABLED=false
# OpenAI-kompatibler Endpunkt, z. B. eine lokale Ollama- oder vLLM-Instanz.
LLM_BASE_URL=http://ollama:11434/v1
LLM_API_KEY=
LLM_MODELL=qwen2.5:14b
# -----------------------------------------------------------------------------
# Einsätze und Übungen (Phase 3)
# -----------------------------------------------------------------------------
# Adressen zu Koordinaten auflösen. Vorgesehen ist ein SELBST betriebener
# Nominatim — Einsatzadressen gehören nicht an einen öffentlichen Dienst, und
# dessen Nutzungsbedingungen erlauben es ohnehin nicht.
GEOCODING_ENABLED=false
# NOMINATIM_URL=http://nominatim:8080
GEOCODING_TIMEOUT=5
# Auf welche Länder die Suche eingegrenzt wird (ISO-Codes, kommagetrennt).
GEOCODING_LAENDER=de,at,ch
# Einsatzportal für Fremdwehren: ein signierter Link ohne Anmeldung, über den
# eine Nachbarwehr den Stand sieht und Lagemeldungen absetzen kann.
PORTAL_ENABLED=true
# Wie lange ein neu erzeugter Link gilt.
PORTAL_GUELTIGKEIT_STUNDEN=24
# Höchstzahl Zugriffe je Link und Stunde.
PORTAL_MAX_ZUGRIFFE=120
# -----------------------------------------------------------------------------
# Wartung und Prüfung (Phase 2)
# -----------------------------------------------------------------------------
# Wann der nächtliche Lauf Prüftermine plant. Cron-Syntax, Zeitzone
# Europe/Berlin. Der Worker trägt den Zeitplan beim Start je Wehr selbst ein.
WARTUNG_NACHTLAUF=15 3 * * *
# Wann die Erinnerungen verschickt werden. Bewusst nach der Planung: sonst
# erinnert der Lauf an Termine, die es noch gar nicht gibt.
WARTUNG_ERINNERUNG_LAUF=45 3 * * *
# Wie weit im Voraus Termine angelegt werden. Ohne Horizont stünden Termine für
# die nächsten zehn Jahre im Dashboard.
WARTUNG_PLANUNGSHORIZONT_TAGE=180
# -----------------------------------------------------------------------------
# Datenschutz und Aufbewahrung (DSGVO)
# -----------------------------------------------------------------------------
# Aufbewahrungsfrist für Einsatz- und Prüfdokumentation in Jahren.
AUFBEWAHRUNG_JAHRE=10
# Aufbewahrungsfrist für den Audit-Log in Tagen. Der Log ist append-only;
# Älteres wird vom Worker exportiert, bevor es entfernt wird.
AUDIT_AUFBEWAHRUNG_TAGE=3650
# Frist in Tagen, nach der ein ausgetretenes Mitglied anonymisiert wird.
PERSON_ANONYMISIERUNG_TAGE=365
# -----------------------------------------------------------------------------
# Telemetrie bleibt aus. Nicht ändern.
# -----------------------------------------------------------------------------
NEXT_TELEMETRY_DISABLED=1
CHECKPOINT_DISABLE=1