import axios from 'axios'
|
import { useUserStore } from '@/store/modules/user'
|
import { ApiStatus } from './status'
|
import { HttpError, handleError, showError, showSuccess } from './error'
|
import { $t } from '@/locales'
|
const REQUEST_TIMEOUT = 15e3
|
const LOGOUT_DELAY = 500
|
const MAX_RETRIES = 0
|
const RETRY_DELAY = 1e3
|
const UNAUTHORIZED_DEBOUNCE_TIME = 3e3
|
let isUnauthorizedErrorShown = false
|
let unauthorizedTimer = null
|
const { VITE_API_URL, VITE_WITH_CREDENTIALS } = import.meta.env
|
const axiosInstance = axios.create({
|
timeout: REQUEST_TIMEOUT,
|
baseURL: VITE_API_URL,
|
withCredentials: VITE_WITH_CREDENTIALS === 'true',
|
validateStatus: (status) => status >= 200 && status < 300,
|
transformResponse: [
|
(data, headers) => {
|
const contentType = headers['content-type']
|
if (contentType?.includes('application/json')) {
|
try {
|
return JSON.parse(data)
|
} catch {
|
return data
|
}
|
}
|
return data
|
}
|
]
|
})
|
axiosInstance.interceptors.request.use(
|
(request2) => {
|
const { accessToken } = useUserStore()
|
if (accessToken) request2.headers.set('Authorization', accessToken)
|
if (
|
request2.data &&
|
!(request2.data instanceof FormData) &&
|
!request2.headers['Content-Type']
|
) {
|
request2.headers.set('Content-Type', 'application/json')
|
request2.data = JSON.stringify(request2.data)
|
}
|
return request2
|
},
|
(error) => {
|
showError(createHttpError($t('httpMsg.requestConfigError'), ApiStatus.error))
|
return Promise.reject(error)
|
}
|
)
|
axiosInstance.interceptors.response.use(
|
(response) => {
|
const { code, msg } = response.data
|
if (code === ApiStatus.success) return response
|
if (code === ApiStatus.unauthorized) handleUnauthorizedError(msg)
|
throw createHttpError(msg || $t('httpMsg.requestFailed'), code)
|
},
|
(error) => {
|
if (error.response?.status === ApiStatus.unauthorized) handleUnauthorizedError()
|
return Promise.reject(handleError(error))
|
}
|
)
|
function createHttpError(message, code) {
|
return new HttpError(message, code)
|
}
|
function handleUnauthorizedError(message) {
|
const error = createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
if (!isUnauthorizedErrorShown) {
|
isUnauthorizedErrorShown = true
|
logOut()
|
unauthorizedTimer = setTimeout(resetUnauthorizedError, UNAUTHORIZED_DEBOUNCE_TIME)
|
showError(error, true)
|
throw error
|
}
|
throw error
|
}
|
function resetUnauthorizedError() {
|
isUnauthorizedErrorShown = false
|
if (unauthorizedTimer) clearTimeout(unauthorizedTimer)
|
unauthorizedTimer = null
|
}
|
function logOut() {
|
setTimeout(() => {
|
useUserStore().logOut()
|
}, LOGOUT_DELAY)
|
}
|
function shouldRetry(statusCode) {
|
return [
|
ApiStatus.requestTimeout,
|
ApiStatus.internalServerError,
|
ApiStatus.badGateway,
|
ApiStatus.serviceUnavailable,
|
ApiStatus.gatewayTimeout
|
].includes(statusCode)
|
}
|
async function retryRequest(config, retries = MAX_RETRIES) {
|
try {
|
return await request(config)
|
} catch (error) {
|
if (retries > 0 && error instanceof HttpError && shouldRetry(error.code)) {
|
await delay(RETRY_DELAY)
|
return retryRequest(config, retries - 1)
|
}
|
throw error
|
}
|
}
|
function delay(ms) {
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
}
|
async function request(config) {
|
if (
|
['POST', 'PUT'].includes(config.method?.toUpperCase() || '') &&
|
config.params &&
|
!config.data
|
) {
|
config.data = config.params
|
config.params = void 0
|
}
|
try {
|
const res = await axiosInstance.request(config)
|
if (config.showSuccessMessage && res.data.msg) {
|
showSuccess(res.data.msg)
|
}
|
return res.data.data
|
} catch (error) {
|
if (error instanceof HttpError && error.code !== ApiStatus.unauthorized) {
|
const showMsg = config.showErrorMessage !== false
|
showError(error, showMsg)
|
}
|
return Promise.reject(error)
|
}
|
}
|
const api = {
|
get(config) {
|
return retryRequest({ ...config, method: 'GET' })
|
},
|
post(config) {
|
return retryRequest({ ...config, method: 'POST' })
|
},
|
put(config) {
|
return retryRequest({ ...config, method: 'PUT' })
|
},
|
del(config) {
|
return retryRequest({ ...config, method: 'DELETE' })
|
},
|
request(config) {
|
return retryRequest(config)
|
}
|
}
|
|
export default api
|