fix: auto repair bugs #50

This commit is contained in:
repair-agent 2026-03-09 10:10:03 +08:00
parent 6b6d4645dd
commit 1e3babee0c
2 changed files with 117 additions and 20 deletions

View File

@ -1,5 +1,5 @@
import axios from 'axios';
import type { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
import type { AxiosInstance, AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
// API 基础配置
// 开发环境使用代理,生产环境使用环境变量
@ -62,6 +62,65 @@ 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}`;
resolve(request(config));
}
});
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 实例
const request: AxiosInstance = axios.create({
baseURL: BASE_URL,
@ -95,12 +154,9 @@ request.interceptors.response.use(
const data = response.data;
// 业务错误处理
if (data.code !== 0) {
// Token 过期
// Token 过期(业务错误码)
if (data.code === 401 || data.code === 1001) {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_info');
localStorage.removeItem('auth-storage');
window.location.href = '/login';
clearAuthAndRedirect();
return Promise.reject(new Error(data.message || '登录已过期'));
}
return Promise.reject(new Error(data.message || '请求失败'));
@ -108,23 +164,64 @@ request.interceptors.response.use(
return data;
},
(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) {
const { status, data } = error.response;
if (status === 401) {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_info');
localStorage.removeItem('auth-storage');
window.location.href = '/login';
const originalConfig = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
// 401: 尝试用 refresh token 刷新
if (status === 401 && originalConfig && !originalConfig._retry) {
const refreshTokenValue = getRefreshToken();
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('网络错误,请检查网络连接'));