Files
client-web/src/api/overseasHttp.ts
T

68 lines
2.9 KiB
TypeScript

import axios, { type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'
import { parseApiJsonResponse } from '@chongchong/shared'
import { ApiError, isApiEnvelope, isApiFailure } from '@/api/types'
import { getOverseasAccessToken } from '@/api/session'
import { refreshAuthenticatedSession } from '@/api/http'
interface RetryableRequest extends InternalAxiosRequestConfig { _retriedOverseasAuth?: boolean }
const transport = axios.create({
baseURL: import.meta.env.VITE_OVERSEAS_API_BASE_URL || '/overseas-api',
timeout: 15_000,
transformResponse: [(data: unknown) => parseApiJsonResponse(data)],
})
async function requireToken() {
const current = getOverseasAccessToken()
if (current) return current
await refreshAuthenticatedSession()
const refreshed = getOverseasAccessToken()
if (!refreshed) throw new ApiError('当前环境未签发海外访问令牌', 'OVERSEAS_TOKEN_UNAVAILABLE')
return refreshed
}
transport.interceptors.request.use(async (config) => {
config.headers.Authorization = `Bearer ${await requireToken()}`
return config
})
transport.interceptors.response.use((response) => response, async (error) => {
const request = error.config as RetryableRequest | undefined
if (error.response?.status !== 401 || !request || request._retriedOverseasAuth) throw error
request._retriedOverseasAuth = true
await refreshAuthenticatedSession()
request.headers.Authorization = `Bearer ${await requireToken()}`
return transport.request(request)
})
export async function overseasApiRequest<T>(config: AxiosRequestConfig) {
try {
const response = await transport.request<unknown>(config)
if (isApiFailure(response.data)) {
throw new ApiError(response.data.message, response.data.code, response.status, undefined,
response.data.traceId, response.data.requestId)
}
if (!isApiEnvelope(response.data) || response.data.code !== '200') {
throw new ApiError(
isApiEnvelope(response.data) ? response.data.message : '海外接口响应格式错误',
isApiEnvelope(response.data) ? response.data.code : 'INVALID_RESPONSE',
undefined,
undefined,
isApiEnvelope(response.data) ? response.data.traceId : null,
isApiEnvelope(response.data) ? response.data.requestId : null,
)
}
return response.data.data as T
} catch (error) {
if (error instanceof ApiError) throw error
if (axios.isAxiosError(error)) {
const body = error.response?.data
if (isApiEnvelope(body) || isApiFailure(body)) {
throw new ApiError(body.message, body.code, error.response?.status, undefined, body.traceId, body.requestId)
}
throw new ApiError(error.code === 'ERR_NETWORK' ? '无法连接海外服务' : error.message, error.code || 'OVERSEAS_HTTP_ERROR', error.response?.status)
}
throw new ApiError(error instanceof Error ? error.message : '海外接口请求失败')
}
}