BEGIN; CREATE EXTENSION IF NOT EXISTS pgcrypto; -- Drop legacy/managed tables for clean rebuild DROP TABLE IF EXISTS auth_sync_state CASCADE; DROP TABLE IF EXISTS user_sites CASCADE; DROP TABLE IF EXISTS site_roles CASCADE; DROP TABLE IF EXISTS roles CASCADE; DROP TABLE IF EXISTS api_clients CASCADE; DROP TABLE IF EXISTS sites CASCADE; DROP TABLE IF EXISTS companies CASCADE; DROP TABLE IF EXISTS systems CASCADE; DROP TABLE IF EXISTS users CASCADE; -- legacy tables DROP TABLE IF EXISTS permissions CASCADE; DROP TABLE IF EXISTS modules 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; CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_sub TEXT NOT NULL UNIQUE, provider_user_id VARCHAR(128) UNIQUE, username TEXT UNIQUE, email TEXT UNIQUE, display_name TEXT, status VARCHAR(16) 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 companies ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, provider_group_id TEXT, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); 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, display_name TEXT NOT NULL, domain TEXT, provider_group_id TEXT, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE systems ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), system_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE roles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), role_key TEXT NOT NULL UNIQUE, role_code TEXT NOT NULL, system_id UUID NOT NULL REFERENCES systems(id) ON DELETE CASCADE, name TEXT NOT NULL, description TEXT, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_roles_system_name UNIQUE (system_id, name), CONSTRAINT uq_roles_system_role_code UNIQUE (system_id, role_code) ); CREATE TABLE site_roles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL REFERENCES sites(id) ON DELETE CASCADE, role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_site_roles_site_role UNIQUE (site_id, role_id) ); CREATE TABLE user_sites ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, site_id UUID NOT NULL REFERENCES sites(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_user_sites_user_site UNIQUE (user_id, site_id) ); CREATE TABLE auth_sync_state ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), entity_type VARCHAR(32) NOT NULL, entity_id UUID NOT NULL, last_synced_at TIMESTAMPTZ, source_version TEXT, last_error TEXT, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_auth_sync_state_entity UNIQUE (entity_type, entity_id) ); CREATE TABLE api_clients ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), client_key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, status VARCHAR(16) 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() ); CREATE INDEX idx_users_user_sub ON users(user_sub); CREATE INDEX idx_users_username ON users(username); CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_sites_company_id ON sites(company_id); CREATE INDEX idx_roles_system_id ON roles(system_id); CREATE INDEX idx_roles_role_code ON roles(role_code); CREATE INDEX idx_site_roles_site_id ON site_roles(site_id); CREATE INDEX idx_site_roles_role_id ON site_roles(role_id); CREATE INDEX idx_user_sites_user_id ON user_sites(user_id); CREATE INDEX idx_user_sites_site_id ON user_sites(site_id); CREATE INDEX idx_auth_sync_entity ON auth_sync_state(entity_type, entity_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); COMMIT;