import axios from 'axios' // Create axios instance const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:5000/api', headers: { 'Content-Type': 'application/json' }, // Disable caching for API requests to ensure we always get fresh data adapter: 'xhr', // Use XHR adapter to avoid service worker caching issues withCredentials: true // Send cookies with requests }) // Add request timeout to prevent hanging requests api.defaults.timeout = 10000 // 10 seconds timeout // Environment-specific configuration const isDevelopment = import.meta.env.VITE_NODE_ENV === 'development' const isProduction = import.meta.env.VITE_NODE_ENV === 'production' // Debug logging function for development const logApiCall = (config) => { if (isDevelopment) { console.log(`🚀 [API] ${config.method?.toUpperCase()} ${config.url}`, { data: config.data, params: config.params, headers: config.headers }) } } // Error logging function for development const logApiError = (error) => { if (isDevelopment) { console.error(`❌ [API Error] ${error.config?.url || 'Unknown URL'}`, { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, message: error.message }) } } // Request interceptor to add auth token and logging api.interceptors.request.use( (config) => { // Add debug logging logApiCall(config) const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, (error) => { logApiError(error) return Promise.reject(error) } ) // Add token validation and refresh logic api.interceptors.request.use( async (config) => { const token = localStorage.getItem('token') if (token) { try { // Check if token is expired const tokenData = JSON.parse(atob(token.split('.')[1])) const isExpired = tokenData.exp * 1000 < Date.now() if (isExpired) { // Token is expired, remove it localStorage.removeItem('token') if (isDevelopment) { console.log('🔑 [Token] Token expired, removed from storage') } // If we're not on the login page, redirect there if (!window.location.pathname.includes('/login')) { window.location.href = '/login' } } else { // Token is valid, add to headers config.headers.Authorization = `Bearer ${token}` } } catch (error) { // Invalid token format, remove it localStorage.removeItem('token') if (isDevelopment) { console.log('🔑 [Token] Invalid token format, removed from storage') } // If we're not on the login page, redirect there if (!window.location.pathname.includes('/login')) { window.location.href = '/login' } } } return config }, (error) => { logApiError(error) return Promise.reject(error) } ) // Response interceptor to handle errors and logging api.interceptors.response.use( (response) => { if (isDevelopment) { console.log(`✅ [API Response] ${response.config.url}`, { status: response.status, data: response.data }) } return response }, (error) => { logApiError(error) if (error.response?.status === 401) { // Only redirect if we're not already on the login page if (!window.location.pathname.includes('/login')) { localStorage.removeItem('token') window.location.href = '/login' } } // Add more detailed error handling for different status codes if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx const status = error.response.status const data = error.response.data if (isDevelopment) { console.error(`🚨 [API Error ${status}]`, { url: error.config.url, method: error.config.method, data: data, headers: error.response.headers }) } } else if (error.request) { // The request was made but no response was received if (isDevelopment) { console.error('🚨 [Network Error] No response received:', { url: error.config?.url, message: error.message }) } } else { // Something happened in setting up the request that triggered an Error if (isDevelopment) { console.error('🚨 [Request Error]', error.message) } } return Promise.reject(error) } ) export default api