BEGIN; CREATE EXTENSION IF NOT EXISTS pgcrypto; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'record_status') THEN CREATE TYPE record_status AS ENUM ('active','inactive'); END IF; END $$; CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), authentik_sub TEXT NOT NULL UNIQUE, authentik_user_id INTEGER, email TEXT UNIQUE, display_name TEXT, status record_status NOT NULL DEFAULT 'active', is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 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, 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, 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 modules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), module_key TEXT NOT NULL UNIQUE, 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 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() ); 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, module_id UUID NOT NULL REFERENCES modules(id) ON DELETE CASCADE, action permission_action NOT NULL, scope_type scope_type NOT NULL, 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() ); 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 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, 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 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, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, authentik_sub) ); CREATE TABLE IF NOT EXISTS 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, module TEXT NOT NULL, action TEXT NOT NULL, scope_type TEXT NOT NULL, scope_id TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS uq_pgp_group_rule ON permission_group_permissions(group_id, system, module, action, scope_type, scope_id); 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_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); COMMIT;