-- AP0.2: Auth, Identity, Tenant & Actor Foundation CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, display_name VARCHAR(255) NOT NULL, portal_role VARCHAR(32) NOT NULL DEFAULT 'user' CHECK (portal_role IN ('user', 'admin')), is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE tenants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug VARCHAR(64) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE tenant_memberships ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, tenant_role VARCHAR(32) NOT NULL DEFAULT 'member' CHECK (tenant_role IN ('owner', 'admin', 'member')), is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (tenant_id, user_id) ); CREATE INDEX idx_tenant_memberships_user ON tenant_memberships(user_id); CREATE TABLE actors ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, actor_type VARCHAR(32) NOT NULL CHECK (actor_type IN ('human', 'agent', 'working_group', 'external_system')), name VARCHAR(255) NOT NULL, user_id UUID NULL REFERENCES users(id) ON DELETE SET NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CHECK ( (actor_type = 'human' AND user_id IS NOT NULL) OR (actor_type <> 'human' AND user_id IS NULL) ) ); CREATE UNIQUE INDEX idx_actors_human_user_tenant ON actors(tenant_id, user_id) WHERE actor_type = 'human'; CREATE INDEX idx_actors_tenant ON actors(tenant_id); CREATE TABLE sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), token VARCHAR(128) NOT NULL UNIQUE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, active_tenant_id UUID NULL REFERENCES tenants(id) ON DELETE SET NULL, expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_sessions_token ON sessions(token); CREATE INDEX idx_sessions_user_id ON sessions(user_id); CREATE TABLE audit_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), action VARCHAR(64) NOT NULL, user_id UUID NULL REFERENCES users(id) ON DELETE SET NULL, tenant_id UUID NULL REFERENCES tenants(id) ON DELETE SET NULL, details JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_audit_log_created ON audit_log(created_at DESC);