import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import { API_BASE_URL } from '../../config'; import { getSystemHealth } from './health'; // Mobile platform detection (simpler approach without Capacitor dependency) const isMobileApp = () => { return typeof window !== 'undefined' && (window.location.href.includes('capacitor://') || window.location.href.includes('ionic://') || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)); }; const isNative = isMobileApp(); // Create a custom axios instance const apiClient = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json', // Add additional headers for mobile apps ...(isNative && { 'X-Requested-With': 'XMLHttpRequest', 'X-Mobile-App': 'true' }) }, timeout: 30000, // 30 seconds timeout withCredentials: false // Disable withCredentials which can cause CORS issues }); // Request interceptor to handle authentication for all requests apiClient.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // Get token from user_session in localStorage const userSession = localStorage.getItem('user_session'); // Add detailed debugging console.log(`API Request to: ${config.url}`); console.log(`Method: ${config.method?.toUpperCase()}`); console.log(`Running on platform: ${isNative ? 'Mobile' : 'Web'}`); // If session exists, parse it and add token to headers if (userSession) { try { const userData = JSON.parse(userSession); console.log(`User session found: ${userData.firstName} ${userData.lastName} (${userData.email})`); if (userData && userData.token) { // Set Authorization header for all requests config.headers.Authorization = `Bearer ${userData.token}`; console.log(`Token applied: ${userData.token.substring(0, 15)}...`); } else { console.warn('Token missing in user session! Session data:', JSON.stringify(userData, null, 2)); } } catch (error) { console.error('Error parsing user session:', error); } } else { console.warn('No user_session found in localStorage - request will be unauthorized'); } // Log final headers for debugging // console.log('Request headers:', JSON.stringify(config.headers, null, 2)); // console.log('Request URL (full):', `${config.baseURL}${config.url}`); return config; }, (error: AxiosError) => { // Handle request errors console.error('Request error:', error); return Promise.reject(error); } ); // Special interceptor for the health endpoint apiClient.interceptors.response.use( (response: AxiosResponse) => { const url = response.config.url; // Check if this is a health endpoint request if (url && (url === '/health' || url.endsWith('/health') || url === '/api/health' || url.endsWith('/api/health'))) { console.log('Health endpoint detected, returning custom health response'); // Use the shared health implementation from health.ts response.data = getSystemHealth(); } return response; }, (error) => Promise.reject(error) ); // Response interceptor to handle authentication-related responses apiClient.interceptors.response.use( (response: AxiosResponse) => { // Any status code within the range of 2xx causes this function to trigger return response; }, async (error: AxiosError) => { const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }; // Handle 401 Unauthorized errors if (error.response?.status === 401 && !originalRequest._retry) { // Since we're using user_session, update refresh token logic accordingly const userSession = localStorage.getItem('user_session'); if (userSession) { originalRequest._retry = true; try { // Here you could implement token refresh logic // Example (commented out): /* const userData = JSON.parse(userSession); // Call your refresh token API const response = await apiClient.post( `${API_BASE_URL}/auth/refresh-token`, { userId: userData.userId, // Include any other required fields }, { headers: { 'Content-Type': 'application/json' } } ); if (response.data && response.data.token) { // Update the token in localStorage userData.token = response.data.token; localStorage.setItem('user_session', JSON.stringify(userData)); // Retry the original request with new token originalRequest.headers.Authorization = `Bearer ${response.data.token}`; return apiClient(originalRequest); } */ // For now, just clear the session and redirect to login localStorage.removeItem('user_session'); // Redirect to login page window.location.href = '/login'; return Promise.reject(error); } catch (refreshError) { // If any error during this process, clear session and redirect localStorage.removeItem('user_session'); // Redirect to login page window.location.href = '/login'; return Promise.reject(refreshError); } } else { // No session available, redirect to login window.location.href = '/login'; } } // Handle 403 Forbidden errors (permissions issue) if (error.response?.status === 403) { console.error('Forbidden resource:', error.config?.url); // You can implement specific handling for forbidden resources // For example, show an access denied notification or redirect // window.location.href = '/access-denied'; } // Handle 404 Not Found errors if (error.response?.status === 404) { console.error('Resource not found:', error.config?.url); } // Handle 500 and other server errors if (error.response?.status && error.response.status >= 500) { console.error('Server error:', error.response.status, error.config?.url); // Optionally show a server error notification } // Network errors if (error.message === 'Network Error') { console.error('Network error - check internet connection'); // Optionally show a network error notification } // Timeout errors if (error.code === 'ECONNABORTED') { console.error('Request timeout'); // Optionally show a timeout error notification } return Promise.reject(error); } ); export default apiClient;