初始提交:OCI 面板前端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user