import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig } from 'axios'; // API Response types export interface ApiResponse { success: boolean; data: T; statusCode?: number; timestamp?: string; message?: string; } export interface ApiErrorResponse { success: false; statusCode: number; timestamp: string; path: string; method: string; error: string; message: string | string[]; errors?: Array<{ field: string; errors: string[] }>; } // Create axios instance const api: AxiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1', timeout: 30000, headers: { 'Content-Type': 'application/json', }, }); // Request interceptor api.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // Add auth token if available if (typeof window !== 'undefined') { const token = localStorage.getItem('accessToken'); if (token && config.headers) { config.headers.Authorization = `Bearer ${token}`; } } return config; }, (error: AxiosError) => { return Promise.reject(error); } ); // Response interceptor api.interceptors.response.use( (response) => { return response; }, async (error: AxiosError) => { const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }; // Handle 401 Unauthorized - try to refresh token if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = localStorage.getItem('refreshToken'); if (refreshToken) { const response = await axios.post( `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1'}/auth/refresh`, { refreshToken } ); const { accessToken, refreshToken: newRefreshToken } = response.data.data; localStorage.setItem('accessToken', accessToken); localStorage.setItem('refreshToken', newRefreshToken); // Retry original request with new token if (originalRequest.headers) { originalRequest.headers.Authorization = `Bearer ${accessToken}`; } return api(originalRequest); } } catch (refreshError) { // Refresh failed, clear storage and redirect to login localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); localStorage.removeItem('user'); if (typeof window !== 'undefined') { window.location.href = '/login'; } } } return Promise.reject(error); } ); // Helper to extract error message export function getErrorMessage(error: unknown): string { if (error instanceof AxiosError) { const apiError = error.response?.data as ApiErrorResponse; if (apiError?.message) { return Array.isArray(apiError.message) ? apiError.message.join(', ') : apiError.message; } } if (error instanceof Error) { return error.message; } return 'An unexpected error occurred'; } // Helper to extract field errors export function getFieldErrors(error: unknown): Array<{ field: string; errors: string[] }> { if (error instanceof AxiosError) { const apiError = error.response?.data as ApiErrorResponse; return apiError?.errors || []; } return []; } export default api;