From 0785f426f71394e755ed9a95a57db29caa7b8396 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Thu, 24 Sep 2026 22:28:46 +0000 Subject: [PATCH] fix: crawlproof-affiliate cron posted to a NULL url and never ran MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 20260913120000_openaffiliate.sql scheduled the job with current_setting('app.site_url', true), which is the exact pattern 20260515090000_cron_config.sql had removed four months earlier. Its header even explains why: the app.* GUCs can only be set by supabase_admin, migrations run as `postgres`, so they are never populated. NULL || '/api/...' is NULL and pg_net rejects the row with "null value in column url of relation http_request_queue violates not-null constraint". The job has therefore never run once since 2026-09-13. It hides well: it is visible only in cron.job_run_details, nothing alerts on it, and the other nine jobs are green. Found while auditing the ten jobs after the move to dev2 — the Supabase cloud project had been failing it hourly too, so self-hosting reproduced the bug rather than causing it. Fixed by reading public.cron_config like the other nine, NOT by setting the GUCs: a database-level setting is invisible to pg_dump, so it would be lost by the next migration and this would return a third time. Adds a guard that fails the migration loudly if either config value is missing, rather than scheduling another job that dies hourly in silence. Applied to dev2; the job now resolves to https://crawlproof.com/api/cron/affiliate and the route is reachable and secret-gated (401 without it). Co-Authored-By: Claude Opus 5 (1M context) --- .../20260924230000_fix_affiliate_cron_url.sql | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 supabase/migrations/20260924230000_fix_affiliate_cron_url.sql diff --git a/supabase/migrations/20260924230000_fix_affiliate_cron_url.sql b/supabase/migrations/20260924230000_fix_affiliate_cron_url.sql new file mode 100644 index 00000000..08357d18 --- /dev/null +++ b/supabase/migrations/20260924230000_fix_affiliate_cron_url.sql @@ -0,0 +1,56 @@ +-- Fix the crawlproof-affiliate cron job, which has never once run. +-- +-- 20260913120000_openaffiliate.sql scheduled it with +-- url := current_setting('app.site_url', true) || '/api/cron/affiliate' +-- which is the pattern 20260515090000_cron_config.sql had already removed +-- four months earlier, for exactly the reason its header describes: the +-- app.* GUCs can only be set by supabase_admin, migrations run as `postgres`, +-- so they are never populated. current_setting(..., true) then returns NULL, +-- `NULL || '/api/cron/affiliate'` is NULL, and pg_net rejects the row with +-- null value in column "url" of relation "http_request_queue" +-- violates not-null constraint +-- +-- It fails silently in the sense that matters: `cron.job_run_details` is the +-- only place it shows up, nothing alerts on it, and every other job is green. +-- Found on 2026-09-24 while auditing the ten jobs after the move to dev2 — +-- the Supabase cloud project had been failing this same job hourly since +-- 2026-09-13, so the migration to self-hosting reproduced it rather than +-- causing it. +-- +-- Fixed the same way the 2026-05 migration fixed the others: read the values +-- from public.cron_config. Deliberately NOT by setting the GUCs — a +-- database-level setting is invisible to pg_dump, so it would be lost by the +-- next migration and this would come back a third time. +-- +-- cron.schedule() upserts by job name, so this is idempotent and safe to +-- re-run. + +select cron.schedule( + 'crawlproof-affiliate', + '23 * * * *', + $cron$ + select net.http_post( + url := (select value from public.cron_config where key = 'site_url') || '/api/cron/affiliate', + headers := jsonb_build_object( + 'content-type', 'application/json', + 'x-cron-secret', (select value from public.cron_config where key = 'cron_secret') + ), + body := '{}'::jsonb + ); + $cron$ +); + +-- Guard: fail the migration loudly if cron_config cannot actually build the +-- URL, rather than scheduling another job that dies hourly in silence. +do $$ +declare u text; +begin + select (select value from public.cron_config where key = 'site_url') || '/api/cron/affiliate' into u; + if u is null then + raise exception 'cron_config.site_url is missing — crawlproof-affiliate would post to a NULL url again'; + end if; + if (select value from public.cron_config where key = 'cron_secret') is null then + raise exception 'cron_config.cron_secret is missing — the affiliate route would reject every call with 401'; + end if; + raise notice 'crawlproof-affiliate will post to %', u; +end $$;