src/app/api/admin/auth/password/route.ts

route·app·2.3 KB · 83 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 bcrypt from "bcryptjs";
import { isValidCodeFormat, normalizeCode } from "@/lib/admin/code-acces";

export const dynamic = "force-dynamic";

// POST /api/admin/auth/password
// Body: { code: "WARI-XXXX-XXXX", password, confirm }
// Force min 8 chars. Passe à onboardingStep=VITRINE (Sprint 4 prendra la suite).
export async function POST(req: NextRequest) {
  let body: { code?: string; password?: string; confirm?: string };
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Body invalide" }, { status: 400 });
  }

  const code = normalizeCode(body.code ?? "");
  const password = body.password ?? "";
  const confirm = body.confirm ?? "";

  if (!isValidCodeFormat(code)) {
    return NextResponse.json({ error: "Code d'accès invalide" }, { status: 400 });
  }
  if (password.length < 8) {
    return NextResponse.json(
      { error: "Le mot de passe doit contenir au moins 8 caractères." },
      { status: 400 }
    );
  }
  if (password !== confirm) {
    return NextResponse.json(
      { error: "Les deux mots de passe ne correspondent pas." },
      { 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, username: 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 || !primary.emailVerifiedAt || !primary.username) {
    return NextResponse.json(
      {
        error:
          "Complète d'abord l'email et le username avant de définir un mot de passe.",
      },
      { status: 422 }
    );
  }

  const motDePasseHash = await bcrypt.hash(password, 10);

  await prisma.user.update({
    where: { id: primary.id },
    data: { motDePasseHash },
  });
  await prisma.tenant.update({
    where: { id: tenant.id },
    data: { onboardingStep: "VITRINE" },
  });

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