实现 LightOps 运维面板基础功能
This commit is contained in:
46
web/src/lib/api.ts
Normal file
46
web/src/lib/api.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export type ApiResponse<T> = {
|
||||
success: boolean;
|
||||
data: T | null;
|
||||
error: string | null;
|
||||
};
|
||||
|
||||
const TOKEN_KEY = 'lightops_token';
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function setToken(token: string | null) {
|
||||
if (token) localStorage.setItem(TOKEN_KEY, token);
|
||||
else localStorage.removeItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
export async function api<T>(path: string, options: RequestInit = {}): Promise<T> {
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('content-type', 'application/json');
|
||||
const token = getToken();
|
||||
if (token) headers.set('authorization', `Bearer ${token}`);
|
||||
const res = await fetch(path, { ...options, headers });
|
||||
const body = (await res.json()) as ApiResponse<T>;
|
||||
if (!res.ok || !body.success) {
|
||||
throw new Error(body.error || `HTTP ${res.status}`);
|
||||
}
|
||||
return body.data as T;
|
||||
}
|
||||
|
||||
export async function post<T>(path: string, body: unknown): Promise<T> {
|
||||
return api<T>(path, { method: 'POST', body: JSON.stringify(body) });
|
||||
}
|
||||
|
||||
export async function del<T>(path: string): Promise<T> {
|
||||
return api<T>(path, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
export function wsUrl(path: string) {
|
||||
const token = getToken();
|
||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const url = new URL(path, `${proto}//${location.host}`);
|
||||
if (token) url.searchParams.set('token', token);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
28
web/src/lib/confirm.ts
Normal file
28
web/src/lib/confirm.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export type ConfirmPayload = {
|
||||
confirmed_at: string;
|
||||
target: string;
|
||||
level: 'normal' | 'high';
|
||||
};
|
||||
|
||||
export function requireConfirm(message: string) {
|
||||
return window.confirm(message);
|
||||
}
|
||||
|
||||
export function requireDangerConfirm(action: string, target: string, level: 'normal' | 'high' = 'high') {
|
||||
const cleanTarget = target.trim();
|
||||
if (!cleanTarget) return null;
|
||||
const typed = window.prompt(`高风险操作:${action}\n请输入目标名称以确认:${cleanTarget}`);
|
||||
if (typed !== cleanTarget) {
|
||||
window.alert('确认内容不匹配,操作已取消。');
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
confirmed_at: new Date().toISOString(),
|
||||
target: cleanTarget,
|
||||
level
|
||||
} satisfies ConfirmPayload;
|
||||
}
|
||||
|
||||
export function withConfirm<T extends Record<string, unknown>>(body: T, confirm: ConfirmPayload | null) {
|
||||
return confirm ? { ...body, confirm_target: confirm.target, confirm } : body;
|
||||
}
|
||||
Reference in New Issue
Block a user