From fb54f22e0cdf6b57d7efc56555ff77ec13ed6f5f Mon Sep 17 00:00:00 2001 From: karim Date: Sun, 24 May 2026 16:09:26 +0200 Subject: [PATCH] Compose-Stack-Stabilisierungen + Gitea-Registry-Image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - App-Image wird jetzt aus Gitea-Container-Registry gepullt (git.kgva.ch/karim/rapport-app:main) — kein lokaler Build mehr noetig. build:-Section bleibt fuer Dev-Workflow. - DB-Port nur noch auf 127.0.0.1 gebunden — kein direkter LAN-Zugriff zur Postgres mehr (Kong-Proxy ist der einzige Weg). - DB-Healthcheck: start_period 30s + 20 retries — Migrations + Schema-Init brauchen beim Erst-Start laenger als 50s. - Realtime: DB_AFTER_CONNECT_QUERY zeigt jetzt auf 'realtime' (ohne Underscore). - App-Image-HEALTHCHECK: wget http://127.0.0.1/ statt localhost (IPv6-Fall). - Init-Setup: zz-rapport-post-init.sh statt 00-init.sh, plus rapport-migrations als separater Mount unter /rapport-migrations. --- .gitignore | 2 +- Dockerfile.app | 2 +- docker-compose.yml | 33 ++++++++++--- scripts/sync-migrations.sh | 10 ++-- volumes/db/init/00-init.sh | 64 ------------------------- volumes/db/init/zz-rapport-post-init.sh | 39 +++++++++++++++ 6 files changed, 72 insertions(+), 78 deletions(-) delete mode 100755 volumes/db/init/00-init.sh create mode 100755 volumes/db/init/zz-rapport-post-init.sh diff --git a/.gitignore b/.gitignore index f52b1c0..583add0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .env .env.local volumes/db/data/ -volumes/db/init/migrations/ +volumes/db/init/rapport-migrations/ volumes/storage/ .DS_Store *.log diff --git a/Dockerfile.app b/Dockerfile.app index dce8313..0be163c 100644 --- a/Dockerfile.app +++ b/Dockerfile.app @@ -30,4 +30,4 @@ FROM nginx:alpine COPY --from=builder /build/app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 -HEALTHCHECK --interval=30s --timeout=3s CMD wget -q --spider http://localhost/ || exit 1 +HEALTHCHECK --interval=30s --timeout=3s CMD wget -q --spider http://127.0.0.1/ || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml index 995f6bd..70131e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,15 +24,24 @@ services: JWT_EXP: 3600 volumes: - postgres-data:/var/lib/postgresql/data - - ./volumes/db/init/migrations:/docker-entrypoint-initdb.d/migrations:ro - - ./volumes/db/init/00-init.sh:/docker-entrypoint-initdb.d/00-init.sh:ro + # Rapport-Migrations gehen in einen separaten Pfad — NICHT in + # /docker-entrypoint-initdb.d, weil das die supabase-internen Migrations + # des Images überschreiben würde. + - ./volumes/db/init/rapport-migrations:/rapport-migrations:ro + # Post-Init läuft alphabetisch nach migrate.sh des Images. Achtung: + # ASCII-Reihenfolge ist 0-9 < A-Z < a-z, daher "99-…" käme VOR "migrate.sh". + # Mit "zz-…" landen wir sicher zuletzt. + - ./volumes/db/init/zz-rapport-post-init.sh:/docker-entrypoint-initdb.d/zz-rapport-post-init.sh:ro ports: - - "${DB_PORT:-5432}:5432" + # Bewusst nur auf 127.0.0.1 — Postgres wird ueber Kong / interne + # Container-Kommunikation erreicht, kein direkter LAN-Zugriff. + - "127.0.0.1:${DB_PORT:-5432}:5432" healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres"] + test: ["CMD", "pg_isready", "-U", "supabase_admin", "-d", "postgres"] interval: 5s timeout: 5s - retries: 10 + retries: 20 + start_period: 30s # ════════════════════════════════════════════════════════════════════════ # GoTrue — Auth (Email-Login, Passwort-Reset, Magic-Links) @@ -96,6 +105,10 @@ services: depends_on: db: condition: service_healthy + ulimits: + nofile: + soft: 10000 + hard: 10000 environment: PORT: 4000 DB_HOST: db @@ -103,13 +116,16 @@ services: DB_USER: supabase_admin DB_PASSWORD: ${POSTGRES_PASSWORD} DB_NAME: postgres - DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' + DB_AFTER_CONNECT_QUERY: 'SET search_path TO realtime' DB_ENC_KEY: supabaserealtime API_JWT_SECRET: ${JWT_SECRET} SECRET_KEY_BASE: ${JWT_SECRET} ERL_AFLAGS: -proto_dist inet_tcp ENABLE_TAILSCALE: "false" DNS_NODES: "''" + RLIMIT_NOFILE: 10000 + APP_NAME: realtime + SEED_SELF_HOST: "true" command: > sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server" @@ -167,6 +183,10 @@ services: # Rapport Frontend — die React-App hinter nginx # ════════════════════════════════════════════════════════════════════════ app: + # Default: aus Gitea-Registry pullen (`docker compose pull` reicht). + # Wer das Image selber bauen will (Dev): RAPPORT_APP_IMAGE in .env auf + # `rapport-app:main` setzen, dann `docker compose --profile build build app`. + image: ${RAPPORT_APP_IMAGE:-git.kgva.ch/karim/rapport-app}:${RAPPORT_APP_TAG:-main} build: context: . dockerfile: Dockerfile.app @@ -174,7 +194,6 @@ services: RAPPORT_APP_TAG: ${RAPPORT_APP_TAG:-main} SUPABASE_URL: ${API_EXTERNAL_URL} SUPABASE_ANON_KEY: ${ANON_KEY} - image: rapport-app:${RAPPORT_APP_TAG:-latest} container_name: rapport-server-app restart: unless-stopped ports: diff --git a/scripts/sync-migrations.sh b/scripts/sync-migrations.sh index 630f0d1..883c0ca 100755 --- a/scripts/sync-migrations.sh +++ b/scripts/sync-migrations.sh @@ -20,11 +20,11 @@ if [ ! -d "$TMPDIR/app/supabase/migrations" ]; then exit 1 fi -rm -rf volumes/db/init/migrations -mkdir -p volumes/db/init/migrations -cp "$TMPDIR/app/supabase/migrations/"*.sql volumes/db/init/migrations/ +rm -rf volumes/db/init/rapport-migrations +mkdir -p volumes/db/init/rapport-migrations +cp "$TMPDIR/app/supabase/migrations/"*.sql volumes/db/init/rapport-migrations/ -COUNT=$(ls volumes/db/init/migrations/*.sql | wc -l | tr -d ' ') -echo "✓ $COUNT Migrations nach volumes/db/init/migrations/ kopiert" +COUNT=$(ls volumes/db/init/rapport-migrations/*.sql | wc -l | tr -d ' ') +echo "✓ $COUNT Migrations nach volumes/db/init/rapport-migrations/ kopiert" echo echo "Nächster Schritt: docker compose up -d (oder neustart wenn schon läuft)" diff --git a/volumes/db/init/00-init.sh b/volumes/db/init/00-init.sh deleted file mode 100755 index fc1e197..0000000 --- a/volumes/db/init/00-init.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# Postgres-Init-Script — läuft beim ersten Start des db-Containers. -# -# 1. Legt die Supabase-Standard-Rollen an (anon, authenticated, service_role, -# supabase_auth_admin, supabase_storage_admin, authenticator). -# Diese referenzieren die in den Rapport-Migrations definierten Policies. -# 2. Wendet alle Rapport-Migrations aus ./migrations/ in alphabetischer -# Reihenfolge an. -# -# Nach diesem Script ist die DB einsatzbereit. - -set -euo pipefail - -echo "→ Supabase-Standard-Rollen anlegen…" -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<-EOSQL - -- Standard-Rollen (idempotent) - do \$\$ begin - if not exists (select 1 from pg_roles where rolname = 'anon') then - create role anon nologin noinherit; - end if; - if not exists (select 1 from pg_roles where rolname = 'authenticated') then - create role authenticated nologin noinherit; - end if; - if not exists (select 1 from pg_roles where rolname = 'service_role') then - create role service_role nologin noinherit bypassrls; - end if; - if not exists (select 1 from pg_roles where rolname = 'authenticator') then - execute format('create role authenticator noinherit login password %L', current_setting('rapport.postgres_password', true)); - end if; - if not exists (select 1 from pg_roles where rolname = 'supabase_auth_admin') then - execute format('create role supabase_auth_admin login password %L', current_setting('rapport.postgres_password', true)); - end if; - if not exists (select 1 from pg_roles where rolname = 'supabase_storage_admin') then - execute format('create role supabase_storage_admin login password %L', current_setting('rapport.postgres_password', true)); - end if; - if not exists (select 1 from pg_roles where rolname = 'supabase_admin') then - execute format('create role supabase_admin superuser login password %L', current_setting('rapport.postgres_password', true)); - end if; - end \$\$; - - grant anon to authenticator; - grant authenticated to authenticator; - grant service_role to authenticator; - - -- auth-Schema (für GoTrue) - create schema if not exists auth authorization supabase_auth_admin; - - -- storage-Schema (für Storage-Service) - create schema if not exists storage authorization supabase_storage_admin; - - -- pgcrypto + andere Extensions - create extension if not exists pgcrypto; - create extension if not exists "uuid-ossp"; -EOSQL - -echo "→ Rapport-Migrations applizieren…" -for f in /docker-entrypoint-initdb.d/migrations/*.sql; do - if [ -f "$f" ]; then - echo " → $(basename "$f")" - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres -f "$f" - fi -done - -echo "✓ DB-Initialisierung abgeschlossen." diff --git a/volumes/db/init/zz-rapport-post-init.sh b/volumes/db/init/zz-rapport-post-init.sh new file mode 100755 index 0000000..edaa667 --- /dev/null +++ b/volumes/db/init/zz-rapport-post-init.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Wird als LETZTES File in /docker-entrypoint-initdb.d/ ausgeführt — nach den +# supabase-internen Init-Scripts (auth-schema, storage-schema, postgrest-roles, +# realtime-schema, supabase_admin-Setup, etc.). Erst hier kann auf auth.users +# als FK-Target referenziert werden. +# +# Applies die Rapport-Schema-Migrations aus /rapport-migrations/ als +# supabase_admin (Default-Superuser des supabase/postgres-Image). + +set -euo pipefail + +# Die supabase-internen Rollen werden vom Image mit Default-Passwörtern angelegt. +# GoTrue, PostgREST, Realtime und Storage verbinden sich aber mit POSTGRES_PASSWORD — +# daher müssen wir die Passwörter angleichen, sonst SASL/MD5-Auth-Fehler. +echo "→ Setze Passwörter für Supabase-Service-Rollen auf POSTGRES_PASSWORD…" +psql -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U supabase_admin -d "${POSTGRES_DB:-postgres}" <