-- Migration: Create sponsored_posts table for First-Party Contextual Ads -- Created: 2026-01-12 -- Purpose: Silent launch infrastructure for sponsored content (table starts empty) create table if not exists sponsored_posts ( id uuid default gen_random_uuid() primary key, created_at timestamptz default now(), advertiser_name text not null, body text not null, -- Markdown content image_url text, -- Optional banner cta_link text not null, cta_text text default 'Learn More', target_categories text[] not null, -- Array of Category IDs to match against active boolean default true, -- Internal tracking (private) impression_goal int default 1000, current_impressions int default 0 ); -- RLS: Public Read-Only (authenticated), Service Role Write-Only alter table sponsored_posts enable row level security; create policy "Users can read active ads" on sponsored_posts for select to authenticated using (active = true); -- Index for efficient category matching queries create index if not exists idx_sponsored_posts_categories on sponsored_posts using gin (target_categories); -- Index for active ads filtering create index if not exists idx_sponsored_posts_active on sponsored_posts (active) where active = true; -- Function to increment ad impressions (called from client) create or replace function increment_ad_impression(p_ad_id uuid) returns void as $$ begin update sponsored_posts set current_impressions = current_impressions + 1 where id = p_ad_id; end; $$ language plpgsql security definer;