3 Commits

Author SHA1 Message Date
b87c8ad64c 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>
2026-09-07 10:43:22 +02:00
77d9a95f7f Run the database in a container of our own
Some checks failed
CI / Lint, Typen, Tests, Build (push) Failing after 5m25s
CI / Integrationstests (echtes Postgres) (push) Failing after 5m9s
Supabase was only ever the host: the application has talked to PostgreSQL
directly through pg/Kysely for a while. So the move is mostly about
supplying what the platform used to supply.

Proved before building anything. All 65 migrations replay onto an empty
database, and the result matches production exactly — 183 columns, 25
policies, 68 indexes, 84 constraints, identical sets, no diff. The only
function missing from the rebuild turned out to matter, see below.

What the platform supplied, deploy/db-init now does:

  - alpenwerk_app, explicitly NOBYPASSRLS. The whole access model is 21
    RLS policies; a role that bypasses them would leave everything
    working while showing too much, and nobody would notice.
  - pgcrypto and pg_trgm. uuid-ossp was available on Supabase but is
    used nowhere — no column default, no function calls uuid_generate_*.
  - anon, authenticated and service_role as NOLOGIN placeholders. No
    policy names them; they only carry grants the platform handed out,
    and a data dump referencing them would fail to restore without them.
  - A stub `auth` schema. The end state needs none of it — checked: no
    foreign key, no policy, no column default refers to it. The June
    2026 migrations do, and rewriting those would be falsifying history;
    they describe what was true then.

The gap the comparison found: rls_auto_enable() and the ensure_rls event
trigger existed only in the running database, created by hand, in no
migration. That is the net which forces RLS on every newly created
table — the reason a forgotten policy yields an empty table instead of
an open one. A rebuild from migrations would silently not have had it:
everything works, and the next new table is unprotected. Now a migration
(20260819100000), verified by creating a table on the rebuild and
confirming RLS came on by itself.

Data moves separately, via scripts/umzug-von-supabase.sh: schema from
the migrations, then pg_dump --data-only --disable-triggers for the rows.
Without --disable-triggers every foreign key trips over load order. RLS
does not interfere — none of the 19 tables uses FORCE ROW LEVEL
SECURITY, so the owner writes through. The dump is deliberately left on
disk afterwards.

psql and node come from two `tools`-profile services rather than being
installed on the host, so the server needs nothing but Docker. The db
service publishes no port at all — reachable only inside the compose
network.

SUPABASE_DB_URL is renamed MIGRATE_DATABASE_URL, since after this it
describes something else entirely; the old name still works so existing
.env files keep running. Both were exercised, as was the error when
neither is set.

The deploy workflow is set to manual-only. Its preconditions were never
met — no secrets, and whether the job container can reach the host's
Docker daemon is untested — and failing on every push teaches people to
ignore red runs. It also needs updating for the new database service
before it could work at all.

Not verified: none of this has run in an actual container. There is no
Docker daemon on this machine. What is verified is the part that
decides whether it can work — the schema, on a real empty database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:34:08 +02:00
fc0989debb Deploy from a push, and start keeping track of migrations
Some checks failed
CI / Lint, Typen, Tests, Build (push) Failing after 5m52s
CI / Integrationstests (echtes Postgres) (push) Failing after 5m13s
Deploy / Migrationen und Container (push) Has been cancelled
A push to master now builds and restarts the application on the server:
a Gitea Actions workflow on a self-hosted runner writes .env from the
repository secrets, applies pending migrations, rebuilds the compose
stack against the host's Docker daemon, and waits for the container's
healthcheck before calling the run green. Without that last step a
deploy counts as successful the moment the container *starts*, even if
the app inside it dies immediately.

Switching migrations on automatically turned up something that had to be
fixed first: supabase_migrations.schema_migrations did not exist at all.
Every one of the 65 migrations was unrecorded, because they have been
applied by hand all along. An automatic `db push` would therefore have
replayed all 65 against the live database — initial_schema and the OM
cutover included. The database was checked against a spread of
migrations first (it is at head), then baselined: all 65 recorded as
applied without executing them.

The runner is scripts/migrate.mjs rather than the Supabase CLI. It needs
only `pg`, which the project already ships, instead of downloading a CLI
whose version drifts independently of this repository; and it does one
thing — the missing files, in order, each in its own transaction — where
`db push` also diffs schemas and may do more than that. Bookkeeping goes
in the same table in the same shape the CLI uses, so `supabase db push`
from a workstation still works and still skips what already ran.

The workflow lives in .github/workflows, not .gitea/. Gitea reads
.gitea/workflows and falls back to .github/workflows only when the
former is absent — creating .gitea/ would have silently switched off
ci.yml, with the run simply never appearing.

Verified: both workflow files parse; the secret check names what is
missing and refuses; values starting with "-" or containing "=" survive
being written to .env; and the runner was exercised against the real
database with a throwaway migration — applied once, skipped on a second
run, and on a deliberate syntax error rolled back whole, recording
nothing. Both probes were removed; the count is back to 65.

Not verified: nothing has run on an actual Gitea runner — none is
registered yet. DEPLOYMENT.md §5 covers registering one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:00:45 +02:00