-- Migration: Definitive enqueue_all_notifications() dispatcher + self-hosted fix -- -- WHY THIS EXISTS: -- 1. Sort-order bug: 20260322_finalize_notification_functions.sql has an -- underscore at filename position 9, which sorts AFTER all 20260322NNNNNN_* -- files (underscore ASCII 95 > digits 48-57). It therefore ran LAST and -- overrode enqueue_all_notifications() with a version that omitted: -- - enqueue_announcement_banner_notifications() (added in 20260322210000) -- - enqueue_pass_slip_expired_notifications() (added in 20260322150000) -- This migration (20260323000000) sorts after all 20260322* files and -- restores the authoritative 10-type definition. -- -- 2. Self-hosted Docker: process_notification_queue() fires the edge function -- via pg_net (HTTP), but on self-hosted the vault secrets and GUC fallbacks -- are often not configured, so the function returns early and the edge -- function is never triggered. enqueue_all_notifications() therefore never -- runs and scheduled_notifications is never populated. -- Fix: a dedicated pg_cron job calls enqueue_all_notifications() directly -- (pure SQL, no HTTP, no vault required). The edge function is still called -- for FCM delivery — only the enqueue step is decoupled. -- ============================================================================ -- 1. Authoritative master dispatcher (all 10 types) -- ============================================================================ CREATE OR REPLACE FUNCTION public.enqueue_all_notifications() RETURNS void LANGUAGE plpgsql AS $$ BEGIN PERFORM public.enqueue_due_shift_notifications(); PERFORM public.enqueue_overtime_idle_notifications(); PERFORM public.enqueue_overtime_checkout_notifications(); PERFORM public.enqueue_isr_event_notifications(); PERFORM public.enqueue_isr_evidence_notifications(); PERFORM public.enqueue_paused_task_notifications(); PERFORM public.enqueue_backlog_notifications(); PERFORM public.enqueue_pass_slip_expiry_notifications(); PERFORM public.enqueue_pass_slip_expired_notifications(); PERFORM public.enqueue_announcement_banner_notifications(); END; $$; -- ============================================================================ -- 2. Dedicated enqueue cron job (self-hosted compatible) -- ============================================================================ -- This job calls enqueue_all_notifications() directly inside PostgreSQL every -- minute. It requires only pg_cron — no pg_net, no vault, no HTTP. -- -- On Supabase cloud: both this job and the edge-function trigger run, which -- is safe because every enqueue function is idempotent via -- ON CONFLICT DO NOTHING. -- On self-hosted Docker: this job ensures scheduled_notifications is populated -- even when the pg_net → edge-function path is broken. -- For FCM delivery you still need the edge function to -- run; use the system crontab as shown below if pg_net -- is not configured: -- -- */1 * * * * curl -sS -X POST \ -- "http://localhost:8000/functions/v1/process_scheduled_notifications" \ -- -H "Authorization: Bearer " \ -- -H "Content-Type: application/json" -d '{}' -- -- Self-hosted vault / GUC setup (run once, required for pg_net path): -- SELECT vault.create_secret('http://localhost:8000', 'supabase_url'); -- SELECT vault.create_secret('', 'service_role_key'); -- -- or via GUC (no vault extension required): -- ALTER DATABASE postgres SET "app.settings.supabase_url" = 'http://localhost:8000'; -- ALTER DATABASE postgres SET "app.settings.service_role_key" = ''; DO $$ BEGIN PERFORM cron.schedule( 'notification_enqueue_every_min', '*/1 * * * *', 'SELECT public.enqueue_all_notifications();' ); EXCEPTION WHEN others THEN RAISE NOTICE 'pg_cron not available (%). Use the system crontab to call the edge function directly instead.', SQLERRM; END $$;