import axios from "axios"; import type { AxiosError, InternalAxiosRequestConfig } from "axios"; import { useAuthStore } from "../store/authStore"; const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? "/api/v1"; export const http = axios.create({ baseURL: apiBaseUrl, timeout: 10000 }); http.interceptors.request.use((config) => { const token = useAuthStore.getState().accessToken; if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); let refreshInFlight: Promise | null = null; function shouldSkipRefresh(config?: InternalAxiosRequestConfig): boolean { const url = config?.url ?? ""; return ( url.includes("/auth/login") || url.includes("/auth/refresh") || url.includes("/auth/register") || url.includes("/auth/check-email") || url.includes("/auth/verify-email") || url.includes("/auth/resend-verification") || url.includes("/auth/request-password-reset") || url.includes("/auth/reset-password") ); } http.interceptors.response.use( (response) => response, async (error: AxiosError) => { const status = error.response?.status; const originalRequest = error.config as (InternalAxiosRequestConfig & { _retry?: boolean }) | undefined; if (!originalRequest || status !== 401 || originalRequest._retry || shouldSkipRefresh(originalRequest)) { return Promise.reject(error); } originalRequest._retry = true; const authStore = useAuthStore.getState(); try { if (!refreshInFlight) { refreshInFlight = authStore.refresh().finally(() => { refreshInFlight = null; }); } await refreshInFlight; return http.request(originalRequest); } catch (refreshError) { useAuthStore.getState().logout(); return Promise.reject(refreshError); } } );