initial commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
102
src/admin/utils/api.ts
Normal file
102
src/admin/utils/api.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
let showSessionExpiredAlert = false
|
||||
let showLogoutAlert = false
|
||||
let getTokenFn: (() => string | null) | null = null
|
||||
let refreshFn: (() => Promise<boolean>) | null = null
|
||||
let refreshPromise: Promise<boolean> | null = null
|
||||
|
||||
export const shouldShowSessionExpiredAlert = (): boolean => {
|
||||
if (showSessionExpiredAlert) {
|
||||
showSessionExpiredAlert = false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const setSessionExpired = (): void => {
|
||||
showSessionExpiredAlert = true
|
||||
}
|
||||
|
||||
export const shouldShowLogoutAlert = (): boolean => {
|
||||
if (showLogoutAlert) {
|
||||
showLogoutAlert = false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const setLogoutAlert = (): void => {
|
||||
showLogoutAlert = true
|
||||
}
|
||||
|
||||
export const setTokenGetter = (fn: () => string | null): void => {
|
||||
getTokenFn = fn
|
||||
}
|
||||
|
||||
export const setRefreshFn = (fn: () => Promise<boolean>): void => {
|
||||
refreshFn = fn
|
||||
}
|
||||
|
||||
export const apiFetch = async (url: string, options: RequestInit = {}): Promise<Response> => {
|
||||
let token: string | null = null
|
||||
try {
|
||||
token = getTokenFn ? getTokenFn() : null
|
||||
} catch {
|
||||
// token retrieval failed
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
...(options.headers as Record<string, string>),
|
||||
}
|
||||
|
||||
if (!headers['Content-Type'] && options.body && !(options.body instanceof FormData)) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
let response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (response.status === 401 && refreshFn) {
|
||||
try {
|
||||
if (!refreshPromise) {
|
||||
refreshPromise = refreshFn().finally(() => {
|
||||
refreshPromise = null
|
||||
})
|
||||
}
|
||||
const refreshed = await refreshPromise
|
||||
if (refreshed) {
|
||||
token = getTokenFn ? getTokenFn() : null
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
})
|
||||
} else {
|
||||
setSessionExpired()
|
||||
}
|
||||
} catch {
|
||||
setSessionExpired()
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
export const getAccessToken = (): string | null => {
|
||||
try {
|
||||
return getTokenFn ? getTokenFn() : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default apiFetch
|
||||
Reference in New Issue
Block a user