68 lines
2.9 KiB
TypeScript
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 : '海外接口请求失败')
|
|
}
|
|
}
|