All files / lib/api/core fetchWrappers.ts

89.18% Statements 33/37
80.95% Branches 17/21
66.66% Functions 2/3
97.05% Lines 33/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 9440x 40x 40x     40x 40x 40x   40x 40x 40x     40x         11x   11x   2x   2x   1x 1x       1x     1x     1x     1x         10x     10x   5x         5x     5x   5x       5x                               2x 2x     1x 1x     1x   1x    
import { makeRequest } from './httpHelpers';
import { refreshToken } from '../auth/authService';
import { clearTokens, notifySessionExpired } from './tokenHelpers';
 
 
const baseUrl = process.env.NEXT_PUBLIC_API_URL!;
const tokenKey = process.env.NEXT_PUBLIC_AUTH_TOKEN_KEY!;
const refreshKey = process.env.NEXT_PUBLIC_AUTH_REFRESH_TOKEN_KEY!;
 
Iif (!baseUrl) throw new Error("API base URL is not défini.");
Iif (!tokenKey) throw new Error("NEXT_PUBLIC_AUTH_TOKEN_KEY est manquant.");
Iif (!refreshKey) throw new Error("NEXT_PUBLIC_AUTH_REFRESH_TOKEN_KEY est manquant.");
 
 
export async function fetchApi<T = unknown>(
    endpoint: string,
    options: RequestInit = {},
    requiresAuth = true
): Promise<T> {
    let response = await makeRequest(endpoint, options, requiresAuth);
  
    if (response.status === 401 && requiresAuth) {
        // Tenter de rafraîchir le token
        const refreshed = await tryRefreshToken();
        
        if (refreshed) {
            // Émettre un événement pour informer de la réussite du refresh (optionnel)
            if (typeof window !== 'undefined') {
                window.dispatchEvent(new CustomEvent('tokenRefreshed'));
            }
            
            // Refaire la requête avec le nouveau token
            response = await makeRequest(endpoint, options, requiresAuth);
        } else {
            // Nettoyer les tokens invalides
            clearTokens();
            
            // Notifier l'expiration de session (déclenche le modal)
            notifySessionExpired();
            
            // Lever une erreur pour arrêter le processus
            throw new Error("Session expirée. Veuillez vous reconnecter.");
        }
    }
    
    // Récupérer le type de contenu de la réponse
    const contentType = response.headers.get("Content-Type") || "";
    
    // Gérer les erreurs HTTP (status codes 4xx, 5xx)
    if (!response.ok) {
        // Tenter de parser l'erreur en JSON si possible
        const errorData = contentType.includes("application/json")
        ? await response.json().catch(() => ({}))
        : {};
        
        // Créer une erreur avec le message du serveur ou le status text par défaut
        const error = new Error(`(${response.status}) ${errorData.detail || response.statusText}`);
        
        // Attacher les données d'erreur à l'exception pour un debugging avancé
        (error as Error & { data: unknown }).data = errorData;
        
        throw error;
    }
    
    // Parser et retourner les données de la réponse
    return contentType.includes("application/json")
        ? await response.json()      // Parser en JSON si le content-type l'indique
        : ({} as T);                 // Retourner un objet vide sinon
}
 
/**
 * Fonction utilitaire pour tenter de rafraîchir le token d'authentification
 * 
 * Cette fonction :
 * - Appelle le service de refresh token
 * - Gère les erreurs de refresh (token expiré, réseau, etc.)
 * - Retourne un booléen indiquant le succès ou l'échec
 * 
 * @returns Promise<boolean> - true si le refresh a réussi, false sinon
 */
async function tryRefreshToken(): Promise<boolean> {
  try {
    await refreshToken();
    
    // Émettre un événement pour informer les autres composants du refresh réussi
    if (typeof window !== 'undefined') {
      window.dispatchEvent(new CustomEvent('tokenRefreshed'));
    }
    
    return true;
  } catch {
    return false;
  }
}