Files
oci-portal-dash/src/api/request.ts
T

67 lines
2.2 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.
import { useAuthStore } from '@/stores/auth'
const BASE = '/api/v1'
export class ApiError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.status = status
}
}
interface RequestOptions {
method?: string
body?: unknown
query?: Record<string, string | number | boolean | undefined>
}
function buildUrl(path: string, query?: RequestOptions['query']): string {
if (!query) return BASE + path
const qs = new URLSearchParams()
for (const [k, v] of Object.entries(query)) {
if (v !== undefined && v !== '') qs.set(k, String(v))
}
const s = qs.toString()
return s ? `${BASE}${path}?${s}` : BASE + path
}
async function parseError(resp: Response): Promise<never> {
let message = `请求失败(${resp.status}`
try {
const body = (await resp.json()) as { error?: string; hint?: string }
if (body.error) message = body.hint ? `${body.hint}${body.error}` : body.error
} catch {
/* 非 JSON 响应体,保留默认消息 */
}
if (resp.status === 401) useAuthStore().logout()
// 全局限速(429)整页拦截;登录接口的 429 是账号锁定,留在表单内提示
if (resp.status === 429 && !resp.url.includes('/auth/login') && location.pathname !== '/blocked') {
location.assign('/blocked')
}
throw new ApiError(resp.status, message)
}
/** 统一请求封装:注入 JWT、401 登出、错误转 ApiError */
export async function request<T>(path: string, opts: RequestOptions = {}): Promise<T> {
const auth = useAuthStore()
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (auth.token) headers.Authorization = `Bearer ${auth.token}`
const resp = await fetch(buildUrl(path, opts.query), {
method: opts.method ?? 'GET',
headers,
body: opts.body === undefined ? undefined : JSON.stringify(opts.body),
})
if (!resp.ok) await parseError(resp)
if (resp.status === 204) return undefined as T
return (await resp.json()) as T
}
export const mockOn = import.meta.env.VITE_MOCK === '1'
/** mock 数据延时返回,模拟网络延迟 */
export function mocked<T>(data: T, ms = 200): Promise<T> {
return new Promise((resolve) => setTimeout(() => resolve(structuredClone(data)), ms))
}