/* 请求封装 */ 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 `${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;