43 lines
2.1 KiB
TypeScript
43 lines
2.1 KiB
TypeScript
let csrfToken: string | null = null
|
|
|
|
export class ApiError extends Error {
|
|
constructor(public readonly status: number, message: string) { super(message) }
|
|
}
|
|
|
|
export function clearSessionToken() { csrfToken = null }
|
|
|
|
async function request(path: string, init: RequestInit, protect: boolean, accept: string): Promise<Response> {
|
|
if (protect && !csrfToken) await apiRequest('/api/auth/me')
|
|
const headers = new Headers(init.headers)
|
|
headers.set('Accept', accept)
|
|
if (init.body) headers.set('Content-Type', 'application/json')
|
|
if (protect && csrfToken) headers.set('X-CSRF-Token', csrfToken)
|
|
const response = await fetch(path, { ...init, credentials: 'include', headers })
|
|
if (!response.ok) {
|
|
if (response.status === 401) clearSessionToken()
|
|
const body: unknown = await response.json().catch(() => null)
|
|
const detail = body && typeof body === 'object' && 'detail' in body ? body.detail : null
|
|
throw new ApiError(response.status, typeof detail === 'string' ? detail : `请求失败(${response.status}),请检查登录状态或填写内容。`)
|
|
}
|
|
return response
|
|
}
|
|
|
|
export async function apiRequest<T>(path: string, init: RequestInit = {}, protect = false): Promise<T> {
|
|
const response = await request(path, init, protect, 'application/json')
|
|
if (response.status === 204) return undefined as T
|
|
const data = await response.json()
|
|
if (path === '/api/auth/login' || path === '/api/auth/me') {
|
|
csrfToken = typeof data.csrf_token === 'string' ? data.csrf_token : null
|
|
}
|
|
return data as T
|
|
}
|
|
|
|
export type ApiDownload = { blob: Blob; filename: string; recordsSha256: string | null }
|
|
|
|
export async function apiDownload(path: string, init: RequestInit = {}, protect = false): Promise<ApiDownload> {
|
|
const response = await request(path, init, protect, 'application/x-ndjson')
|
|
const disposition = response.headers.get('Content-Disposition') ?? ''
|
|
const filename = /^attachment; filename="([A-Za-z0-9._-]+)"$/.exec(disposition)?.[1] ?? 'download.jsonl'
|
|
return { blob: await response.blob(), filename, recordsSha256: response.headers.get('X-Records-SHA256') }
|
|
}
|