Remove Supabase
Some checks failed
CI / Lint, Typen, Tests, Build (push) Failing after 5m40s
CI / Migrationen auf leerer Datenbank (push) Has been cancelled

The database moved to a container of our own; the platform is gone.
This takes out what was left of it — and, where the leftovers were load
bearing, moves rather than deletes.

Moved, not deleted:

  supabase/migrations/  -> db/migrations/      the schema's source of truth
  supabase/build-org.ts -> scripts/build-org.ts
  lib/supabase/types.ts -> lib/types.ts        52 import sites repointed

The bookkeeping needed care. It lived in `supabase_migrations.schema_migrations`,
and simply renaming the schema would have left the runner facing an empty
table: it would have called all 67 migrations pending and replayed them
against a database that is long since current. So the runner now creates
`migrationen.schema_migrations` and, once, copies the old rows across —
guarded so a second run does nothing and a fresh database skips it entirely.
Only then does migration 20260907100000 drop the old schema.

Deleted: the CLI config, the seed, the historical schema/function dumps
(nothing read them), scripts/umzug-von-supabase.sh (the move is done), and
both Supabase packages plus the CLI. Nothing in the application imported
them — the build now succeeds with no environment variables at all, which
is the proof.

Integration tests: six of them signed in through Supabase Auth and asserted
against the anon key and the service role. That model is gone, so the tests
were not portable — they are deleted. session-context and
employee-status-filter already ran on pg and are untouched; om-reporting is
ported to a direct connection because it guards a real risk (the reporting
line rule exists twice, once in SQL and once in TypeScript).

CI: the integration job started a Supabase stack. It now runs a postgres
service, applies deploy/db-init and every migration to an empty database —
that was the valuable part, and it still holds — then checks that a second
run is a no-op, which is what proves the bookkeeping works.

Docs: security-review.md audited a service-role key, a cookie adapter and
auth.users, none of which exist. Restating findings about removed components
would suggest today's system had been reviewed; it has not. It now records
what was removed and says a fresh review is due. data-model.md was already
marked obsolete and described the pre-OM schema; azure-migration.md was a
plan for a route not taken. Both deleted.

Verified: npm ci, typecheck, lint, 445 tests, build — all clean without the
packages. Integration tests skip cleanly with no database. Migration SQL and
the runner are reviewed but NOT executed: no Docker here, and the old
instance no longer resolves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 10:43:22 +02:00
parent 5c310c3a58
commit b87c8ad64c
155 changed files with 386 additions and 4014 deletions

View File

