src/app/api/admin/auth/upload-logo/route.ts
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.
Concepts détectés — comprends la théorie
Route API Next.js
3 occurrencesCe fichier est une route API Next.js (App Router). Voir le contrat API complet pour les conventions de réponse et d'auth.
Voir l'article général
ORM Prisma
2 occurrencesCe fichier accède à la base de données via Prisma. Prisma est l'ORM utilisé côté backend pour les requêtes typées sur PostgreSQL.
Voir l'article général
2 exports
POSTdynamic
Code source· typescript
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma/client";
import { uploadFile } from "@/lib/minio/client";
import { isValidCodeFormat, normalizeCode } from "@/lib/admin/code-acces";
export const dynamic = "force-dynamic";
const MAX_SIZE_BYTES = 5 * 1024 * 1024;
const ALLOWED_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/svg+xml"];
// POST /api/admin/auth/upload-logo
// Multipart : file=<logo>, code=WARI-XXXX-XXXX
// Upload MinIO + retourne logoUrl (à passer ensuite à /vitrine).
// NB: ne met PAS à jour Tenant.logoUrl directement — c'est /vitrine qui le fait.
export async function POST(req: NextRequest) {
const form = await req.formData();
const code = normalizeCode((form.get("code") as string) ?? "");
const file = form.get("file") as File | null;
if (!isValidCodeFormat(code)) {
return NextResponse.json({ error: "Code d'accès invalide" }, { status: 400 });
}
if (!file) {
return NextResponse.json({ error: "Fichier manquant" }, { status: 400 });
}
if (file.size > MAX_SIZE_BYTES) {
return NextResponse.json(
{ error: `Logo trop volumineux (max ${MAX_SIZE_BYTES / 1024 / 1024} Mo)` },
{ status: 413 }
);
}
if (!ALLOWED_TYPES.includes(file.type)) {
return NextResponse.json(
{ error: "Format non supporté (utilise PNG, JPG, WebP ou SVG)" },
{ status: 415 }
);
}
const tenant = await prisma.tenant.findUnique({
where: { codeAcces: code },
select: { id: true, actif: true, onboardingStep: true },
});
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 ext = file.name.split(".").pop() ?? "png";
const key = `setup/${tenant.id}/logo-${Date.now()}.${ext.toLowerCase()}`;
const buffer = Buffer.from(await file.arrayBuffer());
const url = await uploadFile(buffer, key, file.type);
return NextResponse.json({ url });
}
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma/client";
import { uploadFile } from "@/lib/minio/client";
import { isValidCodeFormat, normalizeCode } from "@/lib/admin/code-acces";
export const dynamic = "force-dynamic";
const MAX_SIZE_BYTES = 5 * 1024 * 1024;
const ALLOWED_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/svg+xml"];
// POST /api/admin/auth/upload-logo
// Multipart : file=<logo>, code=WARI-XXXX-XXXX
// Upload MinIO + retourne logoUrl (à passer ensuite à /vitrine).
// NB: ne met PAS à jour Tenant.logoUrl directement — c'est /vitrine qui le fait.
export async function POST(req: NextRequest) {
const form = await req.formData();
const code = normalizeCode((form.get("code") as string) ?? "");
const file = form.get("file") as File | null;
if (!isValidCodeFormat(code)) {
return NextResponse.json({ error: "Code d'accès invalide" }, { status: 400 });
}
if (!file) {
return NextResponse.json({ error: "Fichier manquant" }, { status: 400 });
}
if (file.size > MAX_SIZE_BYTES) {
return NextResponse.json(
{ error: `Logo trop volumineux (max ${MAX_SIZE_BYTES / 1024 / 1024} Mo)` },
{ status: 413 }
);
}
if (!ALLOWED_TYPES.includes(file.type)) {
return NextResponse.json(
{ error: "Format non supporté (utilise PNG, JPG, WebP ou SVG)" },
{ status: 415 }
);
}
const tenant = await prisma.tenant.findUnique({
where: { codeAcces: code },
select: { id: true, actif: true, onboardingStep: true },
});
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 ext = file.name.split(".").pop() ?? "png";
const key = `setup/${tenant.id}/logo-${Date.now()}.${ext.toLowerCase()}`;
const buffer = Buffer.from(await file.arrayBuffer());
const url = await uploadFile(buffer, key, file.type);
return NextResponse.json({ url });
}