chore(docker): seeder service pre-creates unprefixed 'admin' user row

Alerting + outbound controllers resolve acting user via
authentication.name with 'user:' prefix stripped → 'admin'. But
UserRepository.upsert stores env-admin as 'user:admin' (JWT sub format).
The resulting FK mismatch manifests as 500 'alert_rules_created_by_fkey'
on any create operation in a fresh docker stack.

Workaround: run-once 'cameleer-seed' compose service runs psql against
deploy/docker/postgres-init.sql after the server is healthy (i.e. after
Flyway migrations have created tenant_default.users), inserting
user_id='admin' idempotently. The root-cause fix belongs in the backend
(either stop stripping the prefix in alerting/outbound controllers, or
normalise storage to the unprefixed form) and is out of scope for
Plan 03.
This commit is contained in:
hsiegeln
2026-04-20 16:18:07 +02:00
parent bcde6678b8
commit d88bede097
2 changed files with 60 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
-- Dev-stack seed: pre-create the `admin` user row without the `user:` prefix.
--
-- Why: the UI login controller stores the local admin as `user_id='user:admin'`
-- (JWT `sub` format), but the alerting + outbound controllers resolve the FK
-- via `authentication.name` with the `user:` prefix stripped, i.e. `admin`.
-- In k8s these controllers happily insert `admin` because production admins are
-- provisioned through the admin API with unprefixed user_ids. In the local
-- docker stack there's no such provisioning step, so the FK check fails with
-- "alert_rules_created_by_fkey violation" on the first rule create.
--
-- Seeding a row with `user_id='admin'` here bridges the gap so E2E smokes,
-- API probes, and manual dev sessions can create alerting rows straight away.
-- Flyway owns the schema in tenant_default; this script only INSERTs idempotently
-- and is gated on the schema existing.
DO $$
DECLARE
schema_exists bool;
table_exists bool;
BEGIN
SELECT EXISTS(
SELECT 1 FROM information_schema.schemata WHERE schema_name = 'tenant_default'
) INTO schema_exists;
IF NOT schema_exists THEN
RAISE NOTICE 'tenant_default schema not yet migrated — skipping admin seed (Flyway will run on server start)';
RETURN;
END IF;
SELECT EXISTS(
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'tenant_default' AND table_name = 'users'
) INTO table_exists;
IF NOT table_exists THEN
RAISE NOTICE 'tenant_default.users not yet migrated — skipping admin seed';
RETURN;
END IF;
INSERT INTO tenant_default.users (user_id, provider, email, display_name)
VALUES ('admin', 'local', '', 'admin')
ON CONFLICT (user_id) DO NOTHING;
END $$;

View File

@@ -130,6 +130,25 @@ services:
retries: 10 retries: 10
restart: unless-stopped restart: unless-stopped
# Run-once seeder: waits for the server to be healthy (i.e. Flyway migrations
# finished) and inserts a `user_id='admin'` row (without the `user:` prefix)
# so alerting-controller FKs succeed. See deploy/docker/postgres-init.sql for
# the full rationale. Idempotent — exits 0 if the row already exists.
cameleer-seed:
image: postgres:16
container_name: cameleer-seed
depends_on:
cameleer-server:
condition: service_healthy
environment:
PGPASSWORD: cameleer_dev
volumes:
- ./deploy/docker/postgres-init.sql:/seed.sql:ro
entrypoint: ["sh", "-c"]
command:
- "psql -h cameleer-postgres -U cameleer -d cameleer -v ON_ERROR_STOP=1 -f /seed.sql"
restart: "no"
volumes: volumes:
cameleer-pgdata: cameleer-pgdata:
cameleer-chdata: cameleer-chdata: