import { NextResponse } from "next/server"; import { z } from "zod"; import { UserRole } from "@/generated/prisma/enums"; import { hashPassword } from "@/lib/password"; import { prisma } from "@/lib/prisma"; import { recordAudit } from "@/lib/admin/audit"; import { sendSignupWelcome } from "@/lib/email"; import { rateLimitRequest } from "@/lib/rate-limit"; export const runtime = "nodejs"; const schema = z.object({ email: z.string().trim().toLowerCase().email().max(200), password: z.string().min(8).max(200), firstName: z.string().trim().min(1).max(100), lastName: z.string().trim().min(1).max(100), phone: z.string().trim().max(40).optional().nullable(), role: z.enum([UserRole.TOURIST, UserRole.OWNER]).default(UserRole.TOURIST), }); export async function POST(req: Request) { // 5 inscriptions max par IP par heure. const rl = rateLimitRequest(req, "signup", 60 * 60 * 1000, 5); if (!rl.ok) { return NextResponse.json( { error: `Trop de tentatives. Réessayez dans ${rl.retryAfter}s.` }, { status: 429, headers: { "Retry-After": String(rl.retryAfter) } }, ); } let body: unknown; try { body = await req.json(); } catch { return NextResponse.json({ error: "Corps JSON invalide." }, { status: 400 }); } const parsed = schema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(" · ") }, { status: 400 }, ); } const data = parsed.data; const existing = await prisma.user.findUnique({ where: { email: data.email }, select: { id: true } }); if (existing) { return NextResponse.json({ error: "Un compte existe déjà avec cet email." }, { status: 409 }); } const passwordHash = await hashPassword(data.password); const user = await prisma.user.create({ data: { email: data.email, passwordHash, firstName: data.firstName, lastName: data.lastName, phone: data.phone?.trim() || null, role: data.role, isActive: true, }, select: { id: true, email: true, role: true }, }); await recordAudit({ scope: "public.signup", event: "user.create", target: user.id, actorEmail: user.email, details: { role: user.role }, }); // Best-effort welcome email. sendSignupWelcome(user.email, data.firstName).catch(() => {}); return NextResponse.json({ ok: true, userId: user.id }); }