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:
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Läuft EINMALIG beim allerersten Start des Postgres-Containers (leeres
|
||||
# Datenverzeichnis). Richtet ein:
|
||||
# - die benötigten Erweiterungen (PostGIS, pgcrypto, citext, unaccent, pg_trgm)
|
||||
# - den Anwendungsbenutzer "florian_app" OHNE BYPASSRLS
|
||||
# - Vorgabeprivilegien, damit künftige Tabellen automatisch nutzbar sind
|
||||
#
|
||||
# Wichtig zum Verständnis der Mandantentrennung:
|
||||
# Der Eigentümer der Tabellen (POSTGRES_USER) umgeht Row Level Security —
|
||||
# das ist gewollt, damit Migrationen und das Seed-Skript arbeiten können.
|
||||
# Die Anwendung verbindet sich ausschließlich als "florian_app"; für den
|
||||
# greift RLS, und ohne gesetztes app.current_organisation sieht er nichts.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
APP_ROLE="florian_app"
|
||||
APP_PASSWORD="${POSTGRES_APP_PASSWORD:?POSTGRES_APP_PASSWORD muss gesetzt sein}"
|
||||
# Einfache Anführungszeichen im Passwort für SQL verdoppeln.
|
||||
APP_PASSWORD_SQL="${APP_PASSWORD//\'/\'\'}"
|
||||
|
||||
echo "[florian-init] Richte Erweiterungen und Anwendungsrolle ein ..."
|
||||
|
||||
# Erweiterungen. Reines SQL, keine Variablen nötig.
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-'EOSQL'
|
||||
CREATE EXTENSION IF NOT EXISTS postgis; -- Koordinaten, Einsatzorte
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto; -- gen_random_uuid(), digest()
|
||||
CREATE EXTENSION IF NOT EXISTS citext; -- E-Mail ohne Gross/Klein
|
||||
CREATE EXTENSION IF NOT EXISTS unaccent; -- Suche ueber Umlaute hinweg
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- unscharfe Namenssuche
|
||||
EOSQL
|
||||
|
||||
# Anwendungsrolle anlegen. Bewusst OHNE DO-Block: psql ersetzt seine Variablen
|
||||
# nicht innerhalb dollar-quotierter Zeichenketten. Stattdessen erzeugt die
|
||||
# Abfrage den CREATE-Befehl als Text, den \gexec dann ausführt — und zwar nur,
|
||||
# wenn die Rolle noch nicht existiert.
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
SELECT format(
|
||||
'CREATE ROLE %I LOGIN PASSWORD %L NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS',
|
||||
'${APP_ROLE}', '${APP_PASSWORD_SQL}'
|
||||
)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${APP_ROLE}')
|
||||
\gexec
|
||||
|
||||
ALTER ROLE "${APP_ROLE}" PASSWORD '${APP_PASSWORD_SQL}';
|
||||
ALTER ROLE "${APP_ROLE}" SET timezone TO 'Europe/Berlin';
|
||||
EOSQL
|
||||
|
||||
# Rechtevergabe. Hier funktionieren psql-Variablen, weil kein DO-Block im Spiel
|
||||
# ist.
|
||||
psql -v ON_ERROR_STOP=1 \
|
||||
--username "$POSTGRES_USER" \
|
||||
--dbname "$POSTGRES_DB" \
|
||||
-v app_role="$APP_ROLE" \
|
||||
-v datenbank="$POSTGRES_DB" \
|
||||
-v owner="$POSTGRES_USER" <<-'EOSQL'
|
||||
GRANT CONNECT ON DATABASE :"datenbank" TO :"app_role";
|
||||
GRANT USAGE ON SCHEMA public TO :"app_role";
|
||||
|
||||
-- Rechte auf alles, was heute schon existiert (bei Erstinstallation leer).
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO :"app_role";
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :"app_role";
|
||||
|
||||
-- Und auf alles, was der Eigentuemer kuenftig anlegt (Prisma-Migrationen).
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO :"app_role";
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO :"app_role";
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public
|
||||
GRANT EXECUTE ON FUNCTIONS TO :"app_role";
|
||||
|
||||
-- Die Anwendung darf keine Tabellen anlegen oder loeschen.
|
||||
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
|
||||
REVOKE CREATE ON SCHEMA public FROM :"app_role";
|
||||
|
||||
ALTER DATABASE :"datenbank" SET timezone TO 'Europe/Berlin';
|
||||
EOSQL
|
||||
|
||||
echo "[florian-init] Anwendungsrolle '$APP_ROLE' eingerichtet (RLS wirksam)."
|
||||
Reference in New Issue
Block a user