From 981ca81f64e731ded38c2e51de824456475d13a8 Mon Sep 17 00:00:00 2001 From: karim Date: Sat, 23 May 2026 20:36:57 +0200 Subject: [PATCH] README: Alpha-Status + bekannte offene Punkte dokumentiert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stack-Skelett ist drin (compose, Dockerfile, kong.yml, nginx.conf, sync- Script, generate-keys-Skript), aber End-to-End noch nicht lauffähig. Beim ersten lokalen Bring-up sind aufgefallen: - auth.users-Schema fehlt beim Init (Migration 0001 referenziert FK) - Healthcheck-User: supabase/postgres default ist supabase_admin - auth.uid()/auth.role() müssen vor Migrations existieren - Standard-Init-SQL (roles, _supabase, realtime, jwt, …) aus supabase/supabase docker fehlt Punkte sind in separater Iteration zu adressieren — Self-Host-Init in Production-Qualität ist nicht in einem Rutsch zu bauen. Co-Authored-By: Claude Opus 4.7 --- README.md | 15 ++++++++++- docker-compose.yml | 2 +- scripts/generate-keys.mjs | 53 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100755 scripts/generate-keys.mjs diff --git a/README.md b/README.md index 31467da..076cee9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# rapport-server +# RAPPORT-SERVER + +> ⚠️ **Status: Alpha — noch nicht End-to-End-getestet.** Die Files in diesem Repo sind ein Skelett (Docker-Compose, Init-Skripte, Frontend-Build), aber der Stack startet noch nicht out-of-the-box. Vor produktivem Einsatz ist die Supabase-Init-Reihenfolge auszubauen (siehe «Bekannte offene Punkte» unten). Self-Hosting-Stack für [Rapport](https://git.kgva.ch/karim/RAPPORT) — die Studio-Management-Software für Architekturbüros. @@ -135,6 +137,17 @@ docker compose exec storage tar -czf - /var/lib/storage > storage-$(date +%Y%m%d --- +## Bekannte offene Punkte + +Beim ersten End-to-End-Versuch (lokal mit Docker Compose + alternativen Ports) sind diese Probleme aufgefallen: + +1. **`auth.users`-Schema fehlt beim Postgres-Init**: Die Rapport-Migrationen (`0001_initial.sql`) referenzieren `auth.users(id)` als Foreign Key. In Karims `supabase start`-Setup wird das von der Supabase-CLI vorab initialisiert; hier in `docker-compose` muss das per Init-Script (vor den App-Migrationen) explizit angelegt werden — analog zum offiziellen [supabase/supabase Self-Host-Setup](https://github.com/supabase/supabase/tree/master/docker/volumes/db/init). Stub-Variante in `volumes/db/init/00-init.sh` ist drin, aber nicht ausreichend. +2. **Healthcheck-User**: `supabase/postgres` Image hat als Default-User `supabase_admin` (nicht `postgres`). Compose-Healthchecks und SQL-Skripte müssen das berücksichtigen. +3. **`auth.uid()` und `auth.role()` als Stub-Funktionen** brauchen Pre-Migration, damit RLS-Policies + RPCs (`is_studio_member`, `create_studio_with_admin`, …) im Init durchlaufen. +4. **Standard-Init-SQL aus dem offiziellen Supabase Self-Host kopieren**: `roles.sql`, `_supabase.sql`, `realtime.sql`, `webhooks.sql`, `jwt.sql`, `logs.sql` — diese fehlen aktuell. + +Diese Punkte sind in einer separaten Iteration zu adressieren, nicht in einem Schritt. Empfehlung: Sich Schritt für Schritt am offiziellen Supabase-Self-Host-Compose orientieren. + ## Lizenz GNU AGPL-3.0-or-later — identisch zur Rapport-App. diff --git a/docker-compose.yml b/docker-compose.yml index 81daf1b..995f6bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -175,7 +175,7 @@ services: SUPABASE_URL: ${API_EXTERNAL_URL} SUPABASE_ANON_KEY: ${ANON_KEY} image: rapport-app:${RAPPORT_APP_TAG:-latest} - container_name: rapport-app + container_name: rapport-server-app restart: unless-stopped ports: - "${APP_PORT:-8080}:80" diff --git a/scripts/generate-keys.mjs b/scripts/generate-keys.mjs new file mode 100755 index 0000000..c26a515 --- /dev/null +++ b/scripts/generate-keys.mjs @@ -0,0 +1,53 @@ +#!/usr/bin/env node +// Generiert ANON_KEY und SERVICE_ROLE_KEY (JWTs, HS256 signiert) aus dem +// JWT_SECRET in der .env. Ohne diese Keys können GoTrue, PostgREST, Storage +// und Realtime nicht miteinander reden. +// +// Aufruf: +// node scripts/generate-keys.mjs # liest JWT_SECRET aus .env +// node scripts/generate-keys.mjs # explizit übergeben +// +// Output: zwei JWTs auf stdout, die du in .env als ANON_KEY und +// SERVICE_ROLE_KEY einsetzen kannst. + +import crypto from "node:crypto"; +import fs from "node:fs"; + +function base64url(input) { + return Buffer.from(input).toString("base64") + .replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"); +} + +function signJwt(payload, secret) { + const header = base64url(JSON.stringify({ alg: "HS256", typ: "JWT" })); + const body = base64url(JSON.stringify(payload)); + const sig = base64url(crypto.createHmac("sha256", secret).update(`${header}.${body}`).digest()); + return `${header}.${body}.${sig}`; +} + +let secret = process.argv[2]; +if (!secret) { + try { + const env = fs.readFileSync(".env", "utf8"); + const m = env.match(/^JWT_SECRET=(.+)$/m); + if (m) secret = m[1].trim().replace(/^["']|["']$/g, ""); + } catch {} +} + +if (!secret || secret.length < 32 || secret.includes("CHANGE-ME")) { + console.error("✗ Kein gültiges JWT_SECRET gefunden (mind. 32 Zeichen, nicht der Placeholder)."); + console.error(" Setze JWT_SECRET in .env oder gib es als Argument:"); + console.error(" node scripts/generate-keys.mjs $(openssl rand -hex 32)"); + process.exit(1); +} + +const now = Math.floor(Date.now() / 1000); +const tenYears = now + 10 * 365 * 24 * 3600; + +const anonKey = signJwt({ role: "anon", iss: "supabase", iat: now, exp: tenYears }, secret); +const serviceKey = signJwt({ role: "service_role", iss: "supabase", iat: now, exp: tenYears }, secret); + +console.log("ANON_KEY=" + anonKey); +console.log("SERVICE_ROLE_KEY=" + serviceKey); +console.error(""); +console.error("→ Werte in .env eintragen (überschreibt CHANGE-ME-Placeholder).");