实现 LightOps 运维面板基础功能

This commit is contained in:
2026-05-25 01:13:03 +08:00
commit d3bb9f45a6
84 changed files with 23505 additions and 0 deletions

46
web/src/lib/api.ts Normal file
View 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
View 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;
}