Files
multica/server/migrations/600_platform_admin.up.sql
Claude d1978ec404 chore(migrations): renumber platform admin migrations to 600-606
main now carries 583 and 584, so the admin console migrations move past
the numbers reserved by the open-connector work (590-599).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FrVjAFWeckYV69HS51xUhy
2026-10-31 10:05:29 +00:00

38 lines
1.6 KiB
SQL

-- Platform admin console: platform-level roles, an append-only audit log, and
-- persisted account suspension. Relationships (user ids) are validated in
-- application code; no foreign keys by project rule.
--
-- Primary keys are attached in 585/587 from the indexes built CONCURRENTLY in
-- 584/586 (repo convention, see 574-575).
CREATE TABLE IF NOT EXISTS platform_admin (
user_id UUID NOT NULL,
role TEXT NOT NULL CHECK (role IN ('super_admin', 'operator', 'viewer')),
granted_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS admin_audit_log (
id UUID NOT NULL DEFAULT gen_random_uuid(),
actor_user_id UUID NOT NULL,
action TEXT NOT NULL,
target_type TEXT NOT NULL DEFAULT '',
target_id TEXT NOT NULL DEFAULT '',
reason TEXT NOT NULL DEFAULT '',
payload JSONB NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(payload) = 'object'),
ip TEXT NOT NULL DEFAULT '',
user_agent TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Persisted suspension replaces the hardcoded denylist in
-- internal/auth/temporary_disabled_users.go.
ALTER TABLE "user"
ADD COLUMN IF NOT EXISTS disabled_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS disabled_reason TEXT NOT NULL DEFAULT '';
-- Carry the former hardcoded denylist over so those accounts stay suspended.
UPDATE "user" SET disabled_at = now(), disabled_reason = 'migrated from emergency denylist'
WHERE disabled_at IS NULL
AND (id IN ('514492f7-b30f-4147-bd33-c0e8ce5d6d4f', '1d542296-17c6-484a-9914-dcee589be116')
OR lower(email) IN ('[email protected]', '[email protected]'));