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/')) {
|
|
|
|
|
|
// 用户信息相关
|
2025-03-17 19:25:02 +08:00
|
|
|
|
return `${url}`;
|
2025-03-12 18:41:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
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;
|