Files
local-cal/src/app/api/ai-summary/route.ts
Dmytro Stanchiev e1fd7dc5a3 feat(api): add input validation to AI endpoints
Add prompt validation to ai-event (non-empty string, max 2000 chars)
and events array length validation to ai-summary (max 100 items)
to prevent abuse and injection attacks.
2026-04-06 23:24:15 -04:00

65 lines
1.6 KiB
TypeScript

import { NextResponse } from "next/server";
import { auth } from "@/auth";
import { headers } from "next/headers";
export async function POST(request: Request) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
return NextResponse.json(
{ error: "Authentication required" },
{ status: 401 },
);
}
try {
const { events } = await request.json();
if (!events || !Array.isArray(events)) {
return NextResponse.json(
{ error: "Invalid events array" },
{ status: 400 },
);
}
if (events.length > 100) {
return NextResponse.json(
{ error: "Events array must contain 100 or fewer items" },
{ status: 400 },
);
}
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`, // Server-side only
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "@preset/i-cal-editor-summarize", // FREE model
messages: [
{
role: "system",
content: `You summarize a list of events in natural language. Include date, time, and title. Be concise.`,
},
{ role: "user", content: JSON.stringify(events) },
],
temperature: 0.4,
// max_tokens: 300,
}),
});
const data = await res.json();
const summary =
data?.choices?.[0]?.message?.content || "No summary generated.";
return NextResponse.json({ summary });
} catch (error) {
console.error(error);
return NextResponse.json(
{ error: "Failed to summarize events" },
{ status: 500 },
);
}
}