85 lines
2.2 KiB
TypeScript
85 lines
2.2 KiB
TypeScript
import axios, { AxiosError, type InternalAxiosRequestConfig } from 'axios'
|
|
import router from '../router'
|
|
|
|
type RetryableConfig = InternalAxiosRequestConfig & { _retry?: boolean }
|
|
|
|
let isRefreshing = false
|
|
let pendingQueue: Array<(token: string | null) => void> = []
|
|
|
|
function processQueue(token: string | null) {
|
|
pendingQueue.forEach((cb) => cb(token))
|
|
pendingQueue = []
|
|
}
|
|
|
|
const client = axios.create({
|
|
baseURL: '/api/v1',
|
|
timeout: 10000,
|
|
withCredentials: true,
|
|
})
|
|
|
|
client.interceptors.request.use((config) => {
|
|
const token = localStorage.getItem('asset_tracker_token')
|
|
if (token) {
|
|
config.headers = config.headers || {}
|
|
config.headers.Authorization = `Bearer ${token}`
|
|
}
|
|
return config
|
|
})
|
|
|
|
async function refreshAccessToken(): Promise<string> {
|
|
const resp = await axios.post('/api/v1/auth/refresh', {}, { withCredentials: true, timeout: 10000 })
|
|
const token = resp?.data?.access_token || ''
|
|
if (!token) throw new Error('refresh failed')
|
|
localStorage.setItem('asset_tracker_token', token)
|
|
return token
|
|
}
|
|
|
|
function redirectSessionExpired() {
|
|
localStorage.removeItem('asset_tracker_token')
|
|
if (router.currentRoute.value.path !== '/session-expired') {
|
|
router.push('/session-expired')
|
|
}
|
|
}
|
|
|
|
client.interceptors.response.use(
|
|
(resp) => resp,
|
|
async (err: AxiosError) => {
|
|
const status = err?.response?.status
|
|
const original = (err.config || {}) as RetryableConfig
|
|
|
|
if (status !== 401 || original._retry) {
|
|
return Promise.reject(err)
|
|
}
|
|
|
|
original._retry = true
|
|
|
|
if (isRefreshing) {
|
|
return new Promise((resolve, reject) => {
|
|
pendingQueue.push((token) => {
|
|
if (!token) return reject(err)
|
|
original.headers = original.headers || {}
|
|
original.headers.Authorization = `Bearer ${token}`
|
|
resolve(client(original))
|
|
})
|
|
})
|
|
}
|
|
|
|
isRefreshing = true
|
|
try {
|
|
const token = await refreshAccessToken()
|
|
processQueue(token)
|
|
original.headers = original.headers || {}
|
|
original.headers.Authorization = `Bearer ${token}`
|
|
return client(original)
|
|
} catch (e) {
|
|
processQueue(null)
|
|
redirectSessionExpired()
|
|
return Promise.reject(e)
|
|
} finally {
|
|
isRefreshing = false
|
|
}
|
|
},
|
|
)
|
|
|
|
export default client
|