/* 请求封装 */ 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'; import { useAccountStore } from '@/stores/user'; const handleError = dealWarning(); import CryptoJS from 'crypto-js'; // 引入加密库 // 加密配置 const sKey = CryptoJS.enc.Utf8.parse('mXzfCSPBKmEA8aLq'); const iv = CryptoJS.enc.Utf8.parse('tbeJJLC6dZQXXtWr'); /** * 解密函数 * @param text 待解密的Base64字符串 */ const $Decrypt = (text) => { if (!text) return text; try { // 处理Base64编码 let src = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Base64.parse(text)); // AES-CBC-PKCS7解密 let bytes = CryptoJS.AES.decrypt(src, sKey, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); let originalText = bytes.toString(CryptoJS.enc.Utf8); // 尝试解析为JSON对象 return JSON.parse(originalText); } catch (e) { console.error('解密失败:', e); return text; // 解密失败返回原始数据 } }; /** * 加密函数 * @param data 待加密的对象或字符串 */ const $Encrypt = (data) => { if (!data) return data; try { // 将数据转换为JSON字符串 const jsonData = typeof data === 'string' ? data : JSON.stringify(data); // AES-CBC-PKCS7加密 const encrypted = CryptoJS.AES.encrypt(jsonData, sKey, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 转换为Base64字符串 return encrypted.toString(); } catch (e) { console.error('加密失败:', e); return data; // 加密失败返回原始数据 } }; /** 设置用户中心相关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; // 是否需要加密请求数据 needEncrypt?: boolean; // 是否需要解密响应数据 needDecrypt?: 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 account = useAccountStore(); const service = axios.create({ baseURL: params.apiType === 'devApi' ? baseDevURL : baseURL, timeout: 15000, headers: { 'DP_TOKEN': account.accountInfo.access_token, 'DP_REFRESH_TOKEN': account.accountInfo.refresh_token, }, 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) => { response.data.data = $Decrypt(response.data.data) 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;