68 lines
2.0 KiB
TypeScript
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>
|
|
}
|