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).");