Files
Florian WildeandClaude Opus 5 f7ccd9a908 Phase 8: Lokale KI
Wiki mit semantischer Suche ueber pgvector, Diktat ueber faster-whisper,
Berichtsassistent und Belegerfassung — alles gegen selbst betriebene
Modelle. Dazu zwei Protokollansichten: was die Maschine getan hat und was
Menschen getan haben.

Nichts, was ein Modell erzeugt, gelangt ungeprueft in einen Nachweis. Jede
Ausgabe ist zuerst ein eigener Datensatz, gekennzeichnet, mit zwei gleich
grossen Knoepfen davor. Die Datenbank haelt gegen: Eingabe, Ausgabe und
Modell sind unveraenderlich, geloescht wird nicht, entschieden wird einmal.

Kein Rueckfall auf einen Anbieter: fehlen die Container, fehlt die Funktion.
Dafuer bleibt alles andere ohne Modell vollstaendig benutzbar — geprueft von
vierzehn E2E-Tests, die ohne laufendes Modell durchlaufen.

Postgres bringt jetzt PostGIS und pgvector mit (eigenes Image), Ollama und
faster-whisper stehen im Compose-Profil "ki".

73 neue Unit-Tests, 14 neue E2E-Tests, Version 0.9.0.

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

82 lines
3.9 KiB
Bash

#!/bin/bash
# =============================================================================
# Läuft EINMALIG beim allerersten Start des Postgres-Containers (leeres
# Datenverzeichnis). Richtet ein:
# - die benötigten Erweiterungen (PostGIS, pgvector, 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
CREATE EXTENSION IF NOT EXISTS vector; -- semantische Suche im Wiki
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)."