266 lines
8.3 KiB
TypeScript
266 lines
8.3 KiB
TypeScript
/* 请求封装 */
|
||
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;
|