import axios from "axios"; export const baseURL = import.meta.env.VITE_API_BASE_URL; export const axiosInstance = axios.create({ baseURL, withCredentials: true, }); // set content-type to application/json unless sending FormData axiosInstance.interceptors.request.use((config) => { if (!(config.data instanceof FormData)) { config.headers["Content-Type"] = "application/json"; } return config; }); let refreshPromise: Promise | null = null; let refreshTimeout: number | null = null; export const clearRefreshTimeout = () => { if (refreshTimeout) { clearTimeout(refreshTimeout); refreshTimeout = null; } }; export const scheduleTokenRefresh = (expiryTimeSeconds: number) => { clearRefreshTimeout(); if (!expiryTimeSeconds || expiryTimeSeconds <= 0) { return; } const refreshTime = (expiryTimeSeconds - 60) * 1000; if (refreshTime <= 0) { refreshTokenMethod(); } else { refreshTimeout = setTimeout(refreshTokenMethod, refreshTime); } }; export const refreshTokenMethod = async (): Promise => { if (refreshPromise) { return refreshPromise; } refreshPromise = (async () => { try { const refreshResponse = await axiosInstance.post<{ tokenExpiresIn: number; }>("/auth/refresh"); scheduleTokenRefresh(refreshResponse.data.tokenExpiresIn); } catch (err) { clearRefreshTimeout(); console.error(err); throw err; } finally { refreshPromise = null; } })(); return refreshPromise; }; axiosInstance.interceptors.response.use( (response) => response, async (error) => { const originalConfig = error.config; if ( [401, 403].includes(error.response?.status) && !originalConfig._retry && originalConfig.url !== "/auth/refresh" ) { originalConfig._retry = true; try { await refreshTokenMethod(); return axiosInstance(originalConfig); } catch (err) { clearRefreshTimeout(); return Promise.reject(err); } } return Promise.reject(error); }, ); export default axiosInstance;