feat(migrations): create apify_scraper_runs ownership map (chat#1840)
Part of recoupable/chat#1840 item 1 (account-scope the scrape-results endpoint). Hard dependency: this migration must merge + apply before the api PR that reads/writes the table. Contract: docs#262.
What
public.apify_scraper_runs ā ownership map written at scrape start by POST /api/socials/{id}/scrape, read by the GET /api/apify/runs/{runId} validator:
| column | type | notes |
|---|---|---|
run_id | TEXT PK | Apify run id ā natural key, one owner per run |
account_id | UUID NOT NULL | account whose key started the run |
social_id | UUID | scraped social profile (audit, denormalized) |
created_at | timestamptz default now() |
- Loose ids, no FKs ā same rationale as
email_send_log: an ownership log shouldn't cascade-delete with accounts; a dangling row just degrades that run to admin-only polling. - Index on
account_idfor audits; reads are by PK. - RLS enabled, no policies (service-role access only), matching neighboring service-written tables.
Why
Apify run ids carry no account scope, so the results endpoint is admin-only ā agents can start scrapes they can never read (7/7 Forbidden polls in the issue's evidence). This table gives the endpoint an ownership chain to authorize against.
š¤ Generated with Claude Code
Summary by cubic
Create public.apify_scraper_runs to map Apify run IDs to the owning account, enabling account-scoped access for GET /api/apify/runs/{runId} (chat#1840). Must deploy before the API that reads/writes this table.
- Migration
- New table:
run_idTEXT PK,account_idUUID NOT NULL,social_idUUID,created_attimestamptz default now(). - Index on
account_id; reads by PK. - RLS enabled with no policies (service-role only). No FKs to avoid cascade deletes.
- New table: