Files
ai-ticket-platform/frontend/src/lib/api.ts

74 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '/api'
export function apiUrl(path: string) {
return `${API_BASE_URL}${path}`
}
let csrfToken: string | null = null
export class ApiError extends Error {
status: number
payload: unknown
constructor(status: number, message: string, payload: unknown) {
super(message)
this.name = 'ApiError'
this.status = status
this.payload = payload
}
}
async function readError(response: Response) {
let payload: unknown
try {
payload = await response.json()
} catch {
payload = null
}
const detail = payload && typeof payload === 'object' && 'detail' in payload
? String((payload as { detail: unknown }).detail)
: `API 请求失败(${response.status})`
return new ApiError(response.status, detail, payload)
}
async function getCsrfToken() {
if (!csrfToken) {
const response = await fetch(apiUrl('/accounts/auth/csrf'), {
credentials: 'include'
})
if (!response.ok) {
throw await readError(response)
}
const data = (await response.json()) as { csrfToken: string }
csrfToken = data.csrfToken
}
return csrfToken
}
export async function apiRequest<T>(path: string, init?: RequestInit): Promise<T> {
const headers = new Headers(init?.headers)
if (init?.body && !(init.body instanceof FormData) && !headers.has('content-type')) {
headers.set('content-type', 'application/json')
}
const method = init?.method?.toUpperCase() ?? 'GET'
if (!['GET', 'HEAD', 'OPTIONS'].includes(method) && !headers.has('x-csrftoken')) {
headers.set('x-csrftoken', await getCsrfToken())
}
const response = await fetch(apiUrl(path), {
...init,
credentials: 'include',
headers
})
if (!response.ok) {
throw await readError(response)
}
if (response.status === 204) {
return undefined as T
}
return response.json() as Promise<T>
}