utp_service/frontend/src/api/client.ts
2026-04-08 23:20:01 +03:00

63 lines
1.7 KiB
TypeScript

import axios from 'axios'
import { useAuthStore } from '../store/auth'
export const api = axios.create({
baseURL: '/',
paramsSerializer: (params) => {
const sp = new URLSearchParams()
for (const [key, val] of Object.entries(params)) {
if (val === undefined || val === null) continue
if (Array.isArray(val)) {
val.forEach((v) => sp.append(key, String(v)))
} else {
sp.append(key, String(val))
}
}
return sp.toString()
},
})
// Inject access token on every request
api.interceptors.request.use((config) => {
const token = useAuthStore.getState().accessToken
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
// On 401 — try refresh once, then logout
let refreshing: Promise<string> | null = null
api.interceptors.response.use(
(r) => r,
async (error) => {
const original = error.config
if (error.response?.status !== 401 || original._retried) {
return Promise.reject(error)
}
original._retried = true
const { refreshToken, setTokens, clearTokens, username } = useAuthStore.getState()
if (!refreshToken) {
clearTokens()
return Promise.reject(error)
}
if (!refreshing) {
refreshing = axios
.post('/api/v1/auth/refresh', { refresh_token: refreshToken })
.then((r) => {
setTokens(r.data.access_token, r.data.refresh_token, username ?? '')
return r.data.access_token
})
.catch(() => {
clearTokens()
throw new Error('Session expired')
})
.finally(() => { refreshing = null })
}
const newToken = await refreshing
original.headers.Authorization = `Bearer ${newToken}`
return api(original)
}
)