Compare commits
No commits in common. "main" and "fix/auto-20260227-164540" have entirely different histories.
main
...
fix/auto-2
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type { AxiosInstance, AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
import type { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
// API 基础配置
|
// API 基础配置
|
||||||
// 开发环境使用代理,生产环境使用环境变量
|
// 开发环境使用代理,生产环境使用环境变量
|
||||||
@ -62,65 +62,6 @@ function reportToLogCenter(error: Error, context?: Record<string, unknown>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token 自动刷新状态
|
|
||||||
let isRefreshing = false;
|
|
||||||
let failedQueue: Array<{
|
|
||||||
resolve: (value: AxiosResponse) => void;
|
|
||||||
reject: (reason?: unknown) => void;
|
|
||||||
config: InternalAxiosRequestConfig;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
function getRefreshToken(): string | null {
|
|
||||||
try {
|
|
||||||
const authStorage = localStorage.getItem('auth-storage');
|
|
||||||
if (authStorage) {
|
|
||||||
const parsed = JSON.parse(authStorage);
|
|
||||||
return parsed?.state?.refreshToken || null;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processQueue(error: Error | null, token: string | null = null) {
|
|
||||||
failedQueue.forEach(({ resolve, reject, config }) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
request(config).then(resolve).catch(reject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
failedQueue = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStoredTokens(access: string, refresh?: string) {
|
|
||||||
localStorage.setItem('admin_token', access);
|
|
||||||
try {
|
|
||||||
const authStorage = localStorage.getItem('auth-storage');
|
|
||||||
if (authStorage) {
|
|
||||||
const parsed = JSON.parse(authStorage);
|
|
||||||
if (parsed?.state) {
|
|
||||||
parsed.state.token = access;
|
|
||||||
if (refresh) {
|
|
||||||
parsed.state.refreshToken = refresh;
|
|
||||||
}
|
|
||||||
localStorage.setItem('auth-storage', JSON.stringify(parsed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAuthAndRedirect() {
|
|
||||||
localStorage.removeItem('admin_token');
|
|
||||||
localStorage.removeItem('admin_info');
|
|
||||||
localStorage.removeItem('auth-storage');
|
|
||||||
window.location.href = '/login';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 Axios 实例
|
// 创建 Axios 实例
|
||||||
const request: AxiosInstance = axios.create({
|
const request: AxiosInstance = axios.create({
|
||||||
baseURL: BASE_URL,
|
baseURL: BASE_URL,
|
||||||
@ -154,9 +95,12 @@ request.interceptors.response.use(
|
|||||||
const data = response.data;
|
const data = response.data;
|
||||||
// 业务错误处理
|
// 业务错误处理
|
||||||
if (data.code !== 0) {
|
if (data.code !== 0) {
|
||||||
// Token 过期(业务错误码)
|
// Token 过期
|
||||||
if (data.code === 401 || data.code === 1001) {
|
if (data.code === 401 || data.code === 1001) {
|
||||||
clearAuthAndRedirect();
|
localStorage.removeItem('admin_token');
|
||||||
|
localStorage.removeItem('admin_info');
|
||||||
|
localStorage.removeItem('auth-storage');
|
||||||
|
window.location.href = '/login';
|
||||||
return Promise.reject(new Error(data.message || '登录已过期'));
|
return Promise.reject(new Error(data.message || '登录已过期'));
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(data.message || '请求失败'));
|
return Promise.reject(new Error(data.message || '请求失败'));
|
||||||
@ -164,64 +108,23 @@ request.interceptors.response.use(
|
|||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
(error: AxiosError<{ code: number; message: string }>) => {
|
(error: AxiosError<{ code: number; message: string }>) => {
|
||||||
|
// 上报到 Log Center
|
||||||
|
const apiError = new Error(error.message);
|
||||||
|
reportToLogCenter(apiError, {
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method,
|
||||||
|
status: error.response?.status,
|
||||||
|
responseData: error.response?.data,
|
||||||
|
});
|
||||||
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
const { status, data } = error.response;
|
const { status, data } = error.response;
|
||||||
const originalConfig = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
if (status === 401) {
|
||||||
|
localStorage.removeItem('admin_token');
|
||||||
// 401: 尝试用 refresh token 刷新
|
localStorage.removeItem('admin_info');
|
||||||
if (status === 401 && originalConfig && !originalConfig._retry) {
|
localStorage.removeItem('auth-storage');
|
||||||
const refreshTokenValue = getRefreshToken();
|
window.location.href = '/login';
|
||||||
|
|
||||||
if (!refreshTokenValue) {
|
|
||||||
clearAuthAndRedirect();
|
|
||||||
return Promise.reject(new Error(data?.message || '登录已过期'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRefreshing) {
|
|
||||||
// 已在刷新中,排队等待
|
|
||||||
return new Promise<AxiosResponse>((resolve, reject) => {
|
|
||||||
failedQueue.push({ resolve, reject, config: originalConfig });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
originalConfig._retry = true;
|
|
||||||
isRefreshing = true;
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.post(
|
|
||||||
`${request.defaults.baseURL || ''}/api/admin/auth/refresh/`,
|
|
||||||
{ refresh: refreshTokenValue },
|
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
|
||||||
)
|
|
||||||
.then((res) => {
|
|
||||||
const tokenData = res.data.data || res.data;
|
|
||||||
const newAccess: string = tokenData.access;
|
|
||||||
const newRefresh: string | undefined = tokenData.refresh;
|
|
||||||
|
|
||||||
updateStoredTokens(newAccess, newRefresh);
|
|
||||||
isRefreshing = false;
|
|
||||||
processQueue(null, newAccess);
|
|
||||||
|
|
||||||
originalConfig.headers.Authorization = `Bearer ${newAccess}`;
|
|
||||||
return request(originalConfig);
|
|
||||||
})
|
|
||||||
.catch((refreshError) => {
|
|
||||||
isRefreshing = false;
|
|
||||||
processQueue(refreshError instanceof Error ? refreshError : new Error('刷新令牌失败'));
|
|
||||||
clearAuthAndRedirect();
|
|
||||||
return Promise.reject(new Error('登录已过期,请重新登录'));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非 401 错误上报到 Log Center
|
|
||||||
const apiError = new Error(error.message);
|
|
||||||
reportToLogCenter(apiError, {
|
|
||||||
url: error.config?.url,
|
|
||||||
method: error.config?.method,
|
|
||||||
status: error.response?.status,
|
|
||||||
responseData: error.response?.data,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.reject(new Error(data?.message || `请求失败 (${status})`));
|
return Promise.reject(new Error(data?.message || `请求失败 (${status})`));
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error('网络错误,请检查网络连接'));
|
return Promise.reject(new Error('网络错误,请检查网络连接'));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user