This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
const API_BASE_URL = String(import.meta.env.VITE_API_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
let accessToken = ''
|
||||
let refreshHandler = null
|
||||
|
||||
export function setAccessToken(token) {
|
||||
accessToken = token || ''
|
||||
}
|
||||
|
||||
export function clearAccessToken() {
|
||||
accessToken = ''
|
||||
}
|
||||
|
||||
export function setRefreshHandler(handler) {
|
||||
refreshHandler = handler
|
||||
}
|
||||
|
||||
function buildUrl(path) {
|
||||
if (!API_BASE_URL) return path
|
||||
if (/^https?:\/\//i.test(path)) return path
|
||||
return `${API_BASE_URL}${path.startsWith('/') ? '' : '/'}${path}`
|
||||
}
|
||||
|
||||
async function readBody(res) {
|
||||
if (res.status === 204) return null
|
||||
const contentType = res.headers.get('content-type') || ''
|
||||
if (contentType.includes('application/json')) return res.json()
|
||||
return res.text()
|
||||
}
|
||||
|
||||
function normalizeError(data) {
|
||||
if (!data) return new Error('Request failed')
|
||||
if (typeof data === 'string') return new Error(data)
|
||||
if (typeof data?.message === 'string') return new Error(data.message)
|
||||
return new Error('Request failed')
|
||||
}
|
||||
|
||||
async function tryRefresh() {
|
||||
if (!refreshHandler) return false
|
||||
try {
|
||||
const token = await refreshHandler()
|
||||
if (!token) {
|
||||
clearAccessToken()
|
||||
return false
|
||||
}
|
||||
setAccessToken(token)
|
||||
return true
|
||||
} catch {
|
||||
clearAccessToken()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiRequest(
|
||||
path,
|
||||
{ method = 'GET', body, headers = {}, skipAuth = false, skipRefresh = false } = {},
|
||||
) {
|
||||
const url = buildUrl(path)
|
||||
|
||||
const finalHeaders = new Headers(headers)
|
||||
if (!skipAuth && accessToken) {
|
||||
finalHeaders.set('Authorization', `Bearer ${accessToken}`)
|
||||
}
|
||||
|
||||
let finalBody = body
|
||||
if (body && !(body instanceof FormData) && typeof body === 'object') {
|
||||
finalHeaders.set('Content-Type', 'application/json')
|
||||
finalBody = JSON.stringify(body)
|
||||
}
|
||||
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: finalHeaders,
|
||||
body: finalBody,
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (res.status === 401 && !skipRefresh) {
|
||||
const refreshed = await tryRefresh()
|
||||
if (refreshed) {
|
||||
return apiRequest(path, { method, body, headers, skipAuth, skipRefresh: true })
|
||||
}
|
||||
}
|
||||
|
||||
const data = await readBody(res)
|
||||
|
||||
if (!res.ok) {
|
||||
throw normalizeError(data)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
Reference in New Issue
Block a user