CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE IF NOT EXISTS nodes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT UNIQUE NOT NULL, hostname TEXT NOT NULL, is_current BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS domains ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), domain TEXT UNIQUE NOT NULL, current_node TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'active', first_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(), last_seen_at TIMESTAMPTZ, last_synced_at TIMESTAMPTZ, notes TEXT DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS mailboxes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email_address TEXT UNIQUE NOT NULL, local_part TEXT NOT NULL, domain TEXT NOT NULL REFERENCES domains(domain) ON DELETE CASCADE, node_name TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'active', used_bytes BIGINT NOT NULL DEFAULT 0, usage_scanned_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS admin_users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'super_admin', allowed_domains TEXT[] NOT NULL DEFAULT '{}', 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 audit_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), actor_email TEXT, action TEXT NOT NULL, target_type TEXT NOT NULL, target_id TEXT NOT NULL, details JSONB NOT NULL DEFAULT '{}', ip_address TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_domains_node ON domains(current_node); CREATE INDEX IF NOT EXISTS idx_mailboxes_domain ON mailboxes(domain); CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at DESC);