← back to Patty

app/api/petitions/generate/route.ts

76 lines

import { NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '@/lib/auth';
import { callGemini } from '@/lib/gemini';

type PetitionShape = {
  title?: string;
  summary?: string;
  body_html?: string;
  body_text?: string;
  suggested_target?: string;
  suggested_tags?: string[];
  suggested_goal?: number;
};

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 { topic, target, tone, category } = body;

    if (!topic) {
      return NextResponse.json({ error: 'Topic is required' }, { status: 400 });
    }

    const toneDesc = tone || 'formal';
    const targetDesc = target || 'Congress and the Department of Education';
    const categoryDesc = category || 'policy';

    const prompt = `You are a professional petition writer specializing in student debt and education advocacy. Generate a compelling petition about the following topic.

Topic: ${topic}
Target: ${targetDesc}
Tone: ${toneDesc} (${toneDesc === 'urgent' ? 'time-sensitive, action-oriented language' : toneDesc === 'formal' ? 'professional, respectful, data-driven' : toneDesc === 'passionate' ? 'emotional, personal stories, empathetic' : 'community-focused, grassroots, collective action'})
Category: ${categoryDesc}

Return a JSON object with EXACTLY these fields:
{
  "title": "A compelling petition title (max 100 chars)",
  "summary": "A 2-3 sentence summary that hooks readers (max 300 chars)",
  "body_html": "<p>Full petition body in HTML format with multiple paragraphs. Include specific demands, statistics where appropriate, and a clear call to action. Should be 3-5 paragraphs long. Use <strong>, <em>, <ul>, <li> tags for emphasis and lists.</p>",
  "body_text": "Plain text version of the petition body",
  "suggested_target": "The specific person, office, or body to address",
  "suggested_tags": ["array", "of", "relevant", "tags"],
  "suggested_goal": 500
}

Make the petition specific, actionable, and compelling. Focus on student debt, education policy, and higher education issues. The suggested_goal should be between 100 and 10000 based on the scope of the petition.`;

    // 2026-05-05 (tick 29): migrated to lib/gemini.ts wrapper.
    const r = await callGemini<PetitionShape>({ prompt, maxTokens: 2048, temperature: 0.8 });
    if (!r.ok) {
      console.error('[petitions/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({
      title: parsed.title || '',
      summary: parsed.summary || '',
      body_html: parsed.body_html || '',
      body_text: parsed.body_text || '',
      suggested_target: parsed.suggested_target || targetDesc,
      suggested_tags: parsed.suggested_tags || [],
      suggested_goal: parsed.suggested_goal || 500,
    });
  } catch (err) {
    console.error('[api/petitions/generate] Error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to generate petition' }, { status: 500 });
  }
}