chore: 初始化仓库基线(AGENTS.md、git 规范、敏感文件排除)

This commit is contained in:
weijuesen
2026-08-10 22:30:53 +08:00
commit 84abf4454c
358 changed files with 75993 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import axios from 'axios';
import type { AxiosError, AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';
import { message } from 'antd';
export const TOKEN_KEY = 'silkworm_access_token';
export const REFRESH_TOKEN_KEY = 'silkworm_refresh_token';
export interface RequestConfig extends AxiosRequestConfig {
silent?: boolean;
/** 内部标记:本次请求是 refresh 重试,避免无限循环 */
_retried?: boolean;
}
export const http: AxiosInstance = axios.create({
baseURL: '/api/v1',
timeout: 15000,
});
http.interceptors.request.use((config) => {
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
config.headers = config.headers ?? {};
(config.headers as Record<string, string>)['Authorization'] = `Bearer ${token}`;
}
return config;
});
let refreshing: Promise<string | null> | null = null;
const refreshToken = async (): Promise<string | null> => {
const refresh = localStorage.getItem(REFRESH_TOKEN_KEY);
if (!refresh) return null;
try {
const res = await axios.post('/api/v1/auth/refresh', { refreshToken: refresh });
const { accessToken, refreshToken: newRefresh } = res.data || {};
if (!accessToken) return null;
localStorage.setItem(TOKEN_KEY, accessToken);
if (newRefresh) localStorage.setItem(REFRESH_TOKEN_KEY, newRefresh);
return accessToken;
} catch {
return null;
}
};
const clearAuthAndRedirect = () => {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
if (location.pathname !== '/login') {
message.warning('登录已过期,请重新登录');
location.replace('/login');
}
};
http.interceptors.response.use(
(res) => res,
async (err: AxiosError) => {
const status = err.response?.status;
const cfg = err.config as RequestConfig | undefined;
// 访问令牌过期:尝试用 refresh 令牌换新后重试一次
if (status === 401 && cfg && !cfg._retried && !cfg.url?.includes('/auth/')) {
cfg._retried = true;
refreshing = refreshing ?? refreshToken();
const newToken = await refreshing;
refreshing = null;
if (newToken) {
cfg.headers = cfg.headers ?? ({} as InternalAxiosRequestConfig['headers']);
(cfg.headers as Record<string, string>)['Authorization'] = `Bearer ${newToken}`;
return http.request(cfg);
}
clearAuthAndRedirect();
} else if (status === 401) {
clearAuthAndRedirect();
} else if (!cfg?.silent) {
const msg =
(err.response?.data as any)?.error ||
(err.response?.data as any)?.message ||
(err.response?.data as any)?.msg ||
err.message ||
'请求失败';
message.error(typeof msg === 'string' ? msg : '请求失败');
}
return Promise.reject(err);
},
);
export const request = <T = unknown>(config: RequestConfig): Promise<T> =>
http.request<T>(config).then((r) => r.data);
export const get = <T = unknown>(url: string, config?: RequestConfig) =>
request<T>({ ...config, method: 'GET', url });
export const post = <T = unknown>(url: string, data?: unknown, config?: RequestConfig) =>
request<T>({ ...config, method: 'POST', url, data });
export const put = <T = unknown>(url: string, data?: unknown, config?: RequestConfig) =>
request<T>({ ...config, method: 'PUT', url, data });
export const patch = <T = unknown>(url: string, data?: unknown, config?: RequestConfig) =>
request<T>({ ...config, method: 'PATCH', url, data });
export const del = <T = unknown>(url: string, config?: RequestConfig) =>
request<T>({ ...config, method: 'DELETE', url });