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