From 4ea80fa748e2c883fe61da1bffbaaf87ad462d18 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 30 Mar 2026 02:14:26 +0800 Subject: [PATCH] chore: consolidate full database schema into single init_schema.sql --- scripts/init_schema.sql | 96 +++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/scripts/init_schema.sql b/scripts/init_schema.sql index 5bbe825..3e4c524 100644 --- a/scripts/init_schema.sql +++ b/scripts/init_schema.sql @@ -7,6 +7,15 @@ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'record_status') THEN CREATE TYPE record_status AS ENUM ('active','inactive'); END IF; + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'client_status') THEN + CREATE TYPE client_status AS ENUM ('active','inactive'); + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'scope_type') THEN + CREATE TYPE scope_type AS ENUM ('company','site'); + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_action') THEN + CREATE TYPE permission_action AS ENUM ('view','create','update','delete','manage'); + END IF; END $$; @@ -22,6 +31,14 @@ CREATE TABLE IF NOT EXISTS users ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); +CREATE TABLE IF NOT EXISTS auth_sync_state ( + user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, + last_synced_at TIMESTAMPTZ, + source_version TEXT, + last_error TEXT, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + CREATE TABLE IF NOT EXISTS companies ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_key TEXT NOT NULL UNIQUE, @@ -31,6 +48,16 @@ CREATE TABLE IF NOT EXISTS companies ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); +CREATE TABLE IF NOT EXISTS 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, + name TEXT NOT NULL, + status record_status NOT NULL DEFAULT 'active', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + CREATE TABLE IF NOT EXISTS systems ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), system_key TEXT NOT NULL UNIQUE, @@ -49,27 +76,19 @@ CREATE TABLE IF NOT EXISTS modules ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE TABLE IF NOT EXISTS sites ( +-- legacy table: 保留相容舊流程 +CREATE TABLE IF NOT EXISTS permissions ( 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, - name TEXT NOT NULL, - status record_status NOT NULL DEFAULT 'active', + 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(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + CONSTRAINT uq_permissions_user_scope_module_action + UNIQUE (user_id, scope_type, scope_id, module, action) ); -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'scope_type') THEN - CREATE TYPE scope_type AS ENUM ('company','site'); - END IF; - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_action') THEN - CREATE TYPE permission_action AS ENUM ('view','create','update','delete','manage'); - END IF; -END -$$; - CREATE TABLE IF NOT EXISTS user_scope_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, @@ -90,14 +109,6 @@ ALTER TABLE user_scope_permissions OR (scope_type = 'site' AND site_id IS NOT NULL AND company_id IS NULL)) ); -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 TABLE IF NOT EXISTS permission_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_key TEXT NOT NULL UNIQUE, @@ -123,17 +134,46 @@ CREATE TABLE IF NOT EXISTS permission_group_permissions ( action TEXT NOT NULL, scope_type TEXT NOT NULL, scope_id TEXT NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT uq_pgp_group_rule UNIQUE (group_id, system, module, action, scope_type, scope_id) ); -CREATE UNIQUE INDEX IF NOT EXISTS uq_pgp_group_rule -ON permission_group_permissions(group_id, system, module, action, scope_type, scope_id); +CREATE TABLE IF NOT EXISTS api_clients ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_key TEXT NOT NULL UNIQUE, + name TEXT NOT NULL, + status client_status NOT NULL DEFAULT 'active', + api_key_hash TEXT NOT NULL, + allowed_origins JSONB NOT NULL DEFAULT '[]'::jsonb, + allowed_ips JSONB NOT NULL DEFAULT '[]'::jsonb, + allowed_paths JSONB NOT NULL DEFAULT '[]'::jsonb, + rate_limit_per_min INTEGER, + expires_at TIMESTAMPTZ, + last_used_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +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); COMMIT;