| import axios, { AxiosError, type InternalAxiosRequestConfig } from "axios";
|
|
|
| const TOKEN_KEY = "lc_admin_token";
|
| const REFRESH_KEY = "lc_admin_refresh";
|
|
|
| export const tokenStore = {
|
| get: () => localStorage.getItem(TOKEN_KEY),
|
| getRefresh: () => localStorage.getItem(REFRESH_KEY),
|
| set: (access: string, refresh?: string) => {
|
| localStorage.setItem(TOKEN_KEY, access);
|
| if (refresh) localStorage.setItem(REFRESH_KEY, refresh);
|
| },
|
| clear: () => {
|
| localStorage.removeItem(TOKEN_KEY);
|
| localStorage.removeItem(REFRESH_KEY);
|
| },
|
| };
|
|
|
| const baseURL =
|
| import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000/api/v1";
|
|
|
| const api = axios.create({ baseURL, timeout: 20000 });
|
|
|
| api.interceptors.request.use((config) => {
|
| const token = tokenStore.get();
|
| if (token) config.headers.Authorization = `Bearer ${token}`;
|
| return config;
|
| });
|
|
|
| let onUnauthorized: (() => void) | null = null;
|
| export const setUnauthorizedHandler = (fn: () => void) => {
|
| onUnauthorized = fn;
|
| };
|
|
|
|
|
| let refreshing: Promise<string | null> | null = null;
|
|
|
| async function doRefresh(): Promise<string | null> {
|
| const refresh = tokenStore.getRefresh();
|
| if (!refresh) return null;
|
| try {
|
| const res = await axios.post(`${baseURL}/auth/refresh`, {
|
| refresh_token: refresh,
|
| });
|
| tokenStore.set(res.data.access_token, res.data.refresh_token);
|
| return res.data.access_token as string;
|
| } catch {
|
| return null;
|
| }
|
| }
|
|
|
| api.interceptors.response.use(
|
| (res) => res,
|
| async (error: AxiosError) => {
|
| const original = error.config as InternalAxiosRequestConfig & {
|
| _retry?: boolean;
|
| };
|
| const isAuthCall = original?.url?.includes("/auth/");
|
|
|
| if (error.response?.status === 401 && original && !original._retry && !isAuthCall) {
|
| original._retry = true;
|
| refreshing = refreshing ?? doRefresh();
|
| const newToken = await refreshing;
|
| refreshing = null;
|
| if (newToken) {
|
| original.headers.Authorization = `Bearer ${newToken}`;
|
| return api(original);
|
| }
|
| tokenStore.clear();
|
| onUnauthorized?.();
|
| }
|
| return Promise.reject(error);
|
| },
|
| );
|
|
|
| export function apiErrorMessage(error: unknown): string {
|
| if (axios.isAxiosError(error)) {
|
| const detail = error.response?.data?.detail;
|
| if (typeof detail === "string") return detail;
|
| if (Array.isArray(detail) && detail[0]?.msg) return detail[0].msg as string;
|
| if (error.code === "ERR_NETWORK") return "Cannot reach the server.";
|
| }
|
| return "Something went wrong. Please try again.";
|
| }
|
|
|
| export default api;
|
|
|