src/app/api/mobile/catalogue/route.ts

route·app·4.5 KB · 130 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 { filtreTenantsAccessibles, getConfigEffective } from "@/lib/acces-vitrine";
import { getSessionFromRequest } from "@/lib/auth/session";

export async function GET(req: NextRequest) {
  try {
    const session = await getSessionFromRequest(req);
    const clientId = session?.role === "CLIENT" ? session.userId : null;

    const { searchParams } = req.nextUrl;
    const tenantId = searchParams.get("tenantId");
    const page = Math.max(1, parseInt(searchParams.get("page") || "1"));
    const limit = Math.min(50, parseInt(searchParams.get("limit") || "20"));
    const categorie = searchParams.get("categorie");
    const q = searchParams.get("q");
    const tri = searchParams.get("tri"); // newest | prix_asc | prix_desc
    const prixMin = searchParams.get("prixMin");
    const prixMax = searchParams.get("prixMax");
    const dispo = searchParams.get("dispo"); // disponible | sur_commande
    const tagParam = searchParams.get("tag"); // slug1,slug2
    const tagMode = (searchParams.get("tagMode") || "and").toLowerCase(); // and | or
    const tagSlugs = tagParam
      ? tagParam.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
      : [];
    const skip = (page - 1) * limit;

    const where: any = {
      disponible: true,
      statut: dispo === "disponible"
        ? "DISPONIBLE"
        : dispo === "sur_commande"
        ? "SUR_COMMANDE"
        : { in: ["DISPONIBLE", "SUR_COMMANDE"] },
      tenant: { actif: true, vitrineActive: true, ...filtreTenantsAccessibles(clientId) },
    };

    if (tenantId) {
      const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tenantId);
      if (isUuid) {
        where.tenantId = tenantId;
      } else {
        where.tenant = { ...where.tenant, subdomain: tenantId };
      }
    }
    if (q) where.OR = [
      { nom: { contains: q, mode: "insensitive" } },
      { tags: { has: q } },
      { marque: { contains: q, mode: "insensitive" } },
    ];
    if (categorie) where.categories = { some: { categorieId: categorie } };
    if (tagSlugs.length) {
      if (tagMode === "or") {
        where.tagsLinks = { some: { tag: { slug: { in: tagSlugs } } } };
      } else {
        where.AND = tagSlugs.map((slug) => ({
          tagsLinks: { some: { tag: { slug } } },
        }));
      }
    }
    if (prixMin || prixMax) {
      where.prix = {};
      if (prixMin) where.prix.gte = parseFloat(prixMin);
      if (prixMax) where.prix.lte = parseFloat(prixMax);
    }

    const orderBy =
      tri === "prix_asc"  ? [{ prix: "asc"  as const }, { id: "asc" as const }] :
      tri === "prix_desc" ? [{ prix: "desc" as const }, { id: "asc" as const }] :
                            [{ createdAt: "desc" as const }, { id: "asc" as const }];

    const [produits, total] = await Promise.all([
      prisma.produit.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        include: {
          medias: { orderBy: { ordre: "asc" }, select: { url: true, ordre: true, type: true } },
          categories: {
            include: { categorie: { select: { id: true, nom: true } } },
          },
          tagsLinks: {
            include: { tag: { select: { id: true, nom: true, slug: true, couleur: true } } },
          },
          tenant: {
            select: {
              id: true,
              subdomain: true,
              nom: true,
              logoUrl: true,
              whatsapp: true,
              niveauAcces: true,
              configAcces: true,
            },
          },
        },
      }),
      prisma.produit.count({ where }),
    ]);

    return NextResponse.json({
      produits: produits.map((p) => ({
        id: p.id,
        nom: p.nom,
        description: p.description,
        prix: p.prix,
        ancienPrix: p.ancienPrix,
        devise: p.devise,
        stock: p.stock,
        disponible: p.disponible,
        statut: p.statut,
        marque: p.marque,
        tags: p.tags,
        videoUrl: p.videoUrl,
        medias: p.medias,
        categories: p.categories.map((c) => c.categorie),
        tagsList: p.tagsLinks.map((tl) => tl.tag),
        tenant: { ...p.tenant, configAcces: getConfigEffective(p.tenant.configAcces) },
      })),
      total,
      page,
      totalPages: Math.ceil(total / limit),
    });
  } catch (error) {
    console.error("mobile/catalogue error:", error);
    return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
  }
}