import type { AxiosError, AxiosInstance } from "axios"; import axios from "axios"; import { createContext, useContext, useMemo } from "react"; import { useToken } from "../token"; export interface AxiosState { publicApi: AxiosInstance; authorizedApi: AxiosInstance; } const initialState: AxiosState = { publicApi: {} as AxiosInstance, authorizedApi: {} as AxiosInstance } const AxiosContext = createContext(initialState); export default function AxiosProvider({ apiUrl, children }: Readonly<{ apiUrl: string; children: React.ReactNode; }>){ const { getToken } = useToken(); const publicApi = useMemo(() => { const api = axios.create({ baseURL: apiUrl, withCredentials: true }); return api; }, [apiUrl]); const authorizedApi = useMemo(() => { const api = axios.create({ baseURL: apiUrl, withCredentials: true }); api.interceptors.request.use(async (config) => { try{ const token = await getToken(); if(token){ config.headers.Authorization = `Bearer ${token}`; } return config; } catch(error){ return Promise.reject(error as Error); } }); api.interceptors.response.use(r => r, async (error: AxiosError) => { const original = error.config; if(!original){ return Promise.reject(error); } if(error.response?.status === 401 && !original._retry){ original._retry = true; try{ const newToken = await getToken(); original.headers.Authorization = `Bearer ${newToken}`; return api(original); } catch(refreshError){ return Promise.reject(refreshError as Error); } } }); return api; }, [apiUrl, getToken]); const value = useMemo(() => ({ publicApi, authorizedApi }), [authorizedApi, publicApi]); return ( {children} ); } // eslint-disable-next-line react-refresh/only-export-components export function useAxios(){ const context = useContext(AxiosContext); if(!context){ throw new Error("useAxios must be called inside an AxiosProvider"); } return context; }