finalized reminder notification system migrations
This commit is contained in:
@@ -73,7 +73,7 @@ BEGIN
|
||||
FROM public.duty_schedules
|
||||
WHERE start_time BETWEEN now() + interval '15 minutes' - interval '90 seconds'
|
||||
AND now() + interval '15 minutes' + interval '90 seconds'
|
||||
AND status = 'active'
|
||||
AND status IN ('arrival', 'late')
|
||||
LOOP
|
||||
-- Skip if user already checked in for this duty
|
||||
IF EXISTS (
|
||||
@@ -106,6 +106,7 @@ BEGIN
|
||||
FROM public.duty_schedules ds
|
||||
JOIN public.attendance_logs al ON al.duty_schedule_id = ds.id AND al.user_id = ds.user_id
|
||||
WHERE ds.end_time < now()
|
||||
AND ds.end_time >= now() - interval '24 hours'
|
||||
AND ds.status IN ('arrival', 'late')
|
||||
AND al.check_in_at IS NOT NULL
|
||||
AND al.check_out_at IS NULL
|
||||
@@ -388,14 +389,18 @@ $$;
|
||||
CREATE OR REPLACE FUNCTION public.enqueue_pass_slip_expiry_notifications()
|
||||
RETURNS void LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
-- Match any active pass slip in the last 15 minutes (between 45–60 min old).
|
||||
-- Unlike narrow ±90s windows used for shift reminders, pass slips use a
|
||||
-- 15-minute window so this function doesn't need to be called at an exact
|
||||
-- second. ON CONFLICT DO NOTHING prevents duplicate notifications.
|
||||
INSERT INTO public.scheduled_notifications (pass_slip_id, user_id, notify_type, scheduled_for)
|
||||
SELECT ps.id, ps.user_id, 'pass_slip_expiry_15', now()
|
||||
FROM public.pass_slips ps
|
||||
WHERE ps.status = 'approved'
|
||||
AND ps.slip_end IS NULL
|
||||
AND ps.slip_start IS NOT NULL
|
||||
AND ps.slip_start + interval '45 minutes'
|
||||
BETWEEN now() - interval '90 seconds' AND now() + interval '90 seconds'
|
||||
AND ps.slip_start + interval '45 minutes' <= now()
|
||||
AND ps.slip_start + interval '60 minutes' > now()
|
||||
ON CONFLICT DO NOTHING;
|
||||
END;
|
||||
$$;
|
||||
@@ -420,8 +425,18 @@ $$;
|
||||
-- ============================================================================
|
||||
-- 4. PG_NET + PG_CRON SCHEDULING
|
||||
-- ============================================================================
|
||||
-- Uses pg_net to call the process_scheduled_notifications edge function
|
||||
-- from within PostgreSQL, eliminating the need for external cron jobs.
|
||||
-- The edge function (process_scheduled_notifications) now calls
|
||||
-- enqueue_all_notifications() via RPC internally, so only ONE pg_cron job
|
||||
-- is needed to trigger the edge function. This eliminates the fragile
|
||||
-- dependency on two independent cron jobs both working.
|
||||
--
|
||||
-- Credential lookup order:
|
||||
-- 1. vault.decrypted_secrets (Supabase cloud + self-hosted)
|
||||
-- 2. app.settings GUC variables (self-hosted fallback)
|
||||
--
|
||||
-- ONE-TIME SETUP (run once after migration):
|
||||
-- SELECT vault.create_secret('https://YOUR_PROJECT.supabase.co', 'supabase_url');
|
||||
-- SELECT vault.create_secret('YOUR_SERVICE_ROLE_KEY', 'service_role_key');
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_net;
|
||||
|
||||
@@ -432,7 +447,7 @@ DECLARE
|
||||
v_base_url text;
|
||||
v_service_key text;
|
||||
BEGIN
|
||||
-- Try Supabase vault first (standard in self-hosted Supabase)
|
||||
-- Try Supabase vault first (works on both cloud and self-hosted)
|
||||
BEGIN
|
||||
SELECT decrypted_secret INTO v_service_key
|
||||
FROM vault.decrypted_secrets
|
||||
@@ -448,7 +463,7 @@ BEGIN
|
||||
NULL;
|
||||
END;
|
||||
|
||||
-- Fall back to app.settings GUC variables
|
||||
-- Fall back to app.settings GUC variables (self-hosted)
|
||||
IF v_base_url IS NULL THEN
|
||||
v_base_url := current_setting('app.settings.supabase_url', true);
|
||||
END IF;
|
||||
@@ -458,10 +473,11 @@ BEGIN
|
||||
|
||||
-- Guard: skip if config is missing
|
||||
IF v_base_url IS NULL OR v_service_key IS NULL THEN
|
||||
RAISE WARNING 'process_notification_queue: missing supabase_url or service_role_key config';
|
||||
RAISE WARNING 'process_notification_queue: missing vault secrets. Run: SELECT vault.create_secret(''https://YOUR_PROJECT.supabase.co'', ''supabase_url''); SELECT vault.create_secret(''YOUR_KEY'', ''service_role_key'');';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- The edge function handles both enqueue + process internally
|
||||
PERFORM net.http_post(
|
||||
url := v_base_url || '/functions/v1/process_scheduled_notifications',
|
||||
headers := jsonb_build_object(
|
||||
@@ -485,31 +501,25 @@ BEGIN
|
||||
PERFORM cron.unschedule('notification_reminders_every_min');
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END;
|
||||
BEGIN
|
||||
PERFORM cron.unschedule('notification_enqueue_every_min');
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END;
|
||||
|
||||
-- Job 1: Enqueue notifications every minute
|
||||
PERFORM cron.schedule(
|
||||
'notification_enqueue_every_min',
|
||||
'*/1 * * * *',
|
||||
'SELECT public.enqueue_all_notifications();'
|
||||
);
|
||||
|
||||
-- Job 2: Process notification queue via pg_net every minute
|
||||
-- Single job: triggers edge function which handles enqueue + process
|
||||
PERFORM cron.schedule(
|
||||
'notification_process_every_min',
|
||||
'*/1 * * * *',
|
||||
'SELECT public.process_notification_queue();'
|
||||
);
|
||||
|
||||
-- Job 3: Daily cleanup of old processed notifications
|
||||
-- Daily cleanup of old processed notifications
|
||||
PERFORM cron.schedule(
|
||||
'cleanup_old_notifications',
|
||||
'0 3 * * *',
|
||||
'DELETE FROM scheduled_notifications WHERE processed = true AND processed_at < now() - interval ' || quote_literal('7 days') || '; DELETE FROM notification_pushes WHERE pushed_at < now() - interval ' || quote_literal('7 days') || ';'
|
||||
'DELETE FROM scheduled_notifications WHERE processed = true AND processed_at < now() - interval ''7 days''; DELETE FROM notification_pushes WHERE pushed_at < now() - interval ''7 days'';'
|
||||
);
|
||||
|
||||
EXCEPTION WHEN others THEN
|
||||
RAISE NOTICE 'pg_cron/pg_net not available. After enabling them, run these manually:';
|
||||
RAISE NOTICE ' SELECT cron.schedule(%L, %L, %L);', 'notification_enqueue_every_min', '*/1 * * * *', 'SELECT public.enqueue_all_notifications();';
|
||||
RAISE NOTICE ' SELECT cron.schedule(%L, %L, %L);', 'notification_process_every_min', '*/1 * * * *', 'SELECT public.process_notification_queue();';
|
||||
RAISE NOTICE ' SELECT cron.schedule(%L, %L, %L);', 'cleanup_old_notifications', '0 3 * * *', 'DELETE FROM scheduled_notifications WHERE processed = true AND processed_at < now() - interval ' || quote_literal('7 days') || '; DELETE FROM notification_pushes WHERE pushed_at < now() - interval ' || quote_literal('7 days') || ';';
|
||||
RAISE NOTICE 'pg_cron scheduling failed: %. Set up cron jobs manually or use external cron.', SQLERRM;
|
||||
END $$;
|
||||
|
||||
Reference in New Issue
Block a user