From c63f2dbf859a50d68bba25f7dd4e865d52f20aad Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Fri, 3 Jul 2026 13:50:50 -0500 Subject: [PATCH] feat(migrations): create apify_scraper_runs ownership map (chat#1840) Records which account started each Apify scraper run at scrape start, so GET /api/apify/runs/{runId} can authorize the owning account instead of being admin-only. Loose ids/no FKs like email_send_log; RLS enabled with no policies (service-role writes only). Co-Authored-By: Claude Fable 5 --- ...260703120000_create_apify_scraper_runs.sql | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 supabase/migrations/20260703120000_create_apify_scraper_runs.sql diff --git a/supabase/migrations/20260703120000_create_apify_scraper_runs.sql b/supabase/migrations/20260703120000_create_apify_scraper_runs.sql new file mode 100644 index 0000000..d2a67a9 --- /dev/null +++ b/supabase/migrations/20260703120000_create_apify_scraper_runs.sql @@ -0,0 +1,29 @@ +-- Ownership map for Apify scraper runs (recoupable/chat#1840). +-- +-- GET /api/apify/runs/{runId} is admin-only because Apify run identifiers +-- carry no account scope — the endpoint has nothing to authorize against, so +-- account-key agents that start a scrape via POST /api/socials/{id}/scrape +-- get Forbidden polling their own run. This records who started each run at +-- start time, giving the results endpoint an ownership chain: owning account +-- OR admin may poll. Written by the api scrape-start handler; read by the +-- results validator. +-- +-- Loose ids, no FKs (like email_send_log / error_logs — an ownership log +-- shouldn't cascade-delete with accounts, and a dangling row just means the +-- run degrades to admin-only). run_id is the natural key: one owner per run. +-- social_id is denormalized for audit ("which profile was scraped"). +-- +-- RLS: enabled with no policies, matching neighboring service-written tables. +-- The api reads/writes via the service role (bypasses RLS); anon/authenticated +-- have no access. + +CREATE TABLE IF NOT EXISTS public.apify_scraper_runs ( + run_id TEXT PRIMARY KEY, + account_id UUID NOT NULL, -- account whose key started the run + social_id UUID, -- social profile the run scrapes (audit) + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS apify_scraper_runs_account_id_idx ON public.apify_scraper_runs (account_id); + +ALTER TABLE public.apify_scraper_runs ENABLE ROW LEVEL SECURITY;