import axios, { AxiosError, InternalAxiosRequestConfig } from "axios"; import { LOGIN_SESSION_EXPIRED_HREF, readStoredRefreshToken, refreshBackendAccessToken, } from "@/app/utils/authToken"; const axiosInstance = axios.create({ baseURL: process.env.API_URL || process.env.NEXT_PUBLIC_API_URL, }); type RetriableConfig = InternalAxiosRequestConfig & { _retry?: boolean }; let requestInterceptorId: number | null = null; let responseInterceptorId: number | null = null; const clearInterceptors = () => { if (requestInterceptorId !== null) { axiosInstance.interceptors.request.eject(requestInterceptorId); requestInterceptorId = null; } if (responseInterceptorId !== null) { axiosInstance.interceptors.response.eject(responseInterceptorId); responseInterceptorId = null; } }; /** * Always prefer localStorage accessToken (may have been refreshed) over any * closed-over token passed into SetupAxiosInterceptors. */ export const SetupAxiosInterceptors = (_inputToken: string | null) => { clearInterceptors(); requestInterceptorId = axiosInstance.interceptors.request.use( (config) => { try { const token = (typeof window !== "undefined" ? localStorage.getItem("accessToken") : null) ?? _inputToken; if (token) { config.headers.Authorization = `Bearer ${token}`; } } catch { if (_inputToken) { config.headers.Authorization = `Bearer ${_inputToken}`; } } return config; }, (error) => Promise.reject(error), ); responseInterceptorId = axiosInstance.interceptors.response.use( (response) => response, async (error: AxiosError) => { const status = error.response?.status; const original = error.config as RetriableConfig | undefined; // Only 401 = unauthenticated. Do not treat 403 (forbidden) as session expiry. if (status === 401 && original && !original._retry && typeof window !== "undefined") { original._retry = true; const refreshed = await refreshBackendAccessToken(readStoredRefreshToken()); if (refreshed) { original.headers = original.headers ?? {}; original.headers.Authorization = `Bearer ${refreshed}`; axiosInstance.defaults.headers.Authorization = `Bearer ${refreshed}`; return axiosInstance(original); } window.location.href = LOGIN_SESSION_EXPIRED_HREF; } console.error("API Error:", error.response?.data || error); return Promise.reject(error); }, ); }; export default axiosInstance;