- 添加 Accept-Language 请求头支持后端翻译 - 优化账户状态指示器和测试模态框 - 简化用户属性表单和配置模态框 - 新增多个国际化翻译条目 - 重构管理视图代码,提升可维护性
97 lines
2.6 KiB
TypeScript
97 lines
2.6 KiB
TypeScript
/**
|
|
* Axios HTTP Client Configuration
|
|
* Base client with interceptors for authentication and error handling
|
|
*/
|
|
|
|
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios'
|
|
import type { ApiResponse } from '@/types'
|
|
import { getLocale } from '@/i18n'
|
|
|
|
// ==================== Axios Instance Configuration ====================
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api/v1'
|
|
|
|
export const apiClient: AxiosInstance = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
timeout: 30000,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
|
|
// ==================== Request Interceptor ====================
|
|
|
|
apiClient.interceptors.request.use(
|
|
(config: InternalAxiosRequestConfig) => {
|
|
// Attach token from localStorage
|
|
const token = localStorage.getItem('auth_token')
|
|
if (token && config.headers) {
|
|
config.headers.Authorization = `Bearer ${token}`
|
|
}
|
|
|
|
// Attach locale for backend translations
|
|
if (config.headers) {
|
|
config.headers['Accept-Language'] = getLocale()
|
|
}
|
|
|
|
return config
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error)
|
|
}
|
|
)
|
|
|
|
// ==================== Response Interceptor ====================
|
|
|
|
apiClient.interceptors.response.use(
|
|
(response) => {
|
|
// Unwrap standard API response format { code, message, data }
|
|
const apiResponse = response.data as ApiResponse<unknown>
|
|
if (apiResponse && typeof apiResponse === 'object' && 'code' in apiResponse) {
|
|
if (apiResponse.code === 0) {
|
|
// Success - return the data portion
|
|
response.data = apiResponse.data
|
|
} else {
|
|
// API error
|
|
return Promise.reject({
|
|
status: response.status,
|
|
code: apiResponse.code,
|
|
message: apiResponse.message || 'Unknown error'
|
|
})
|
|
}
|
|
}
|
|
return response
|
|
},
|
|
(error: AxiosError<ApiResponse<unknown>>) => {
|
|
// Handle common errors
|
|
if (error.response) {
|
|
const { status, data } = error.response
|
|
|
|
// 401: Unauthorized - clear token and redirect to login
|
|
if (status === 401) {
|
|
localStorage.removeItem('auth_token')
|
|
localStorage.removeItem('auth_user')
|
|
// Only redirect if not already on login page
|
|
if (!window.location.pathname.includes('/login')) {
|
|
window.location.href = '/login'
|
|
}
|
|
}
|
|
|
|
// Return structured error
|
|
return Promise.reject({
|
|
status,
|
|
code: data?.code,
|
|
message: data?.message || error.message
|
|
})
|
|
}
|
|
|
|
// Network error
|
|
return Promise.reject({
|
|
status: 0,
|
|
message: 'Network error. Please check your connection.'
|
|
})
|
|
}
|
|
)
|
|
|
|
export default apiClient
|