@@ -15,12 +15,10 @@ eine `profiles`-Zeile braucht (siehe [docs/entra-sso.md](docs/entra-sso.md)).
- **Containerisiert:** die Next.js-App (`Dockerfile`) **und die Datenbank**
(Dienst `db`, `postgres:17-alpine`). Beide zusammen in
`docker-compose.yml`; die Daten liegen im benannten Volume `db-daten`.
- **Nicht mehr Supabase.** Die Anwendung sprach ohnehin unmittelbar mit
PostgreSQL — Supabase war nur der Betreiber. Was die Plattform beisteuerte,
bringt jetzt `deploy/db-init/` mit: die Anwendungsrolle ohne BYPASSRLS, die
zwei benutzten Erweiterungen und eine Attrappe des `auth`-Schemas, die nur
die alten Migrationen von Juni brauchen. Der Umzug steht in
[Abschnitt 3a](#3a-umzug-von-supabase).
- **Die Datenbank gehört zum Projekt.** Was eine gehostete Plattform sonst
beisteuert, bringt `deploy/db-init/` mit: die Anwendungsrolle ohne
BYPASSRLS, die zwei benutzten Erweiterungen und eine Attrappe des
`auth`-Schemas, die nur die Migrationen von Juni 2026 brauchen.
- Läuft die Datenbank woanders (Azure Flexible Server, RDS, eigenes Blech),
genügt es, `DATABASE_URL` dorthin zeigen zu lassen und den `db`-Dienst nicht
zu starten. Die Anwendung merkt keinen Unterschied.
@@ -152,41 +150,6 @@ docker compose up -d --build
`migrate` steht im Profil `tools` und läuft bei `docker compose up` nicht mit.
Auf dem Host wird dafür weder Node noch psql gebraucht — nur Docker.
## 3a. Umzug von Supabase
Einmalig, wenn der Bestand noch bei Supabase liegt:
```bash
SUPABASE_DB_URL='postgresql://postgres:<passwort>@<projekt>.supabase.com:5432/postgres' \
./scripts/umzug-von-supabase.sh
```
Was das Skript tut und warum:
1. **Schema aus den Migrationen**, nicht aus einem Abzug. Nachgewiesen ist,
dass alle Migrationen auf einer leeren Datenbank durchlaufen und dabei
Spalte für Spalte, Index für Index, Policy für Policy dasselbe ergeben wie
die gewachsene Produktion. Der Weg hat zwei Vorteile: die Buchführung
stimmt danach von selbst, und Supabase-eigene Rechte und Eigentümer kommen
gar nicht erst mit.
2. **Nur die Daten** werden abgezogen (`pg_dump --data-only
--disable-triggers`). Ohne `--disable-triggers` stolpert jede
Fremdschlüsselprüfung über die Ladereihenfolge. RLS steht nicht im Weg —
keine der 19 Tabellen hat `FORCE ROW LEVEL SECURITY`, der Eigentümer
schreibt also durch.
3. Am Ende werden die Zeilenzahlen ausgegeben. **Mit denen bei Supabase
vergleichen**, bevor irgendetwas abgeschaltet wird.
Der Abzug bleibt als Datei liegen. Erst löschen, wenn die Anwendung gegen die
neue Datenbank nachweislich läuft.
Danach in `.env`:
```
DATABASE_URL=postgresql://alpenwerk_app:<APP_DB_PASSWORD>@db:5432/alpenwerk
DATABASE_SSL=false
```
### Was am `auth`-Schema übrigbleibt
`deploy/db-init/01-auth-attrappe.sql` legt ein leeres `auth`-Schema an. Es wird
@@ -283,8 +246,8 @@ und löscht sie danach wieder.
| Secret | Anmerkung |
|---|---|
| `DATABASE_URL` | Transaktions-Pooler (Supabase: 6543) — den benutzt die Anwendung |
| `SUPABASE_DB_URL` | **Direkter** Zugang (Supabase: 5432) — nur für die Migrationen |
| `DATABASE_URL` | Verbindung der Anwendungsrolle — die benutzt die Anwendung |
| `MIGRATE_DATABASE_URL` | Zugang als Verwalter — nur für die Migrationen |
| `AUTH_SECRET` | |
| `AUTH_URL` | z. B. `https://hr.elycon.solutions` |
| `AUTH_MICROSOFT_ENTRA_ID_ID` | |
@@ -332,9 +295,9 @@ node --env-file=.env scripts/migrate.mjs --dry-run # was stünde an
node --env-file=.env scripts/migrate.mjs # anwenden
```
Buch geführt wird in `supabase_migrations.schema_migrations`, derselben Tabelle
in derselben Form, die die Supabase-CLI benutzt — `supabase db push` von der
Arbeitsstation bleibt damit möglich und überspringt, was hier schon lief.
Buch geführt wird in `migrationen.schema_migrations`: eine Zeile je
angewendeter Datei, mit ihrem Text. Der Läufer wendet nur an, was dort
fehlt.
> **Einmalig, im August 2026 bereits erledigt:** Die Migrationen wurden bis
> dahin von Hand eingespielt, die Buchführungstabelle existierte gar nicht. Ein
@@ -409,9 +372,6 @@ Single-Instance-Compose-Konfiguration ist das nicht nötig.
- **Cron läuft nicht:** `docker compose logs cron` – prüft, ob
`/etc/crontabs/root` korrekt geschrieben wurde und ob `CRON_SECRET` in
`.env` gesetzt ist (leer/fehlend führt serverseitig zu `401`).
- **`max clients reached in session mode`:** der Verbindungsstring zeigt auf
den Sitzungs-Modus des Poolers. Auf den Transaktions-Modus wechseln (bei
Supabase Port 6543).
- **Healthcheck rot:** `docker compose logs app` – meist `DATABASE_URL`
fehlend oder nicht erreichbar. Der Pool baut die Verbindung erst beim
ersten Zugriff auf, der Fehler steht deshalb im Log der Anfrage, nicht im