import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { assertSameOrigin, requireRole } from "@/lib/auth/session"; import { enroll, newSecret, totpStatus, verify } from "@/lib/admin/mfa"; import { handle, parseBody, apiError } from "@/lib/api"; import { hit } from "@/lib/auth/ratelimit"; export const dynamic = "force-dynamic"; export async function GET() { try { const u = await requireRole("MODERATOR"); const enrolled = await totpStatus(u.id); return NextResponse.json({ enrolled, fresh: !!u.mfaAt && Date.now() - u.mfaAt.getTime() < 30 * 60000, setup: enrolled ? null : newSecret(u.email) }); } catch (e) { return handle(e); } } const Body = z.object({ code: z.string().regex(/^\d{6}$/), base32: z.string().min(16).max(64).optional() }); export async function POST(req: NextRequest) { try { await assertSameOrigin(); const u = await requireRole("MODERATOR"); const b = await parseBody(req, Body); const rl = await hit(`mfa:${u.id}`, 10, 600); if (!rl.ok) return apiError(429, "rate_limited", "Trop de tentatives"); const ok = b.base32 ? await enroll(u.id, b.base32, b.code) && await verify(u, b.code) : await verify(u, b.code); return ok ? NextResponse.json({ ok: true }) : apiError(400, "invalid_code", "Code invalide"); } catch (e) { return handle(e); } }