Files
AI_Translator/apps/web/lib/admin-api.ts
2025-12-29 15:52:50 +08:00

68 lines
2.0 KiB
TypeScript

export const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8030'
export const ADMIN_UNAUTHORIZED_EVENT = 'admin:unauthorized'
export class AdminUnauthorizedError extends Error {
constructor(message = '未登录或登录已过期') {
super(message)
this.name = 'AdminUnauthorizedError'
}
}
export function getAdminToken(): string {
if (typeof window === 'undefined') return ''
return localStorage.getItem('admin_token') || ''
}
export function clearAdminToken() {
if (typeof window === 'undefined') return
localStorage.removeItem('admin_token')
}
function notifyUnauthorized(message?: string) {
if (typeof window === 'undefined') return
window.dispatchEvent(new CustomEvent(ADMIN_UNAUTHORIZED_EVENT, { detail: { message } }))
}
async function safeReadErrorMessage(res: Response): Promise<string | null> {
try {
const data = await res.json()
const detail = typeof data?.detail === 'string' ? data.detail : null
if (detail) return detail
} catch {
// ignore
}
return null
}
export async function adminFetch(path: string, init: RequestInit = {}): Promise<Response> {
const token = getAdminToken()
const headers = new Headers(init.headers)
if (!token) {
notifyUnauthorized()
throw new AdminUnauthorizedError()
}
if (token) headers.set('Authorization', `Bearer ${token}`)
if (!headers.has('Content-Type') && init.body) headers.set('Content-Type', 'application/json')
const res = await fetch(`${API_BASE}${path}`, { ...init, headers })
if (res.status === 401) {
const message = (await safeReadErrorMessage(res)) || undefined
clearAdminToken()
notifyUnauthorized(message)
throw new AdminUnauthorizedError(message)
}
return res
}
export async function adminFetchJson<T>(path: string, init: RequestInit = {}): Promise<T> {
const res = await adminFetch(path, init)
if (!res.ok) {
const message = (await safeReadErrorMessage(res)) || `请求失败 (${res.status})`
throw new Error(message)
}
return res.json() as Promise<T>
}