# Supabase Credential Migration — Deployment Notes ## Hintergrund Das Tippspiel-Repo hatte versehentlich Secrets im Git-History (alter `.gitea/workflows/build.yml`). Im Zuge der Bereinigung wurden: - **DB-Passwort** rotiert - **Service Role Key** vom Legacy-JWT-Format auf das neue Supabase-Schlüsselformat migriert Die alten Werte sind ungültig — Deployments mit alter Konfiguration funktionieren nicht mehr. ## Supabase Key-System: was sich geändert hat Supabase hat das API-Key-System umgestellt: | Alt (Legacy, JWT) | Neu | |---|---| | `anon` (Browser/Public) | `sb_publishable_...` | | `service_role` (Backend) | `sb_secret_...` | **Wichtige Unterschiede:** - Legacy-Keys sind JWTs (lange Strings beginnend mit `eyJ...`), neue Keys sind Tokens beginnend mit `sb_secret_...` bzw. `sb_publishable_...` - Legacy-Keys können nur als Gruppe rotiert werden (per JWT-Secret-Reset, invalidiert alle User-Sessions) - Neue Keys sind **einzeln** rotierbar - Das Supabase-SDK akzeptiert beide Formate — **kein Code-Change nötig**, nur die Env-Variable austauschen **Wir nutzen jetzt den neuen `sb_secret_...`-Key** unter dem Variablennamen `SUPABASE_SERVICE_ROLE_KEY`. ## Betroffene Variablen | Variable | Wo zu finden | |---|---| | `DATABASE_URL` | Supabase Dashboard → Project Settings → Database → Connection string (**Transaction Pooler**, Port 6543) | | `SUPABASE_URL` | Supabase Dashboard → Project Settings → API (unverändert) | | `SUPABASE_SERVICE_ROLE_KEY` | Supabase Dashboard → Project Settings → API Keys → **Secret keys** Tab → `sb_secret_...` | > **Wichtig:** Bei `DATABASE_URL` den **Transaction Pooler** (Port 6543, Host `aws-0-eu-west-1.pooler.supabase.com`, User `postgres.`) verwenden — **nicht** Direct Connection (Port 5432, Host `db..supabase.co`). Direct Connection läuft nur über IPv6 und scheitert in vielen Umgebungen mit `ENOTFOUND`. Korrektes URL-Format (Pooler): ```text postgresql://postgres.:@aws-0-eu-west-1.pooler.supabase.com:6543/postgres ``` ## Deployment-Stellen Die Variablen müssen an **drei Stellen** synchron gehalten werden: ### 1. Lokales Dev-Environment Datei: `backend/.env` (gitignored, nicht im Repo). **Wichtig bei frischem Clone / Maschinenwechsel:** `backend/.env` ist **bewusst** nicht in Git — Secrets gehören niemals ins Repo. Stattdessen liegt `backend/.env.example` als Template mit Platzhaltern bei. Auf jeder neuen Maschine einmalig: ```bash cd backend cp .env.example .env # dann .env öffnen und Platzhalter durch echte Werte ersetzen ``` **Mindestens benötigte Werte für lauffähiges Dev-Setup:** ```bash DATABASE_URL=postgresql://postgres.:@aws-0-eu-west-1.pooler.supabase.com:6543/postgres SUPABASE_URL=https://.supabase.co SUPABASE_SERVICE_ROLE_KEY=sb_secret_... FOOTBALL_API_KEY=... ``` `ANTHROPIC_API_KEY` und `ELEVENLABS_API_KEY` aus `.env.example` werden aktuell **nicht** benötigt (Features wurden entfernt). **Empfehlung:** Werte in einem Passwort-Manager (1Password, Bitwarden o.ä.) hinterlegen, damit sie beim nächsten Umzug sofort zur Hand sind. **Niemals** per E-Mail/Slack/Chat verschicken. ### 2. Portainer Stack `wm2026-tippspiel` (Stack 115) Portainer UI → Stack → **Environment variables** → Werte ersetzen → **Update the stack** > **Häufiger Fehler:** Beim Pasten **nicht** den Variablennamen mitkopieren. Ins Value-Feld kommt nur der reine Wert, **nicht** `DATABASE_URL=postgresql://...`. Sonst entsteht eine doppelte Präfix-Verschachtelung und der Container kann keine DB-Verbindung aufbauen. ### 3. CI/CD Workflow (`.gitea/workflows/build.yml`) Der Workflow holt die Env-Vars **dynamisch aus der Portainer Stack-Konfig** (Schritt 2). Im Repo selbst stehen **keine** Secrets, sondern nur Referenzen wie `${{ secrets.PORTAINER_TOKEN }}` und `${{ secrets.DEPLOY_TOKEN }}`. Diese beiden Gitea-Secrets bleiben unverändert. ## Migration-Checklist für neue Umgebung - [ ] Aktuelles DB-Passwort aus Supabase abrufen (oder neu setzen unter Project Settings → Database) - [ ] Aktuellen `sb_secret_...`-Key aus Supabase Dashboard kopieren (API Keys → Secret keys) - [ ] Connection String aus Pooler-Tab kopieren, `[YOUR-PASSWORD]` ersetzen - [ ] In Ziel-Stack die drei Variablen setzen: `DATABASE_URL`, `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY` - [ ] Stack redeployen - [ ] Smoke-Test: `curl http://:/api/matches` → soll JSON mit `matches`-Array liefern (kein 500) - [ ] Falls 500: Container-Logs prüfen auf `ENOTFOUND` (→ Direct Connection statt Pooler) oder `28P01 invalid_password` (→ Passwort falsch oder Pooler-Cache braucht ~30s) ## Code-relevante Stellen - DB-Pool wird konfiguriert in `backend/src/db/client.ts` — liest `DATABASE_URL` - SSL ist nur in `NODE_ENV=production` aktiv, mit `rejectUnauthorized: false` - Pool-Settings: max 10 Verbindungen, 5s Connection-Timeout, 30s Idle-Timeout ## Referenzen - Supabase API Keys Doku: - Supabase Connection Pooler: