diff --git a/backend/migrations/001_init.sql b/backend/migrations/001_init.sql index 3f56509..ab066f1 100644 --- a/backend/migrations/001_init.sql +++ b/backend/migrations/001_init.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS nodes ( CREATE TABLE IF NOT EXISTS domains ( id SERIAL PRIMARY KEY, domain TEXT UNIQUE NOT NULL, - node_name TEXT NOT NULL, + node_name TEXT, status TEXT NOT NULL DEFAULT 'active', first_seen_at TIMESTAMPTZ DEFAULT now(), last_seen_at TIMESTAMPTZ DEFAULT now() @@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS mailboxes ( id SERIAL PRIMARY KEY, email_address TEXT UNIQUE NOT NULL, domain TEXT NOT NULL, - node_name TEXT NOT NULL, + node_name TEXT, status TEXT NOT NULL DEFAULT 'active', used_bytes BIGINT DEFAULT 0, last_usage_scan_at TIMESTAMPTZ, @@ -53,6 +53,12 @@ ALTER TABLE nodes ALTER TABLE nodes ADD COLUMN IF NOT EXISTS is_current BOOLEAN DEFAULT false; +ALTER TABLE domains + ADD COLUMN IF NOT EXISTS node_name TEXT; + +ALTER TABLE domains + ADD COLUMN IF NOT EXISTS current_node TEXT; + ALTER TABLE domains ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT now(); @@ -62,6 +68,9 @@ ALTER TABLE domains ALTER TABLE domains ADD COLUMN IF NOT EXISTS notes TEXT DEFAULT ''; +ALTER TABLE mailboxes + ADD COLUMN IF NOT EXISTS node_name TEXT; + ALTER TABLE mailboxes ADD COLUMN IF NOT EXISTS local_part TEXT; @@ -107,14 +116,69 @@ ALTER TABLE audit_log ALTER TABLE audit_log ALTER COLUMN details SET DEFAULT '{}'; --- Fill local_part for existing rows +-- ============================================================ +-- Compatibility backfills +-- ============================================================ + +-- If an earlier version used current_node instead of node_name, copy it. +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'domains' + AND column_name = 'current_node' + ) THEN + EXECUTE ' + UPDATE domains + SET node_name = current_node + WHERE node_name IS NULL + AND current_node IS NOT NULL + '; + END IF; +END $$; + +-- If node_name is still empty, set it to current app node from env when available. +DO $$ +DECLARE + app_node_name TEXT; +BEGIN + app_node_name := current_setting('app.node_name', true); + + IF app_node_name IS NOT NULL AND app_node_name <> '' THEN + UPDATE domains + SET node_name = app_node_name + WHERE node_name IS NULL; + + UPDATE mailboxes + SET node_name = app_node_name + WHERE node_name IS NULL; + END IF; +END $$; + +-- Fill current_node from node_name for compatibility with newer code variants. +UPDATE domains +SET current_node = node_name +WHERE current_node IS NULL + AND node_name IS NOT NULL; + +-- Fill local_part for existing rows. UPDATE mailboxes SET local_part = split_part(email_address, '@', 1) WHERE local_part IS NULL AND email_address LIKE '%@%'; +-- Fill mailbox node_name from domain node_name where possible. +UPDATE mailboxes m +SET node_name = d.node_name +FROM domains d +WHERE m.domain = d.domain + AND m.node_name IS NULL + AND d.node_name IS NOT NULL; + -- Keep old and new usage timestamp columns in sync initially. --- This must be guarded because older/newer MVP databases may not have last_usage_scan_at. +-- Dynamic EXECUTE is important here, otherwise PostgreSQL may still parse a missing column. DO $$ BEGIN IF EXISTS ( @@ -124,15 +188,16 @@ BEGIN AND table_name = 'mailboxes' AND column_name = 'last_usage_scan_at' ) THEN - UPDATE mailboxes - SET usage_scanned_at = last_usage_scan_at - WHERE usage_scanned_at IS NULL - AND last_usage_scan_at IS NOT NULL; + EXECUTE ' + UPDATE mailboxes + SET usage_scanned_at = last_usage_scan_at + WHERE usage_scanned_at IS NULL + AND last_usage_scan_at IS NOT NULL + '; END IF; END $$; -- Backfill new audit target columns from old target column. --- This must also be guarded because some installations may already use target_id only. DO $$ BEGIN IF EXISTS ( @@ -142,10 +207,12 @@ BEGIN AND table_name = 'audit_log' AND column_name = 'target' ) THEN - UPDATE audit_log - SET target_id = target - WHERE target_id IS NULL - AND target IS NOT NULL; + EXECUTE ' + UPDATE audit_log + SET target_id = target + WHERE target_id IS NULL + AND target IS NOT NULL + '; END IF; END $$; @@ -153,10 +220,40 @@ UPDATE audit_log SET target_type = 'unknown' WHERE target_type IS NULL; +-- ============================================================ +-- Final constraints/defaults after backfill +-- ============================================================ + +ALTER TABLE domains + ALTER COLUMN node_name SET DEFAULT 'unknown'; + +ALTER TABLE mailboxes + ALTER COLUMN node_name SET DEFAULT 'unknown'; + +UPDATE domains +SET node_name = 'unknown' +WHERE node_name IS NULL; + +UPDATE mailboxes +SET node_name = 'unknown' +WHERE node_name IS NULL; + +ALTER TABLE domains + ALTER COLUMN node_name SET NOT NULL; + +ALTER TABLE mailboxes + ALTER COLUMN node_name SET NOT NULL; + +-- ============================================================ -- Useful indexes +-- ============================================================ + CREATE INDEX IF NOT EXISTS idx_domains_node_name ON domains(node_name); +CREATE INDEX IF NOT EXISTS idx_domains_current_node + ON domains(current_node); + CREATE INDEX IF NOT EXISTS idx_mailboxes_domain ON mailboxes(domain);