← back to Patty
app/api/campaigns/generate/route.ts
70 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { verifyAuth } from '@/lib/auth';
import { callGemini } from '@/lib/gemini';
type CampaignShape = { subject?: string; body_html?: string; body_text?: string };
export async function POST(request: NextRequest) {
const user = verifyAuth(request);
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
try {
const body = await request.json();
const { petition_id, tone, call_to_action_type } = body;
let petitionContext = '';
if (petition_id) {
const petResult = await query(`SELECT title, summary, body_text, target, signature_count, signature_goal FROM petitions WHERE id = $1`, [petition_id]);
if (petResult.rows.length > 0) {
const p = petResult.rows[0];
petitionContext = `
Petition Title: ${p.title}
Petition Summary: ${p.summary || 'N/A'}
Petition Target: ${p.target || 'N/A'}
Current Signatures: ${p.signature_count} / ${p.signature_goal} goal
Petition Body: ${(p.body_text || '').slice(0, 500)}`;
}
}
const toneDesc = tone || 'urgent';
const ctaType = call_to_action_type || 'sign';
const prompt = `You are an expert email copywriter for advocacy campaigns about student debt and education. Generate a compelling campaign email.
${petitionContext ? `PETITION CONTEXT:${petitionContext}` : 'Generate a general student debt advocacy email.'}
Tone: ${toneDesc}
Call to Action: ${ctaType === 'sign' ? 'Sign the petition' : ctaType === 'share' ? 'Share with friends' : ctaType === 'donate' ? 'Support the cause' : 'Take action'}
Return a JSON object with EXACTLY these fields:
{
"subject": "Compelling email subject line (max 80 chars, no clickbait)",
"body_html": "<div style='font-family: sans-serif; max-width: 600px; margin: 0 auto;'>Full HTML email content with inline styles. Include greeting, context, emotional hook, call to action button, and signature. Use <a>, <strong>, <p>, <h2> tags. Include a prominent CTA button styled as: <a href='#sign' style='display:inline-block;padding:12px 32px;background:#7c3aed;color:white;text-decoration:none;border-radius:8px;font-weight:bold;'>Sign Now</a></div>",
"body_text": "Plain text version of the email for non-HTML clients"
}
Make it personal, urgent, and compelling. Reference specific statistics about student debt where relevant.`;
// 2026-05-05 (tick 29): migrated to lib/gemini.ts wrapper.
const r = await callGemini<CampaignShape>({ prompt, maxTokens: 2048, temperature: 0.8 });
if (!r.ok) {
console.error('[campaigns/generate] gemini failed:', r.reason, r.detail);
return NextResponse.json(
{ error: r.reason === 'no_key' ? 'AI service not configured' : 'AI generation failed' },
{ status: r.status },
);
}
const parsed = r.data;
return NextResponse.json({
subject: parsed.subject || '',
body_html: parsed.body_html || '',
body_text: parsed.body_text || '',
});
} catch (err) {
console.error('[api/campaigns/generate] Error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to generate campaign content' }, { status: 500 });
}
}