← 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 });
  }
}