Smail/frontend/src/utils/axios.js
smail b2bfa09506
Some checks are pending
Security Checks / security (push) Waiting to run
修复会话失效后设置面板残留并补录降级验收
2026-09-26 01:54:06 +08:00

151 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import axios from 'axios'
import {useGlobalStatusStore} from '@/stores/useGlobalStatusStore';
import lang from '../i18n/i18n';
import {router} from "@/router";
import {createLoginRedirectHandler} from "@/utils/navigation.mjs";
import { decodeBlobAPIError } from './apiResponse.mjs'
//创建axios的一个实例
const http = axios.create({
baseURL: import.meta.env.VITE_APP_URL, //接口统一域名
timeout: 60000,
// 初始化授权和登录会话都依赖服务端 Cookie;开发代理或反向代理跨源时也必须携带 Cookie。
withCredentials: true,
xsrfCookieName: 'smail_csrf',
xsrfHeaderName: 'X-CSRF-Token',
withXSRFToken: true, //设置超时
headers: {
'Lang': lang.lang
}
});
let redirectToLogin = null;
function handleLoginRedirect() {
const currentRoute = router.currentRoute.value;
if (currentRoute?.name === 'login' || currentRoute?.name === 'login-verification') {
return Promise.resolve(false);
}
useGlobalStatusStore().clear();
if (!redirectToLogin) {
redirectToLogin = createLoginRedirectHandler(router);
}
return redirectToLogin();
}
//响应拦截器
function attachApiErrorDetails(error) {
const payload = error?.response?.data
const details = payload?.data
if (payload && typeof payload === 'object') {
error.apiErrorCode = typeof details?.code === 'string' ? details.code : ''
error.apiRequestId = typeof details?.requestId === 'string' ? details.requestId : ''
error.apiErrorFields = details?.fields && typeof details.fields === 'object' ? details.fields : {}
if (typeof payload.errorMsg === 'string' && payload.errorMsg.trim()) {
error.message = payload.errorMsg
}
}
return error
}
function getRequestPath(config) {
const rawUrl = String(config?.url || '').split('?')[0]
try {
const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'
return new URL(rawUrl, config?.baseURL || origin).pathname
} catch (error) {
return rawUrl
}
}
function isSetupRequest(config) {
const path = getRequestPath(config)
return path === '/api/setup' || path === '/api/v1/setup' || path === '/__smail_setup_bootstrap'
}
function isAuthenticationRefreshRequest(config) {
const path = getRequestPath(config)
return path === '/api/user/info' || path === '/api/v1/user/info'
}
function isAuthenticationFailureError(error) {
const errorCode = error?.response?.data?.data?.code
if (errorCode === 'RECENT_AUTH_REQUIRED') return false
return error?.response?.data?.errorNo === 403
|| (error?.response?.status === 403 && isAuthenticationRefreshRequest(error.config))
}
http.interceptors.response.use(async (response) => {
//响应成功
await decodeBlobAPIError(response)
if (response.data?.errorNo === 403
&& response.data?.data?.code !== 'RECENT_AUTH_REQUIRED'
&& !isSetupRequest(response.config)
&& !isAuthenticationRefreshRequest(response.config)) {
await handleLoginRedirect();
}
return response.data;
}, async (error) => {
//响应错误
await decodeBlobAPIError(error.response)
if (error.response && error.response.status) {
let message = ""
switch (error.response.status) {
case 400:
message = '请求错误';
break;
case 401:
message = '请求错误';
break;
case 403:
if (isSetupRequest(error.config)) {
message = lang.setup_failed
} else if (isAuthenticationRefreshRequest(error.config)) {
message = '登录状态已失效'
} else if (isAuthenticationFailureError(error)) {
await handleLoginRedirect();
} else {
message = '无权访问'
}
break;
case 404:
message = '请求地址出错';
break;
case 408:
message = '请求超时';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '网关超时!';
break;
case 505:
message = 'HTTP版本不受支持';
break;
default:
// eslint-disable-next-line no-unused-vars
message = '请求失败';
}
const enrichedError = attachApiErrorDetails(error)
if (message && !enrichedError.response?.data?.errorMsg) {
enrichedError.message = message
}
return Promise.reject(enrichedError)
}
return Promise.reject(attachApiErrorDetails(error));
});
export {http};