Files
Situation-Awareness-Platfor…/src/utils/request.ts

195 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-03-12 18:41:20 +08:00
/* 请求封装 */
import axios, { type AxiosRequestConfig } from 'axios';
import { dealWarning } from './status';
import router from '@/router';
import { repoInfoStore } from '@/stores/Repo';
import { useGlobalInfoStore } from '@/stores/Global';
import { cutParamsInUrl, getSignUtmSource } from './index';
import { Message } from 'vue-devui/message';
import degradeInterceptor from './degradeInterceptor';
const handleError = dealWarning();
/** 设置用户中心相关api迁移时的api前缀 */
const setPassportPrefix = (url: string, method: string) => {
const prefix = (import.meta as any).env.VITE_PASSPORT_PREFIX;
if (prefix) {
if (url?.includes('/api/v1/user/')) {
// 用户信息相关
return `${prefix}${url}`;
}
if (url?.includes('/api/v1/oauth/')) {
// 用户权限
return `${prefix}${url}`;
}
if (url?.includes('/api/v1/internal/messages')) {
// 消息通知
return `${prefix}${url}`;
}
if (url?.includes('/api/v1/follow')) {
// 关注状态
return `${prefix}${url}`;
}
if (url?.includes('/api/v1/obs') && method === 'get') {
// 图片相关
return `${prefix}${url}`;
}
}
return url;
};
export const baseURL = (import.meta as any).env.VITE_API_HOST;
export const baseDevURL = (import.meta as any).env.VITE_DEV_API_HOST;
type CustomConfigs = {
// 是否使用自定义的错误处理
customError?: boolean;
};
interface PendingTask {
config: AxiosRequestConfig;
resolve: Function;
}
let refreshing = false;
const queue: PendingTask[] = [];
const refreshToken = async () => axios.request({
url: baseURL + `${(import.meta as any).env.VITE_PASSPORT_PREFIX || ''}` + '/api/v1/user/token/refresh',
method: 'post',
data: {
refresh_token: localStorage.getItem('refresh_token')
},
headers: {
Authorization: 'Bearer ' + localStorage.getItem('access_token'),
'content-type': 'application/x-www-form-urlencoded'
},
withCredentials: true
});
const proxyService = (params: any, customConfigs?: CustomConfigs) => {
const service = axios.create({
baseURL: params.apiType === 'devApi' ? baseDevURL : baseURL,
timeout: 15000,
withCredentials: true
});
/** user api相关请求白名单 */
const whiteList = ['/login', 'login/mobile', '/sendVeriCode', '/register', '/quickLogin', '/quickRegister', '/forgetCode', '/forgetPassword', '/checkSameUser', '/token'];
const notAuthorization = (url: string = '') => {
return whiteList.some((str) => url.includes(str));
};
// request 拦截器
service.interceptors.request.use(
(config) => {
// 配置请求头
const globalStore = useGlobalInfoStore();
const repoStore = repoInfoStore();
const currentRoute = router?.currentRoute?.value;
const repoId = currentRoute?.meta?.type === 'repo' ? repoStore.repoInfo?.id : undefined;
const title = config.headers['homeweb-page-title'] || currentRoute?.meta?.reportTitle;
delete config.headers['homeweb-page-title'];
if (title && params.apiType !== 'devApi') {
// devpress接口不需要上报
config.headers['page-title'] = encodeURIComponent(title);
}
if (currentRoute.name === 'homepage' && params.apiType !== 'devApi') {
const homeTitleConfig = {
0: '个人主页',
1: '组织主页'
};
const pageTitle = homeTitleConfig[globalStore.namespaceType as 0 | 1];
config.headers['page-title'] = encodeURIComponent(pageTitle);
}
if (repoId && params.apiType !== 'devApi') {
config.headers['page-repo-id'] = repoId;
}
if (params.apiType !== 'devApi') {
config.headers['page-ref'] = encodeURIComponent(cutParamsInUrl(window.page_ref || ''));
config.headers['page-uri'] = encodeURIComponent(cutParamsInUrl(window.location.href || ''));
config.headers['gitcode-utm-source'] = encodeURIComponent(getSignUtmSource());
}
const access_token = window.localStorage.getItem('access_token');
const { url } = config;
if (access_token && !notAuthorization(url)) {
config.headers.Authorization = `Bearer ${access_token}`;
}
/** 修改用户模块前缀名称 */
config.url = setPassportPrefix(config.url as string, config.method as string);
return config;
},
(error) => {
return Promise.reject(error);
}
);
service.interceptors.request.use(degradeInterceptor.onRequestFulfilled, degradeInterceptor.onRequestRejected);
// response 拦截器
service.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const { response } = error;
const { config } = response || {};
if (refreshing) {
return new Promise((resolve) => {
queue.unshift({
config,
resolve
});
});
}
if (response?.status === 401 && localStorage.getItem('access_token') && !config?.url.includes('/token/refresh')) {
refreshing = true;
try {
const res = await refreshToken();
refreshing = false;
if (res.status === 200) {
const { access_token, refresh_token } = res.data;
localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token);
queue.forEach(({ config, resolve }) => {
if (config.headers) {
config.headers.Authorization = `Bearer ${access_token}`;
}
resolve(axios(config));
});
queue.splice(0);
config.headers.Authorization = `Bearer ${access_token}`;
return axios(config);
} else {
handleError({}, true);
return Promise.reject(res.data);
}
} catch (e) {
handleError({}, true);
}
} else if (response) {
// 请求已发出但是不在2xx的范围
// showMessage(response);
response.data.http_status = response.status;
if (!customConfigs || !customConfigs.customError) {
handleError(response);
}
return Promise.reject(response.data);
} else {
// 网络连接超时
const timeoutRegx = /\btimeout\b/;
if (error?.message && timeoutRegx.test(error.message)) {
Message.error('网络连接超时');
}
return Promise.reject(error);
}
}
);
service.interceptors.response.use(degradeInterceptor.onResponseFulfilled, degradeInterceptor.onResponseRejected);
return service(params);
};
export default proxyService;