import axios from 'axios' import type { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const request: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_API_BASE || '/api', timeout: 30000, headers: { 'Content-Type': 'application/json', }, // Session tokens live in HttpOnly cookies — send them along. withCredentials: true, }) // Response interceptor request.interceptors.response.use( (response: AxiosResponse) => { // Backend wraps everything in { data, success } — unwrap to data const body = response.data if (body && typeof body === 'object' && 'success' in body && 'data' in body) { return body.data } return body }, async (error: AxiosError) => { // Access token expired: try the refresh cookie once, then retry the // original request. A second 401 (refresh failed) logs the user out. const config = error.config as (InternalAxiosRequestConfig & { _retried?: boolean }) | undefined if ( error.response?.status === 401 && config && !config._retried && !config.url?.includes('/auth/login') && !config.url?.includes('/auth/refresh') ) { config._retried = true try { // 规范拼接:VITE_API_BASE 可能带尾斜杠(如 /v2-api/),直接相加会产生 // /v2-api//auth/refresh 双斜杠导致 404 const base = (import.meta.env.VITE_API_BASE || '/api').replace(/\/+$/, '') await axios.post( `${base}/auth/refresh`, {}, { withCredentials: true }, ) return request.request(config) } catch { // fall through to the 401 handling below } } if (error.response) { const { status, data } = error.response switch (status) { case 401: ElMessage.error('Unauthorized, please login') router.push('/login') break case 403: ElMessage.error('Forbidden') break case 404: ElMessage.error('Resource not found') break case 500: ElMessage.error('Server error') break default: const errorMessage = (data as any)?.message || 'Request failed' ElMessage.error(errorMessage) } } else if (error.request) { ElMessage.error('Network error') } else { ElMessage.error('Request failed') } return Promise.reject(error) } ) export default request