initial commit: rocket.new export of broadcastbeat

This commit is contained in:
Ryan Salazar
2026-05-07 16:39:17 +00:00
commit 70aa1ad46e
302 changed files with 60710 additions and 0 deletions

View File

@@ -0,0 +1,403 @@
-- ============================================================
-- Migration: Ad Ops & Email Marketing System
-- Timestamp: 20260322270000
-- Extends: ad_campaigns (from 20260322260000)
-- Adds: ad_placements, documents, email_lists, email_subscribers,
-- email_campaigns, email_events, email_suppressions,
-- adops_inbox_log, adops_pending_invoices
-- ============================================================
-- ─── 1. Extend ad_campaigns with ad ops fields ───────────────────────────────
ALTER TABLE public.ad_campaigns
ADD COLUMN IF NOT EXISTS campaign_name TEXT,
ADD COLUMN IF NOT EXISTS publication_id TEXT,
ADD COLUMN IF NOT EXISTS placement_zone TEXT,
ADD COLUMN IF NOT EXISTS banner_url TEXT,
ADD COLUMN IF NOT EXISTS destination_url TEXT,
ADD COLUMN IF NOT EXISTS agreed_rate NUMERIC(12,2),
ADD COLUMN IF NOT EXISTS impressions BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS clicks BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS ctr NUMERIC(6,4) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS created_by TEXT,
ADD COLUMN IF NOT EXISTS insertion_order_file TEXT,
ADD COLUMN IF NOT EXISTS invoice_id TEXT,
ADD COLUMN IF NOT EXISTS blackmagic_excluded BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS mooninvoice_invoice_id TEXT,
ADD COLUMN IF NOT EXISTS mooninvoice_contact_id TEXT,
ADD COLUMN IF NOT EXISTS approval_screenshot_url TEXT,
ADD COLUMN IF NOT EXISTS live_screenshot_url TEXT,
ADD COLUMN IF NOT EXISTS approved_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS approved_by TEXT,
ADD COLUMN IF NOT EXISTS deployed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS deactivated_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS io_waived BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS pending_info_fields JSONB;
-- Update status check to include ad ops statuses
ALTER TABLE public.ad_campaigns
DROP CONSTRAINT IF EXISTS ad_campaigns_status_check;
ALTER TABLE public.ad_campaigns
ADD CONSTRAINT ad_campaigns_status_check
CHECK (status IN (
'draft','pending_info','pending_approval','approved',
'active','paused','ended','cancelled','completed'
));
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_publication ON public.ad_campaigns(publication_id);
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_start_date ON public.ad_campaigns(start_date);
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_end_date ON public.ad_campaigns(end_date);
-- ─── 2. ad_placements ────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.ad_placements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID REFERENCES public.ad_campaigns(id) ON DELETE CASCADE,
publication_id TEXT NOT NULL,
zone_id TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT false,
deployed_at TIMESTAMPTZ,
deactivated_at TIMESTAMPTZ,
screenshot_url TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_ad_placements_campaign_id ON public.ad_placements(campaign_id);
CREATE INDEX IF NOT EXISTS idx_ad_placements_active ON public.ad_placements(is_active);
CREATE INDEX IF NOT EXISTS idx_ad_placements_publication ON public.ad_placements(publication_id);
ALTER TABLE public.ad_placements ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_ad_placements" ON public.ad_placements;
CREATE POLICY "admin_manage_ad_placements"
ON public.ad_placements FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 3. documents ────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.adops_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID REFERENCES public.ad_campaigns(id) ON DELETE SET NULL,
email_campaign_id UUID,
document_type TEXT NOT NULL DEFAULT 'io' CHECK (document_type IN ('io','invoice','contract','other')),
file_url TEXT NOT NULL,
file_name TEXT,
uploaded_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
uploaded_by TEXT,
notes TEXT
);
CREATE INDEX IF NOT EXISTS idx_adops_documents_campaign_id ON public.adops_documents(campaign_id);
ALTER TABLE public.adops_documents ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_adops_documents" ON public.adops_documents;
CREATE POLICY "admin_manage_adops_documents"
ON public.adops_documents FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 4. email_lists ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.email_lists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
list_name TEXT NOT NULL,
publication_id TEXT NOT NULL,
subscriber_count INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
last_updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_email_lists_publication ON public.email_lists(publication_id);
ALTER TABLE public.email_lists ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_email_lists" ON public.email_lists;
CREATE POLICY "admin_manage_email_lists"
ON public.email_lists FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 5. email_subscribers ────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.email_subscribers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
list_id UUID REFERENCES public.email_lists(id) ON DELETE CASCADE,
email TEXT NOT NULL,
first_name TEXT,
last_name TEXT,
subscribed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','unsubscribed','bounced','complained')),
source TEXT,
last_opened TIMESTAMPTZ,
last_clicked TIMESTAMPTZ,
soft_bounce_count INTEGER NOT NULL DEFAULT 0,
UNIQUE(list_id, email)
);
CREATE INDEX IF NOT EXISTS idx_email_subscribers_list_id ON public.email_subscribers(list_id);
CREATE INDEX IF NOT EXISTS idx_email_subscribers_email ON public.email_subscribers(email);
CREATE INDEX IF NOT EXISTS idx_email_subscribers_status ON public.email_subscribers(status);
ALTER TABLE public.email_subscribers ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_email_subscribers" ON public.email_subscribers;
CREATE POLICY "admin_manage_email_subscribers"
ON public.email_subscribers FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 6. email_campaigns (distributions) ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.adops_email_campaigns (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_name TEXT NOT NULL,
client_name TEXT,
publication_id TEXT NOT NULL,
list_id UUID REFERENCES public.email_lists(id) ON DELETE SET NULL,
subject TEXT NOT NULL,
from_name TEXT NOT NULL,
from_email TEXT NOT NULL,
reply_to TEXT,
html_content TEXT,
send_date DATE,
send_time_et TEXT DEFAULT '11:00',
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN (
'draft','pending_info','pending_test','test_sent','pending_approval',
'approved','scheduled','sending','sent','cancelled','held'
)),
approved_by TEXT,
approved_at TIMESTAMPTZ,
sent_count INTEGER NOT NULL DEFAULT 0,
test_sent_at TIMESTAMPTZ,
insertion_order_file TEXT,
invoice_id TEXT,
agreed_rate NUMERIC(12,2),
mooninvoice_invoice_id TEXT,
is_newsletter BOOLEAN NOT NULL DEFAULT false,
newsletter_issue_number INTEGER,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_adops_email_campaigns_publication ON public.adops_email_campaigns(publication_id);
CREATE INDEX IF NOT EXISTS idx_adops_email_campaigns_status ON public.adops_email_campaigns(status);
CREATE INDEX IF NOT EXISTS idx_adops_email_campaigns_send_date ON public.adops_email_campaigns(send_date);
ALTER TABLE public.adops_email_campaigns ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_adops_email_campaigns" ON public.adops_email_campaigns;
CREATE POLICY "admin_manage_adops_email_campaigns"
ON public.adops_email_campaigns FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 7. email_events ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.email_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID REFERENCES public.adops_email_campaigns(id) ON DELETE CASCADE,
subscriber_id UUID REFERENCES public.email_subscribers(id) ON DELETE SET NULL,
event_type TEXT NOT NULL CHECK (event_type IN ('delivered','open','click','bounce','unsubscribe','complaint','failed')),
event_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
link_url TEXT,
ip_hash TEXT,
user_agent TEXT,
bounce_type TEXT
);
CREATE INDEX IF NOT EXISTS idx_email_events_campaign_id ON public.email_events(campaign_id);
CREATE INDEX IF NOT EXISTS idx_email_events_subscriber_id ON public.email_events(subscriber_id);
CREATE INDEX IF NOT EXISTS idx_email_events_type ON public.email_events(event_type);
CREATE INDEX IF NOT EXISTS idx_email_events_timestamp ON public.email_events(event_timestamp);
ALTER TABLE public.email_events ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_email_events" ON public.email_events;
CREATE POLICY "admin_manage_email_events"
ON public.email_events FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 8. email_suppressions ───────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.email_suppressions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL,
reason TEXT NOT NULL CHECK (reason IN ('unsubscribe','hard_bounce','complaint','manual')),
suppressed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
publication_id TEXT,
UNIQUE(email, publication_id)
);
CREATE INDEX IF NOT EXISTS idx_email_suppressions_email ON public.email_suppressions(email);
CREATE INDEX IF NOT EXISTS idx_email_suppressions_publication ON public.email_suppressions(publication_id);
ALTER TABLE public.email_suppressions ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_email_suppressions" ON public.email_suppressions;
CREATE POLICY "admin_manage_email_suppressions"
ON public.email_suppressions FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 9. adops_inbox_log ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.adops_inbox_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message_id TEXT UNIQUE,
from_email TEXT NOT NULL,
subject TEXT,
received_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
request_type TEXT CHECK (request_type IN ('banner','distribution','newsletter','inquiry','unknown','ignored')),
parsed_data JSONB,
action_taken TEXT,
campaign_id UUID REFERENCES public.ad_campaigns(id) ON DELETE SET NULL,
email_campaign_id UUID REFERENCES public.adops_email_campaigns(id) ON DELETE SET NULL,
thread_id TEXT,
is_whitelisted BOOLEAN NOT NULL DEFAULT false,
processing_status TEXT NOT NULL DEFAULT 'received' CHECK (processing_status IN (
'received','processing','replied','completed','error','ignored'
)),
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_adops_inbox_log_from_email ON public.adops_inbox_log(from_email);
CREATE INDEX IF NOT EXISTS idx_adops_inbox_log_received_at ON public.adops_inbox_log(received_at);
CREATE INDEX IF NOT EXISTS idx_adops_inbox_log_status ON public.adops_inbox_log(processing_status);
ALTER TABLE public.adops_inbox_log ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_adops_inbox_log" ON public.adops_inbox_log;
CREATE POLICY "admin_manage_adops_inbox_log"
ON public.adops_inbox_log FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 10. adops_pending_invoices ──────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.adops_pending_invoices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID REFERENCES public.ad_campaigns(id) ON DELETE CASCADE,
client_name TEXT NOT NULL,
client_email TEXT,
mooninvoice_contact_id TEXT,
invoice_data JSONB NOT NULL,
retry_count INTEGER NOT NULL DEFAULT 0,
last_retry_at TIMESTAMPTZ,
next_retry_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','retrying','created','failed')),
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_adops_pending_invoices_status ON public.adops_pending_invoices(status);
CREATE INDEX IF NOT EXISTS idx_adops_pending_invoices_next_retry ON public.adops_pending_invoices(next_retry_at);
ALTER TABLE public.adops_pending_invoices ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_adops_pending_invoices" ON public.adops_pending_invoices;
CREATE POLICY "admin_manage_adops_pending_invoices"
ON public.adops_pending_invoices FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM public.user_profiles up
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
)
);
-- ─── 11. Seed default email lists ────────────────────────────────────────────
DO $$
BEGIN
INSERT INTO public.email_lists (list_name, publication_id, is_active)
VALUES
('Broadcast Beat Full List', 'broadcast-beat', true),
('AV Beat Full List', 'av-beat', true),
('Backlot Beat Full List', 'backlot-beat', true)
ON CONFLICT DO NOTHING;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Email list seed failed: %', SQLERRM;
END $$;