TypeScript 50.6%
JavaScript 46.1%
SQL 2.1%
Shell 0.6%
CSS 0.5%
1import { NextRequest, NextResponse } from "next/server";2import { z } from "zod";3import { askAssistant } from "@/lib/assistant";4import { dbAvailable } from "@/lib/db";5export const dynamic = "force-dynamic";6export const maxDuration = 60;7const Body = z.object({ messages: z.array(z.object({ role: z.enum(["user", "assistant"]), content: z.string().min(1).max(4000) })).min(1).max(30), context: z.object({ uid: z.string().max(200).optional() }).optional() });8// Limitation simple par IP (anti-abus) : 30 requêtes / 10 min, en mémoire du processus.9const hits = new Map<string, number[]>();10function limited(ip: string) { const now = Date.now(); const arr = (hits.get(ip) ?? []).filter((t) => now - t < 600000); arr.push(now); hits.set(ip, arr); return arr.length > 30; }11export async function POST(req: NextRequest) {12 const ip = req.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? "local";13 if (limited(ip)) return NextResponse.json({ error: "Trop de requêtes, réessayez dans quelques minutes." }, { status: 429 });14 if (!dbAvailable()) return NextResponse.json({ error: "Données indisponibles" }, { status: 503 });15 const parsed = Body.safeParse(await req.json().catch(() => null));16 if (!parsed.success) return NextResponse.json({ error: "Requête invalide" }, { status: 400 });17 try { return NextResponse.json(await askAssistant(parsed.data.messages, parsed.data.context)); }18 catch (e) { console.error("[assistant]", (e as Error).message); return NextResponse.json({ error: "L'assistant est momentanément indisponible." }, { status: 502 }); }19}20