From 357ebad821ec91f35a1793aedb0192dc611b0b64 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 30 Mar 2026 19:42:05 +0800 Subject: [PATCH] chore(db): rebuild init schema with drop-recreate and group-centric constraints --- scripts/init_schema.sql | 99 ++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/scripts/init_schema.sql b/scripts/init_schema.sql index 3197aad..23bae1c 100644 --- a/scripts/init_schema.sql +++ b/scripts/init_schema.sql @@ -2,7 +2,22 @@ BEGIN; CREATE EXTENSION IF NOT EXISTS pgcrypto; -CREATE TABLE IF NOT EXISTS users ( +-- Drop all managed tables to ensure clean rebuild +DROP TABLE IF EXISTS auth_sync_state CASCADE; +DROP TABLE IF EXISTS user_scope_permissions CASCADE; +DROP TABLE IF EXISTS permission_group_permissions CASCADE; +DROP TABLE IF EXISTS permission_group_members CASCADE; +DROP TABLE IF EXISTS permission_groups CASCADE; +DROP TABLE IF EXISTS modules CASCADE; +DROP TABLE IF EXISTS systems CASCADE; +DROP TABLE IF EXISTS sites CASCADE; +DROP TABLE IF EXISTS companies CASCADE; +DROP TABLE IF EXISTS users CASCADE; +DROP TABLE IF EXISTS api_clients CASCADE; +-- remove legacy table if present +DROP TABLE IF EXISTS permissions CASCADE; + +CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), authentik_sub TEXT NOT NULL UNIQUE, authentik_user_id INTEGER, @@ -14,7 +29,7 @@ CREATE TABLE IF NOT EXISTS users ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS auth_sync_state ( +CREATE TABLE auth_sync_state ( user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, last_synced_at TIMESTAMPTZ, source_version TEXT, @@ -22,7 +37,7 @@ CREATE TABLE IF NOT EXISTS auth_sync_state ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS companies ( +CREATE TABLE companies ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, @@ -31,7 +46,7 @@ CREATE TABLE IF NOT EXISTS companies ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS sites ( +CREATE TABLE sites ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_key TEXT NOT NULL UNIQUE, company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, @@ -41,7 +56,7 @@ CREATE TABLE IF NOT EXISTS sites ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS systems ( +CREATE TABLE systems ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), system_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, @@ -50,7 +65,7 @@ CREATE TABLE IF NOT EXISTS systems ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS modules ( +CREATE TABLE modules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), module_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, @@ -59,20 +74,8 @@ CREATE TABLE IF NOT EXISTS modules ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); --- legacy table: 保留相容舊流程 -CREATE TABLE IF NOT EXISTS permissions ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, - scope_type VARCHAR(32) NOT NULL, - scope_id VARCHAR(128) NOT NULL, - module VARCHAR(128) NOT NULL, - action VARCHAR(32) NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - CONSTRAINT uq_permissions_user_scope_module_action - UNIQUE (user_id, scope_type, scope_id, module, action) -); - -CREATE TABLE IF NOT EXISTS user_scope_permissions ( +-- direct permission table retained only for compatibility +CREATE TABLE user_scope_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, module_id UUID NOT NULL REFERENCES modules(id) ON DELETE CASCADE, @@ -81,18 +84,14 @@ CREATE TABLE IF NOT EXISTS user_scope_permissions ( company_id UUID REFERENCES companies(id) ON DELETE CASCADE, site_id UUID REFERENCES sites(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT user_scope_permissions_scope_check + CHECK (scope_type = 'site' AND site_id IS NOT NULL AND company_id IS NULL), + CONSTRAINT user_scope_permissions_action_check + CHECK (action IN ('view', 'edit')) ); -ALTER TABLE user_scope_permissions DROP CONSTRAINT IF EXISTS user_scope_permissions_check; -ALTER TABLE user_scope_permissions - ADD CONSTRAINT user_scope_permissions_check - CHECK ( - ((scope_type = 'company' AND company_id IS NOT NULL AND site_id IS NULL) - OR (scope_type = 'site' AND site_id IS NOT NULL AND company_id IS NULL)) - ); - -CREATE TABLE IF NOT EXISTS permission_groups ( +CREATE TABLE permission_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, @@ -101,7 +100,7 @@ CREATE TABLE IF NOT EXISTS permission_groups ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS permission_group_members ( +CREATE TABLE permission_group_members ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE, authentik_sub TEXT NOT NULL, @@ -109,7 +108,7 @@ CREATE TABLE IF NOT EXISTS permission_group_members ( CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, authentik_sub) ); -CREATE TABLE IF NOT EXISTS permission_group_permissions ( +CREATE TABLE permission_group_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE, system TEXT NOT NULL, @@ -118,10 +117,12 @@ CREATE TABLE IF NOT EXISTS permission_group_permissions ( scope_type TEXT NOT NULL, scope_id TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT permission_group_permissions_scope_check CHECK (scope_type = 'site'), + CONSTRAINT permission_group_permissions_action_check CHECK (action IN ('view', 'edit')), CONSTRAINT uq_pgp_group_rule UNIQUE (group_id, system, module, action, scope_type, scope_id) ); -CREATE TABLE IF NOT EXISTS api_clients ( +CREATE TABLE api_clients ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), client_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, @@ -141,22 +142,20 @@ INSERT INTO systems (system_key, name, status) VALUES ('member', 'Member Center', 'active') ON CONFLICT (system_key) DO NOTHING; -CREATE INDEX IF NOT EXISTS idx_users_authentik_sub ON users(authentik_sub); -CREATE INDEX IF NOT EXISTS idx_sites_company_id ON sites(company_id); -CREATE INDEX IF NOT EXISTS idx_permissions_user_id ON permissions(user_id); -CREATE INDEX IF NOT EXISTS idx_usp_user_id ON user_scope_permissions(user_id); -CREATE INDEX IF NOT EXISTS idx_usp_module_id ON user_scope_permissions(module_id); -CREATE INDEX IF NOT EXISTS idx_usp_company_id ON user_scope_permissions(company_id); -CREATE INDEX IF NOT EXISTS idx_usp_site_id ON user_scope_permissions(site_id); -CREATE UNIQUE INDEX IF NOT EXISTS uq_usp_company - ON user_scope_permissions(user_id, module_id, action, scope_type, company_id) - WHERE scope_type = 'company'; -CREATE UNIQUE INDEX IF NOT EXISTS uq_usp_site - ON user_scope_permissions(user_id, module_id, action, scope_type, site_id) - WHERE scope_type = 'site'; -CREATE INDEX IF NOT EXISTS idx_api_clients_status ON api_clients(status); -CREATE INDEX IF NOT EXISTS idx_api_clients_expires_at ON api_clients(expires_at); -CREATE INDEX IF NOT EXISTS idx_systems_system_key ON systems(system_key); -CREATE INDEX IF NOT EXISTS idx_modules_module_key ON modules(module_key); +CREATE INDEX idx_users_authentik_sub ON users(authentik_sub); +CREATE INDEX idx_sites_company_id ON sites(company_id); +CREATE INDEX idx_usp_user_id ON user_scope_permissions(user_id); +CREATE INDEX idx_usp_module_id ON user_scope_permissions(module_id); +CREATE INDEX idx_usp_site_id ON user_scope_permissions(site_id); +CREATE UNIQUE INDEX uq_usp_site + ON user_scope_permissions(user_id, module_id, action, scope_type, site_id); +CREATE INDEX idx_pgm_group_id ON permission_group_members(group_id); +CREATE INDEX idx_pgm_authentik_sub ON permission_group_members(authentik_sub); +CREATE INDEX idx_pgp_group_id ON permission_group_permissions(group_id); +CREATE INDEX idx_pgp_scope_site ON permission_group_permissions(scope_id); +CREATE INDEX idx_api_clients_status ON api_clients(status); +CREATE INDEX idx_api_clients_expires_at ON api_clients(expires_at); +CREATE INDEX idx_systems_system_key ON systems(system_key); +CREATE INDEX idx_modules_module_key ON modules(module_key); COMMIT;