96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
import axios, { AxiosError, type AxiosInstance, type AxiosRequestConfig } from 'axios'
|
||
import { Device, Language, type ApiRequest, type ApiResponse, type ErrorResponse } from '@/types/api'
|
||
import { useAuthStore } from '@/stores/auth'
|
||
|
||
// 根据环境选择基础地址
|
||
// Docker 开发环境:Vite proxy 会将 /api 转发到后端
|
||
// 生产环境:Nginx 代理 /api 到后端网关
|
||
const baseURL = import.meta.env.DEV ? '/' : '/api'
|
||
|
||
const client: AxiosInstance = axios.create({
|
||
baseURL,
|
||
timeout: 30000,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
// 请求拦截器:注入 JWT
|
||
client.interceptors.request.use((config) => {
|
||
const token = useAuthStore.getState().token
|
||
if (token && config.headers) {
|
||
config.headers.Authorization = `Bearer ${token}`
|
||
}
|
||
return config
|
||
})
|
||
|
||
// 响应拦截器:统一错误处理
|
||
client.interceptors.response.use(
|
||
(response) => response,
|
||
(error: AxiosError<ErrorResponse>) => {
|
||
const status = error.response?.status
|
||
const message = error.response?.data?.message || error.message || '请求失败'
|
||
|
||
if (status === 401) {
|
||
useAuthStore.getState().logout()
|
||
window.location.href = '/login'
|
||
}
|
||
|
||
return Promise.reject(new Error(message))
|
||
}
|
||
)
|
||
|
||
/** 包装业务请求(注册/业务类接口) */
|
||
export async function apiPost<TReq, TRes>(
|
||
url: string,
|
||
data: TReq,
|
||
config?: AxiosRequestConfig
|
||
): Promise<TRes> {
|
||
const body: ApiRequest<TReq> = {
|
||
device: Device.Web,
|
||
language: Language.SimplifiedChinese,
|
||
data,
|
||
}
|
||
|
||
const response = await client.post<ApiResponse<TRes>>(url, body, config)
|
||
const result = response.data
|
||
|
||
if (!result.success) {
|
||
throw new Error(result.message)
|
||
}
|
||
|
||
if (result.data === null) {
|
||
throw new Error('接口返回数据为空')
|
||
}
|
||
|
||
return result.data
|
||
}
|
||
|
||
/** 原始 POST(登录/认证类接口,不包装) */
|
||
export async function rawPost<TReq, TRes>(
|
||
url: string,
|
||
data: TReq,
|
||
config?: AxiosRequestConfig
|
||
): Promise<TRes> {
|
||
const response = await client.post<TRes>(url, data, config)
|
||
return response.data
|
||
}
|
||
|
||
/** 通用 GET */
|
||
export async function apiGet<TRes>(url: string, config?: AxiosRequestConfig): Promise<TRes> {
|
||
const response = await client.get<ApiResponse<TRes>>(url, config)
|
||
const result = response.data
|
||
|
||
if (!result.success) {
|
||
throw new Error(result.message)
|
||
}
|
||
|
||
if (result.data === null) {
|
||
throw new Error('接口返回数据为空')
|
||
}
|
||
|
||
return result.data
|
||
}
|
||
|
||
export default client
|