src/app/api/admin/auth/verify-email/route.ts

route·app·2.6 KB · 89 lignes· Voir l'itinéraire
Annotation non disponible

Lance npm run annotate (nécessite ANTHROPIC_API_KEY dans .env.local) pour générer une annotation française par Claude Haiku 4.5.

2 exports

POSTdynamic

Code source· typescript

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma/client";
import { isValidCodeFormat, normalizeCode } from "@/lib/admin/code-acces";

export const dynamic = "force-dynamic";

// POST /api/admin/auth/verify-email
// Body: { code: "WARI-XXXX-XXXX", verifCode: "123456" }
// Vérifie le code 6 chiffres reçu par email, marque emailVerifiedAt,
// passe à onboardingStep=USERNAME.
export async function POST(req: NextRequest) {
  let body: { code?: string; verifCode?: string };
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Body invalide" }, { status: 400 });
  }

  const code = normalizeCode(body.code ?? "");
  const verifCode = (body.verifCode ?? "").trim();

  if (!isValidCodeFormat(code)) {
    return NextResponse.json({ error: "Code d'accès invalide" }, { status: 400 });
  }
  if (!/^\d{6}$/.test(verifCode)) {
    return NextResponse.json({ error: "Code à 6 chiffres requis" }, { status: 400 });
  }

  const tenant = await prisma.tenant.findUnique({
    where: { codeAcces: code },
    select: {
      id: true,
      onboardingStep: true,
      actif: true,
      users: {
        where: { isPrimary: true },
        select: { id: true, resetToken: true, resetTokenExpiry: true, emailVerifiedAt: true },
        take: 1,
      },
    },
  });
  if (!tenant) {
    return NextResponse.json({ error: "Code invalide" }, { status: 404 });
  }
  if (!tenant.actif || tenant.onboardingStep === "DONE") {
    return NextResponse.json({ error: "Setup non disponible" }, { status: 422 });
  }

  const primary = tenant.users[0];
  if (!primary) {
    return NextResponse.json(
      { error: "Aucun email saisi pour ce tenant. Recommence l'étape précédente." },
      { status: 422 }
    );
  }

  if (!primary.resetToken || !primary.resetTokenExpiry) {
    return NextResponse.json(
      { error: "Aucun code en attente. Demande un nouveau code." },
      { status: 422 }
    );
  }
  if (new Date(primary.resetTokenExpiry) < new Date()) {
    return NextResponse.json(
      { error: "Code expiré. Demande un nouveau code." },
      { status: 410 }
    );
  }
  if (primary.resetToken !== verifCode) {
    return NextResponse.json({ error: "Code incorrect." }, { status: 401 });
  }

  // OK : marque verified + nettoie le code
  await prisma.user.update({
    where: { id: primary.id },
    data: {
      emailVerifiedAt: new Date(),
      resetToken: null,
      resetTokenExpiry: null,
    },
  });
  await prisma.tenant.update({
    where: { id: tenant.id },
    data: { onboardingStep: "USERNAME" },
  });

  return NextResponse.json({ ok: true });
}