import axios from 'axios' import { goLogin } from '../utils/navigate' import { authStorageKeys, clearAuthSession, markActivity, refreshAuthSession, refreshIfNeeded, } from '../auth/session' const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL ?? '/api', timeout: 15000, }) http.interceptors.request.use(async (config) => { const isAuthenticationRequest = config.url?.endsWith('/auth/login') || config.url?.endsWith('/auth/refresh') || config.url?.endsWith('/auth/logout') || config.url?.endsWith('/auth/sso/exchange') || config.url?.endsWith('/auth/sso/bind') if (!isAuthenticationRequest) { const activeToken = await refreshIfNeeded(true) if (activeToken) markActivity() } const token = localStorage.getItem(authStorageKeys.token) if (token) config.headers.Authorization = `Bearer ${token}` return config }) http.interceptors.response.use( (response) => response, async (error) => { const isAuthenticationRequest = error.config?.url?.endsWith('/auth/login') || error.config?.url?.endsWith('/auth/refresh') || error.config?.url?.endsWith('/auth/logout') || error.config?.url?.endsWith('/auth/sso/exchange') || error.config?.url?.endsWith('/auth/sso/bind') const retryableConfig = error.config as (typeof error.config & { _jiaowuRetried?: boolean }) | undefined if (error.response?.status === 401 && !isAuthenticationRequest && !retryableConfig?._jiaowuRetried) { const token = await refreshAuthSession() if (token && retryableConfig) { retryableConfig._jiaowuRetried = true retryableConfig.headers.Authorization = `Bearer ${token}` return http.request(retryableConfig) } } if (error.response?.status === 401 && !isAuthenticationRequest) { clearAuthSession() goLogin(location.pathname + location.search + location.hash) } return Promise.reject(error) }, ) export function apiErrorMessage(error: unknown) { if (!axios.isAxiosError(error)) return '操作失败,请稍后重试。' const status = error.response?.status const data = error.response?.data if (data?.errors && typeof data.errors === 'object') { const validationMessage = Object.values(data.errors) .flat() .map((item) => String(item).trim()) .filter(Boolean) .join(';') if (validationMessage) return validationMessage } const detail = typeof data?.detail === 'string' ? data.detail.trim() : '' if (detail) return detail const title = typeof data?.title === 'string' ? data.title.trim() : '' if (title) return title if (status === 403) return '您没有权限执行此操作,请联系管理员。' if (status === 404) return '请求的资源不存在。' if (status === 401) return '登录已过期,请重新登录。' if (status && status >= 500) return '服务器繁忙,请稍后重试。' return '操作失败,请检查网络连接。' } export default http