SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
5 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
1.3 KB · 11 lines typescript
Raw Blame History
1import { NextRequest, NextResponse } from "next/server";2import { z } from "zod";3import { assertSameOrigin, requireRole } from "@/lib/auth/session";4import { enroll, newSecret, totpStatus, verify } from "@/lib/admin/mfa";5import { handle, parseBody, apiError } from "@/lib/api";6import { hit } from "@/lib/auth/ratelimit";7export const dynamic = "force-dynamic";8export 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); } }9const Body = z.object({ code: z.string().regex(/^\d{6}$/), base32: z.string().min(16).max(64).optional() });10export 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); } }11