const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ""; type RequestOptions = { method?: string; body?: unknown; token?: string; }; async function request(path: string, options: RequestOptions = {}): Promise { const { method = "GET", body, token } = options; const headers: Record = { "Content-Type": "application/json", }; if (token) { headers["Authorization"] = `Bearer ${token}`; } const res = await fetch(`${API_BASE}/api${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) { const error = await res.json().catch(() => ({ detail: "Erreur réseau" })); throw new Error(error.detail ?? "Erreur inconnue"); } if (res.status === 204) return undefined as T; return res.json(); } // Auth export const authApi = { login: (email: string, password: string) => { const form = new URLSearchParams({ username: email, password }); return fetch(`${API_BASE}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: form.toString(), }).then((r) => { if (!r.ok) throw new Error("Identifiants incorrects"); return r.json() as Promise<{ access_token: string; token_type: string }>; }); }, me: (token: string) => request("/auth/me", { token }), }; // Clients export const clientsApi = { list: (token: string) => request("/clients/", { token }), get: (id: number, token: string) => request(`/clients/${id}`, { token }), create: (data: ClientCreate, token: string) => request("/clients/", { method: "POST", body: data, token }), update: (id: number, data: Partial, token: string) => request(`/clients/${id}`, { method: "PATCH", body: data, token }), delete: (id: number, token: string) => request(`/clients/${id}`, { method: "DELETE", token }), }; // Audits export const auditsApi = { list: (token: string, clientId?: number) => request(`/audits/${clientId ? `?client_id=${clientId}` : ""}`, { token }), get: (id: number, token: string) => request(`/audits/${id}`, { token }), create: (data: AuditCreate, token: string) => request("/audits/", { method: "POST", body: data, token }), update: (id: number, data: Partial, token: string) => request(`/audits/${id}`, { method: "PATCH", body: data, token }), }; // Types export type User = { id: number; email: string; fullName: string; isActive: boolean; isAdmin: boolean; }; export type Client = { id: number; nom: string; contact: string | null; email: string | null; telephone: string | null; notes: string | null; createdAt: string; }; export type ClientCreate = { nom: string; contact?: string; email?: string; telephone?: string; notes?: string; }; export type Audit = { id: number; clientId: number; nom: string; statut: "planifie" | "en_cours" | "termine" | "annule"; dateDebut: string | null; dateFin: string | null; scoreGlobal: number | null; createdAt: string; }; export type AuditCreate = { clientId: number; nom: string; dateDebut?: string; }; export type AuditDetail = Audit & { cibles: Cible[]; vulnerabilites: Vulnerabilite[]; }; export type Cible = { id: number; auditId: number; type: "ip" | "domaine" | "subnet"; valeur: string; validee: boolean; }; export type Vulnerabilite = { id: number; auditId: number; criticite: "critique" | "important" | "modere" | "faible"; titre: string; description: string; recommandation: string; cve: string | null; cvssScore: number | null; cible: string | null; };