Files
admin-web/src/shared/api/overseasHttp.ts
T

78 lines
2.4 KiB
TypeScript
Raw Normal View History

2026-08-20 09:10:36 +08:00
import axios, { type InternalAxiosRequestConfig } from 'axios'
import { parseApiJsonResponse } from '@chongchong/shared'
import { refreshAuthenticatedSession } from './http'
interface CachedOverseasToken {
accessToken: string
refreshAfter: number
}
type RetryableOverseasRequestConfig = InternalAxiosRequestConfig & { _overseasAuthRetry?: boolean }
const TOKEN_REFRESH_SAFETY_WINDOW_MS = 30_000
let cachedToken: CachedOverseasToken | null = null
export const overseasHttp = axios.create({
baseURL: import.meta.env.VITE_OVERSEAS_API_BASE_URL || '/overseas-api',
timeout: 15_000,
transformResponse: [(data: unknown) => parseApiJsonResponse(data)],
})
function currentOverseasAccessToken() {
return cachedToken && cachedToken.refreshAfter > Date.now() ? cachedToken.accessToken : ''
}
async function requireOverseasAccessToken() {
const current = currentOverseasAccessToken()
if (current) return current
await refreshAuthenticatedSession()
const refreshed = currentOverseasAccessToken()
if (!refreshed) throw new Error('当前环境未签发海外访问令牌')
return refreshed
}
export function replaceOverseasAccessToken(accessToken: string | null, expiresIn: number) {
if (!accessToken && expiresIn === 0) {
cachedToken = null
return
}
if (!accessToken || !Number.isFinite(expiresIn) || expiresIn <= 0) {
throw new Error('海外访问令牌响应无效')
}
cachedToken = {
accessToken,
refreshAfter: Date.now() + Math.max(0, expiresIn * 1000 - TOKEN_REFRESH_SAFETY_WINDOW_MS),
}
}
export function clearOverseasAccessToken() {
cachedToken = null
}
overseasHttp.interceptors.request.use(async (config) => {
const accessToken = await requireOverseasAccessToken()
config.headers.Authorization = `Bearer ${accessToken}`
return config
})
overseasHttp.interceptors.response.use(
(response) => response.data,
async (error) => {
const config = error.config as RetryableOverseasRequestConfig | undefined
if (error.response?.status !== 401 || !config || config._overseasAuthRetry) {
return Promise.reject(error)
}
config._overseasAuthRetry = true
clearOverseasAccessToken()
try {
await refreshAuthenticatedSession()
const accessToken = await requireOverseasAccessToken()
config.headers.Authorization = `Bearer ${accessToken}`
return overseasHttp.request(config)
} catch {
return Promise.reject(error)
}
},
)