mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
Les données de la grille (ventes 12 mois, stock, marges, gammes, métriques réseau Qlik) n'étaient accessibles par aucun moyen programmatique, et getProductRows() exige un fournisseur : chercher un produit sans le connaître était impossible. Contrainte de conception : l'API ne recalcule jamais rien. La grille était reconstruite en direct et gardée seulement 10 min en mémoire ; une API qui appellerait getProductRows() serait lente et imprévisible. On persiste donc le résultat d'un calcul qui a déjà lieu, et on le sert. - Table grid_rows : colonnes scalaires (filtre/tri/recherche en SQL) + payload jsonb du ProductRow complet. Remplie en effet de bord NON bloquant par getProductRows(), purge des articles disparus via computed_at. Survit aux redémarrages, contrairement au cache mémoire. - Endpoints /api/v1 : fournisseurs, grid, products/search (transversale, tous fournisseurs), products/:codein, network/:codeCentrale, openapi.json. Pagination, tri sur liste blanche, projection de champs, validation zod. 202 not_ready si un fournisseur n'a pas encore d'instantané. - Authentification double : clé d'API (X-API-Key ou Bearer, SHA-256 en base, révocable) ou session existante. Le middleware exempte /api/v1 — sans quoi un script recevait une redirection 302 vers /login au lieu d'un 401 JSON. - Gestion des clés dans /settings (server actions, clé affichée une seule fois). Vérifié contre une base PostgreSQL locale : 401 JSON sans clé, 401 sur clé révoquée, recherche renvoyant plusieurs fournisseurs, upsert + purge, et 24 appels /api/v1 sans déclencher un seul recalcul (l'ancienne route /api/grid/rows en déclenche un à chaque appel). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
import NextAuth from "next-auth";
|
|
import { authConfig } from "@/lib/auth.config";
|
|
import { NextResponse } from "next/server";
|
|
|
|
const { auth } = NextAuth(authConfig);
|
|
|
|
export default auth((req) => {
|
|
const isLoggedIn = !!req.auth;
|
|
const { nextUrl } = req;
|
|
|
|
const isApiAuthRoute = nextUrl.pathname.startsWith("/api/auth");
|
|
const isPublicRoute = nextUrl.pathname === "/login" || nextUrl.pathname.startsWith("/public/");
|
|
// L'API publique s'authentifie elle-même (clé d'API ou session) et rend ses
|
|
// propres erreurs JSON. Sans cette exemption, un script non authentifié
|
|
// recevrait une redirection 302 + la page HTML de login au lieu d'un 401.
|
|
const isPublicApiRoute = nextUrl.pathname.startsWith("/api/v1");
|
|
|
|
// 1. Laisser passer les requêtes d'auth API
|
|
if (isApiAuthRoute) return NextResponse.next();
|
|
|
|
// 1 bis. Laisser passer /api/v1 — l'authentification est faite dans les handlers
|
|
if (isPublicApiRoute) return NextResponse.next();
|
|
|
|
// 2. Rediriger vers /login si non connecté et route non publique
|
|
if (!isLoggedIn && !isPublicRoute) {
|
|
return NextResponse.redirect(new URL("/login", nextUrl));
|
|
}
|
|
|
|
// 3. Rediriger vers le dashboard si déjà connecté et sur /login
|
|
if (isLoggedIn && isPublicRoute) {
|
|
return NextResponse.redirect(new URL("/grid", nextUrl));
|
|
}
|
|
|
|
// 4. Protection par rôle (Admin seulement pour les paramètres)
|
|
const isAdminRoute = nextUrl.pathname.startsWith("/settings") || nextUrl.pathname.startsWith("/admin");
|
|
const userRole = (req.auth?.user as any)?.role;
|
|
|
|
if (isAdminRoute && userRole !== "admin") {
|
|
return NextResponse.redirect(new URL("/grid", nextUrl));
|
|
}
|
|
|
|
return NextResponse.next();
|
|
});
|
|
|
|
export const config = {
|
|
// Protège toutes les routes sauf fichiers statiques et assets
|
|
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.png$|.*\\.jpg$).*)"],
|
|
};
|