src/app/api/auth/client/verify/route.ts

route·app·4.1 KB · 102 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.

1 export

GET

Code source· typescript

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma/client";
import { createHash } from "crypto";
import { createSession } from "@/lib/auth/session";
import { fusionnerPanierEtWishlist } from "@/lib/panier/fusion";


export async function GET(req: NextRequest) {
  try {
    const rawToken = req.nextUrl.searchParams.get("token");
    const source = req.nextUrl.searchParams.get("source");
    if (!rawToken) {
      return NextResponse.json({ error: "Token manquant" }, { status: 400 });
    }
    const hashedToken = createHash("sha256").update(rawToken).digest("hex");
    const magicToken = await prisma.magicToken.findUnique({
      where: { token: hashedToken },
      include: { client: true },
    });
    if (!magicToken) {
      return NextResponse.json({ error: "Token invalide" }, { status: 401 });
    }
    if (magicToken.usedAt) {
      return NextResponse.json({ error: "Token deja utilise" }, { status: 401 });
    }
    if (new Date() > magicToken.expiresAt) {
      return NextResponse.json({ error: "Token expire" }, { status: 401 });
    }

    // Source mobile — ne PAS consommer le token ici.
    // Le token sera consommé par POST /api/mobile/auth côté app native.
    // Redirige vers le deep link wari:// avec token + identifier.
    if (source === "mobile") {
      const identifier = encodeURIComponent(magicToken.identifier);
      return NextResponse.redirect(
        `wari://auth/verify?token=${rawToken}&identifier=${identifier}`
      );
    }

    const isNewClient = !magicToken.client;
    let client = magicToken.client;
    if (!client) {
      client = await prisma.clientAccount.create({
        data: {
          phone: magicToken.identifierType === "phone" ? magicToken.identifier : null,
          email: magicToken.identifierType === "email" ? magicToken.identifier : null,
          originTenantId: magicToken.tenantId === "wari" ? null : magicToken.tenantId,
        },
      });
    }
    await prisma.magicToken.update({
      where: { id: magicToken.id },
      data: { usedAt: new Date(), clientId: client.id },
    });
    await prisma.clientAccount.update({
      where: { id: client.id },
      data: { lastSeenAt: new Date() },
    });

    // Fusion panier/wishlist anonyme → compte client
    const sessionId = req.cookies.get("panier_session")?.value;
    if (sessionId && magicToken.tenantId !== "wari") {
      await fusionnerPanierEtWishlist(sessionId, client.id, magicToken.tenantId);
    }

    await createSession(client.id, "CLIENT", magicToken.tenantId);

    const baseUrl = process.env.MAGIC_LINK_BASE_URL || "http://localhost:3000";
    const tenantId = magicToken.tenantId;
    const newEmail = req.nextUrl.searchParams.get("newEmail");
    const invitationToken = req.nextUrl.searchParams.get("invitationToken");

    // Vérification email
    if (source === "verify-email") {
      await prisma.clientAccount.update({
        where: { id: client.id },
        data: { emailVerifiedAt: new Date() },
      });
    }

    // Changement email
    if (source === "change-email" && newEmail) {
      await prisma.clientAccount.update({
        where: { id: client.id },
        data: { email: newEmail, emailVerifiedAt: new Date() },
      });
    }

    const tenantBaseUrl2 = tenantId === "wari" ? baseUrl : baseUrl.replace("https://", "https://" + tenantId + ".");
    if (isNewClient) return NextResponse.redirect(tenantBaseUrl2 + "/auth/client/profil" + (tenantId === "wari" ? "?tenant=wari" : "?tenant=" + tenantId));
    if (source === "reset") return NextResponse.redirect(tenantBaseUrl2 + "/auth/client/nouveau-mdp" + (tenantId === "wari" ? "?tenant=wari" : ""));
    if (source === "verify-email" || source === "change-email") return NextResponse.redirect(tenantBaseUrl2 + "/compte?verified=1");
    if (source === "invitation" && invitationToken) {
      return NextResponse.redirect(tenantBaseUrl2 + "/invitation/" + tenantId + "?token=" + invitationToken);
    }
    return NextResponse.redirect(tenantBaseUrl2);
  } catch (error) {
    console.error("verify error:", error);
    return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
  }
}