cangku/frontend/src/api/request.js

118 lines
3.3 KiB
JavaScript

import axios from 'axios';
import { message } from 'antd';
import { getRequestErrorMessage, isAuthSessionFailure } from './authSessionPolicy';
import { requestDesktopSessionRenewal } from '../utils/desktopEmbedSession';
const DEFAULT_REQUEST_TIMEOUT_MS = Math.max(
Number(import.meta.env.VITE_API_REQUEST_TIMEOUT_MS) || 120000,
15000
);
const request = axios.create({
baseURL: '', // Proxy handles the base URL
timeout: DEFAULT_REQUEST_TIMEOUT_MS,
});
const AUTH_USER_STORAGE_KEY = 'auth_user';
function clearAuthSession() {
localStorage.removeItem('token');
localStorage.removeItem(AUTH_USER_STORAGE_KEY);
}
function parseJwtPayload(token) {
if (!token) {
return null;
}
try {
const [, payload] = token.split('.');
if (!payload) {
return null;
}
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
const padded = normalized.padEnd(normalized.length + ((4 - normalized.length % 4) % 4), '=');
return JSON.parse(window.atob(padded));
} catch {
return null;
}
}
function isLocalPreviewSession() {
const isLocalHost = ['localhost', '127.0.0.1'].includes(window.location.hostname);
if (!isLocalHost) {
return false;
}
const payload = parseJwtPayload(localStorage.getItem('token'));
return payload?.authSource === 'local-preview';
}
function isPublicAuthPage() {
return ['/login', '/callback', '/install'].includes(window.location.pathname);
}
function shouldBypass401Redirect(error) {
const url = error?.config?.url || '';
return url.includes('/api/auth/verify') || isLocalPreviewSession();
}
// Request interceptor
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response interceptor
request.interceptors.response.use(
(response) => {
const res = response.data;
// Compatible with backend interfaces that don't return code
if (res && res.code && res.code !== 200) {
const shouldBypassRedirect = res.code === 401 && shouldBypass401Redirect(response);
if (!shouldBypassRedirect && !response.config?.silentError) {
message.error(res.message || 'Error');
}
if (res.code === 401 && !shouldBypassRedirect) {
// Handle unauthorized - 但如果已在登录页则不重定向
clearAuthSession();
if (!isPublicAuthPage()) {
if (!requestDesktopSessionRenewal('api-unauthorized')) {
window.location.href = '/login';
}
}
}
return Promise.reject(new Error(res.message || 'Error'));
}
return res;
},
(error) => {
const authSessionFailure = isAuthSessionFailure(error.response);
if (!authSessionFailure && !error.config?.silentError) {
message.error(getRequestErrorMessage(error));
}
if (authSessionFailure && !shouldBypass401Redirect(error)) {
clearAuthSession();
// 如果已在登录页则不重定向,避免无限循环
if (!isPublicAuthPage()) {
if (!requestDesktopSessionRenewal('api-unauthorized')) {
window.location.href = '/login';
}
}
}
return Promise.reject(error);
}
);
export default request;