package database // NotificationMigrations creates the one table Phase 9d needs: // // notification_log — one row per (event, lead_days, audience, channel) // that the cron scanner has already attempted. The UNIQUE constraint // makes the cron idempotent — running it twice on the same day does // not re-send. // // channel ∈ {'matrix', 'email'} — set by the dispatcher. // audience ∈ {'parents', 'students'}. // status ∈ {'sent', 'failed', 'skipped'} — 'skipped' when the upstream // service URL isn't configured, so we know not to count it as failure. func NotificationMigrations() []string { return []string{ `CREATE TABLE IF NOT EXISTS notification_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES cal_school_event(id) ON DELETE CASCADE, lead_days INT NOT NULL, audience VARCHAR(20) NOT NULL CHECK (audience IN ('parents','students')), channel VARCHAR(20) NOT NULL CHECK (channel IN ('matrix','email')), status VARCHAR(20) NOT NULL CHECK (status IN ('sent','failed','skipped')), error_message TEXT, run_date DATE NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(event_id, lead_days, audience, channel) )`, `CREATE INDEX IF NOT EXISTS idx_notification_log_event ON notification_log(event_id)`, `CREATE INDEX IF NOT EXISTS idx_notification_log_run_date ON notification_log(run_date)`, } }