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 { db } from "@/db/client";4import { reports } from "@/db/schema";5import { assertSameOrigin, currentUser, clientIp } from "@/lib/auth/session";6import { hit } from "@/lib/auth/ratelimit";7import { ipHash } from "@/lib/auth/crypto";8import { audit } from "@/lib/auth/audit";9import { apiError, handle, parseBody } from "@/lib/api";10export const dynamic = "force-dynamic";11const Body = z.object({ targetType: z.enum(["LISTING", "AGGREGATED_LISTING", "USER", "MESSAGE"]), targetId: z.string().min(1).max(200), reason: z.enum(["arnaque", "doublon", "contenu_inapproprie", "informations_fausses", "deja_vendu_loue", "droits_photos", "autre"]), details: z.string().trim().max(1000).optional() });12export async function POST(req: NextRequest) {13 try { await assertSameOrigin(); const b = await parseBody(req, Body); const u = await currentUser(); const iph = ipHash(await clientIp());14 const rl = await hit(`report:${u?.id ?? iph}`, 10, 86400); if (!rl.ok) return apiError(429, "rate_limited", "Trop de signalements aujourd'hui.");15 const [r] = await db().insert(reports).values({ targetType: b.targetType, targetId: b.targetId, reporterId: u?.id ?? null, reason: b.reason, details: b.details ?? null }).returning({ id: reports.id });16 await audit("report.create", { actorId: u?.id ?? null, objectType: b.targetType, objectId: b.targetId, ipHash: iph, meta: { reason: b.reason } });17 return NextResponse.json({ ok: true, id: r.id }, { status: 201 }); } catch (e) { return handle(e); }18}19