import axios from 'axios'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1'; const DEBUG_MODE = import.meta.env.DEV; // Enable detailed logging in development export const api = axios.create({ baseURL: API_URL, headers: { 'Content-Type': 'application/json', }, timeout: 30000, // 30 second timeout }); // Separate instance for AI Copilot with longer timeout (AI can take a while to respond) export const copilotApi = axios.create({ baseURL: API_URL, headers: { 'Content-Type': 'application/json', }, timeout: 300000, // 5 minute timeout for AI requests (large tasks need multiple tool calls) }); // Token getter function - set by AuthContext when authenticated let getToken: (() => Promise) | null = null; export function setTokenGetter(getter: (() => Promise) | null) { getToken = getter; } // Shared request interceptor function (async to support token refresh) const requestInterceptor = async (config: any) => { if (getToken) { try { const token = await getToken(); config.headers.Authorization = `Bearer ${token}`; } catch { // Token fetch failed - request goes without auth header } } if (DEBUG_MODE) { console.log(`[API] → ${config.method?.toUpperCase()} ${config.url}`, { data: config.data, params: config.params, }); } return config; }; const requestErrorInterceptor = (error: any) => { console.error('[API] Request error:', error); return Promise.reject(error); }; // Apply interceptors to both API instances api.interceptors.request.use(requestInterceptor, requestErrorInterceptor); copilotApi.interceptors.request.use(requestInterceptor, requestErrorInterceptor); // Shared response interceptor function const responseInterceptor = (response: any) => { // Log successful response in development mode if (DEBUG_MODE) { console.log(`[API] ← ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, { data: response.data, }); } return response; }; const responseErrorInterceptor = (error: any) => { const { config, response } = error; // Enhanced error logging if (response) { // Server responded with error status console.error( `[API] ✖ ${response.status} ${config?.method?.toUpperCase()} ${config?.url}`, { status: response.status, statusText: response.statusText, data: response.data, } ); // Log specific error types if (response.status === 401) { console.warn('[API] Authentication required - user may need to log in again'); } else if (response.status === 403) { console.warn('[API] Permission denied - user lacks required permissions'); } else if (response.status === 404) { console.warn('[API] Resource not found'); } else if (response.status === 409) { console.warn('[API] Conflict detected:', response.data.conflicts || response.data.message); } else if (response.status >= 500) { console.error('[API] Server error - backend may be experiencing issues'); } } else if (error.request) { // Request was made but no response received console.error('[API] ✖ Network error - no response received', { method: config?.method?.toUpperCase(), url: config?.url, message: error.message, }); } else { // Something else happened console.error('[API] ✖ Request setup error:', error.message); } return Promise.reject(error); }; // Apply response interceptors to both API instances api.interceptors.response.use(responseInterceptor, responseErrorInterceptor); copilotApi.interceptors.response.use(responseInterceptor, responseErrorInterceptor);