import axios, { InternalAxiosRequestConfig } from 'axios'; import { getApiBaseUrl } from '../config'; import { triggerSessionExpired, isUserSyncEndpoint, isTenantEndpoint } from './sessionExpiration'; import { isUserSyncedInBrowser, isActivationPending, syncUserAccount } from './userSyncService'; export const BASE_API_URL = getApiBaseUrl(); // Create Axios Instance export const apiClient = axios.create({ baseURL: BASE_API_URL, headers: { 'Content-Type': 'application/json; version=1', Accept: 'application/json; version=1', }, timeout: 15000, }); // Request Interceptor to dynamically set baseURL and attach Bearer Token apiClient.interceptors.request.use( async (config: InternalAxiosRequestConfig) => { // Dynamically synchronize baseURL from runtime configuration config.baseURL = getApiBaseUrl(); const token = localStorage.getItem('pfm_access_token'); if (token && config.headers) { config.headers.Authorization = `Bearer ${token}`; } // User Sync & Tenant Lifecycle Guard: // All API calls require an authenticated and enabled user in the backend system. const isLifecycleRequest = isUserSyncEndpoint(config.url) || isTenantEndpoint(config.url); if (token && !isLifecycleRequest) { // If user account is flagged as pending administrator activation, block downstream API calls if (isActivationPending()) { return Promise.reject(new Error('User account is pending administrator activation')); } const storedSub = localStorage.getItem('pfm_user_sub') || ''; if (!isUserSyncedInBrowser(storedSub, token)) { const syncRes = await syncUserAccount(storedSub, token); if (!syncRes.success) { return Promise.reject(new Error(syncRes.errorMessage || 'User account sync pending activation')); } } } return config; }, (error) => Promise.reject(error) ); // Response Interceptor for global HTTP 401 detection apiClient.interceptors.response.use( (response) => response, (error) => { if (error?.response?.status === 401) { const requestUrl = error.config?.url; const isLifecycleEndpoint = isUserSyncEndpoint(requestUrl) || isTenantEndpoint(requestUrl); // Handle 401 Unauthorized errors if (!isLifecycleEndpoint) { // Working endpoint returned HTTP 401: trigger session expiration prompt const detailMsg = error?.response?.data?.detail || error?.response?.data?.message || 'Authentication session expired or security token invalidated (HTTP 401).'; triggerSessionExpired(detailMsg, requestUrl); } else { // User sync endpoint returned HTTP 401: clear cached auth state if (typeof window !== 'undefined') { localStorage.removeItem('pfm_access_token'); localStorage.removeItem('pfm_access_user'); localStorage.removeItem('pfm_user_sync_record'); localStorage.removeItem('pfm_user_pending_activation'); localStorage.removeItem('pfm_user_pending_activation_reason'); } } } return Promise.reject(error); } );