BEGIN; CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), authentik_sub VARCHAR(255) NOT NULL UNIQUE, authentik_user_id INTEGER, email VARCHAR(320), display_name VARCHAR(255), 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 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 INDEX IF NOT EXISTS idx_users_authentik_sub ON users(authentik_sub); CREATE INDEX IF NOT EXISTS idx_permissions_user_id ON permissions(user_id); CREATE TABLE IF NOT EXISTS organizations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), org_code VARCHAR(64) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, tax_id VARCHAR(32), status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_organizations_org_code ON organizations(org_code); CREATE INDEX IF NOT EXISTS idx_organizations_status ON organizations(status); CREATE TABLE IF NOT EXISTS member_organizations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), member_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_member_organizations_member_org UNIQUE (member_id, organization_id) ); CREATE INDEX IF NOT EXISTS idx_member_organizations_member_id ON member_organizations(member_id); CREATE INDEX IF NOT EXISTS idx_member_organizations_org_id ON member_organizations(organization_id); COMMIT;