espace-paie-odentas/lib/secureApi.ts

76 lines
No EOL
2.5 KiB
TypeScript

// Créez un nouveau fichier lib/secureApi.ts
/**
* Fetcher sécurisé qui passe par l'API Next.js (pas directement le Lambda)
* À utiliser pour les ressources sensibles comme les contrats individuels
*/
export async function secureApi<T>(path: string, init: RequestInit = {}): Promise<T> {
const headers = new Headers(init.headers || {});
// Headers par défaut
headers.set("Accept", "application/json");
if (init.body && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
// URL locale (API Next.js) au lieu du Lambda direct
const url = `/api${path}`;
console.log("🔒 Secure API call:", { url, method: init.method || 'GET' });
const res = await fetch(url, {
...init,
credentials: "include", // Important pour les cookies de session
headers,
cache: "no-store",
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(text || `API error ${res.status}`);
}
return res.json();
}
/*
* Exemple d'utilisation (à mettre dans app/(app)/contrats/[id]/page.tsx)
* ---------------------------------------------------------------
* import { useQuery } from '@tanstack/react-query';
* import { secureApi } from '@/lib/secureApi';
*
* // Importe ton vrai type ContratDetail depuis tes types de domaine
* // ou déclare un type minimal local si nécessaire.
* // import type { ContratDetail } from '@/types/contrats';
*
* function useContratDetail(id: string) {
* return useQuery<ContratDetail>({
* queryKey: ['contrat', id],
* queryFn: async () => {
* try {
* // ✅ Utilise le fetcher sécurisé au lieu de api()
* return await secureApi<ContratDetail>(`/contrats/${id}`);
* } catch (error: unknown) {
* const message = error instanceof Error ? error.message : String(error);
* // Gestion spécifique des erreurs d'accès
* if (message.includes('403') || message.includes('forbidden')) {
* throw new Error('access_denied');
* }
* if (message.includes('404')) {
* throw new Error('not_found');
* }
* throw error;
* }
* },
* staleTime: 15_000,
* retry: (failureCount: number, error: unknown) => {
* const message = error instanceof Error ? error.message : String(error);
* // Ne pas réessayer si c'est un problème d'accès
* if (message === 'access_denied' || message === 'not_found') {
* return false;
* }
* return failureCount < 3;
* },
* });
* }
*/