diff --git a/src/app/api/submission-receive/route.ts b/src/app/api/submission-receive/route.ts new file mode 100644 index 0000000..521ca8c --- /dev/null +++ b/src/app/api/submission-receive/route.ts @@ -0,0 +1,96 @@ +import { NextResponse, type NextRequest } from 'next/server'; +import { createClient } from '@supabase/supabase-js'; + +export const dynamic = 'force-dynamic'; + +function slugify(input: string): string { + return (input || 'submission') + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .trim() + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .slice(0, 80) || 'submission'; +} + +function randSuffix(): string { + return Math.random().toString(36).slice(2, 8); +} + +export async function POST(request: NextRequest) { + const auth = request.headers.get('authorization') || ''; + const expected = process.env.DISTRIBUTE_SHARED_SECRET; + if (!expected) return NextResponse.json({ error: 'receiver not configured' }, { status: 500 }); + if (!auth.startsWith('Bearer ') || auth.slice(7) !== expected) { + return NextResponse.json({ error: 'unauthorized' }, { status: 401 }); + } + + let body: any; + try { + body = await request.json(); + } catch { + return NextResponse.json({ error: 'invalid json' }, { status: 400 }); + } + + const title = (body?.title || '').toString().trim(); + const content = (body?.body || '').toString(); + if (!title || !content) return NextResponse.json({ error: 'title and body are required' }, { status: 400 }); + + const contributor = body?.contributor || {}; + const featuredImage = body?.featured_image_url || null; + const attachments = Array.isArray(body?.attachments) ? body.attachments : []; + const submissionId = body?.submission_id || null; + const property = body?.property || 'broadcastbeat'; + + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) return NextResponse.json({ error: 'supabase not configured' }, { status: 500 }); + const supabase = createClient(url, key, { auth: { persistSession: false } }); + + const baseSlug = slugify(title); + const slug = `${baseSlug}-${randSuffix()}`; + + const excerpt = content + .replace(/<[^>]+>/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .slice(0, 240); + + const { data: article, error: artErr } = await supabase + .schema('bb') + .from('native_articles') + .insert({ + slug, + title, + content, + excerpt, + author_name: contributor?.contact_name || contributor?.firm_name || 'PR Submission', + category: 'latest', + featured_image: featuredImage, + status: 'pending_pr_rewrite', + }) + .select('id, slug') + .single(); + + if (artErr) return NextResponse.json({ error: artErr.message, where: 'native_articles insert' }, { status: 500 }); + + const { error: subErr } = await supabase + .schema('bb') + .from('original_submissions') + .insert({ + submission_id: submissionId, + property, + title, + body: content, + featured_image_url: featuredImage, + attachments, + contributor, + contributor_email: contributor?.email || null, + raw_payload: body, + native_article_id: article.id, + }); + + if (subErr) return NextResponse.json({ error: subErr.message, where: 'original_submissions insert', article_id: article.id }, { status: 500 }); + + return NextResponse.json({ ok: true, article_id: article.id, slug: article.slug, status: 'pending_pr_rewrite' }, { status: 201 }); +}