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) } }, )