feat(web): task-2 布局骨架+登录页(ProLayout 菜单对齐归档版、Basic Auth 登录、401 全局登出、/api 代理)+ 领域 API 层(路径对齐 dataProvider)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// 对齐 web.archived dataProvider 的请求语义(fetch → Basic Auth → 401 全局登出)。
|
||||
export const AUTH_STORAGE_KEY = 'creatorhub.auth';
|
||||
|
||||
export class RequestError extends Error {
|
||||
status: number;
|
||||
body?: unknown;
|
||||
constructor(message: string, status: number, body?: unknown) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
|
||||
export function unauthorized(): never {
|
||||
localStorage.removeItem(AUTH_STORAGE_KEY);
|
||||
if (window.location.pathname !== '/login') {
|
||||
window.history.replaceState({}, '', '/login');
|
||||
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||
}
|
||||
throw new RequestError('认证已过期,请重新登录', 401);
|
||||
}
|
||||
|
||||
export async function request(path: string, options: RequestInit = {}): Promise<any> {
|
||||
const auth = localStorage.getItem(AUTH_STORAGE_KEY);
|
||||
const headers: Record<string, string> = { ...(options.headers as Record<string, string>) };
|
||||
if (auth) headers.Authorization = `Basic ${btoa(auth)}`;
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(`/api${path}`, { ...options, headers });
|
||||
} catch (networkError: any) {
|
||||
throw new RequestError(`网络请求失败:${networkError?.message ?? networkError}`, 0);
|
||||
}
|
||||
if (response.status === 401) unauthorized();
|
||||
if (!response.ok) {
|
||||
const body = await response.json().catch(() => ({}));
|
||||
throw new RequestError(body.error || `请求失败 (${response.status})`, response.status, body);
|
||||
}
|
||||
if (response.status === 204) return null;
|
||||
const contentType = response.headers?.get?.('content-type') || '';
|
||||
if (contentType.toLowerCase().includes('text/html')) {
|
||||
throw new RequestError('API 返回了非 JSON 响应,请检查控制面路由', response.status);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export function jsonOptions(method: string, data: unknown): RequestInit {
|
||||
return {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user