feat: 新增日志中心、功能优化与代码重构
- 新增 log_datasource.go / log_proxy.go / user_api_token.go 后端控制器 - 新增 log_datasource.go model - 新增 monitor/log-center.ts API 模块 - 新增 LogCenter 前端组件及页面 - 重构 deployment 相关组件 (HPDA/UpdateImageModal/bundle-yaml 等) - 新增 ResetPasswordDialog / code-snippet-block / copy-button 等 UI 组件 - 重构多个 k8s 相关组件 (TopologyView/ResourceVisualEditor/PodFileBrowser 等) - 更新多个 API 和工具函数 (auth.ts/monitor/index.ts/request.ts 等) - 更新静态资源 (index.html + 新 JS/CSS bundle) - 删除废弃静态资源文件
This commit is contained in:
1 parent
5ca0e6d83a
commit
8d46e8f352
197 files changed
+5014
-815
No files matched your search
@@ -0,0 +1,54 @@
|
||||
// clipboard.ts — 统一剪贴板复制(Clipboard API + execCommand 回退)// Logan.Li
|
||||
|
||||
/** 复制文本到剪贴板,兼容 HTTP / 非安全上下文 // Logan.Li */
|
||||
export async function copyToClipboard(text: string): Promise<boolean> {
|
||||
if (!text) return false;
|
||||
|
||||
// 优先使用 Clipboard API
|
||||
try {
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
/* 回退 execCommand */
|
||||
}
|
||||
|
||||
// execCommand 回退(参考 AgentWizard)// Logan.Li
|
||||
if (typeof document === 'undefined') return false;
|
||||
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.top = '0';
|
||||
textArea.style.left = '0';
|
||||
textArea.style.width = '2em';
|
||||
textArea.style.height = '2em';
|
||||
textArea.style.padding = '0';
|
||||
textArea.style.border = 'none';
|
||||
textArea.style.outline = 'none';
|
||||
textArea.style.boxShadow = 'none';
|
||||
textArea.style.background = 'transparent';
|
||||
textArea.style.opacity = '0';
|
||||
textArea.style.pointerEvents = 'none';
|
||||
textArea.style.zIndex = '-1';
|
||||
textArea.style.fontSize = '16px';
|
||||
textArea.contentEditable = 'true';
|
||||
textArea.readOnly = true;
|
||||
document.body.appendChild(textArea);
|
||||
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
if (textArea.setSelectionRange) {
|
||||
textArea.setSelectionRange(0, text.length);
|
||||
}
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
success = document.execCommand('copy');
|
||||
} catch {
|
||||
success = false;
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return success;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ export {
|
||||
http,
|
||||
default as request,
|
||||
getApiBase,
|
||||
getPublicApiBase,
|
||||
buildApiUrl,
|
||||
getWsBase,
|
||||
getAuthToken,
|
||||
|
||||
@@ -28,6 +28,13 @@ export function getApiBase(): string {
|
||||
return API_PREFIX;
|
||||
}
|
||||
|
||||
/** 对外展示的 API 根路径(curl/CI 文档),无环境变量时拼接当前浏览器域名 // Logan.Li */
|
||||
export function getPublicApiBase(): string {
|
||||
const base = getApiBase();
|
||||
if (/^https?:\/\//i.test(base)) return base;
|
||||
return `${window.location.origin}${base}`;
|
||||
}
|
||||
|
||||
/** 拼接 REST API 完整路径(含 /api/v1 前缀) // Logan.Li */
|
||||
export function buildApiUrl(path: string, params?: URLSearchParams): string {
|
||||
const p = path.startsWith('/') ? path : `/${path}`;
|
||||
@@ -141,7 +148,18 @@ export const http = {
|
||||
const resp = await fetch(resolveApiUrl(url), {
|
||||
headers: { Authorization: 'Bearer ' + getAuthToken() },
|
||||
});
|
||||
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||
if (!resp.ok) {
|
||||
const ct = resp.headers.get('Content-Type') || '';
|
||||
if (ct.includes('json')) {
|
||||
try {
|
||||
const json = await resp.json() as { message?: string; Message?: string };
|
||||
throw new Error(json.message || json.Message || `HTTP ${resp.status}`);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && !e.message.startsWith('HTTP ')) throw e;
|
||||
}
|
||||
}
|
||||
throw new Error(`HTTP ${resp.status}`);
|
||||
}
|
||||
const blob = await resp.blob();
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
|
||||
@@ -6,6 +6,8 @@ export const STORAGE_KEYS = {
|
||||
token: 'token',
|
||||
theme: 'aiops-theme',
|
||||
user: 'user-storage',
|
||||
/** API 访问密钥明文(本机 localStorage,生成/重新生成后写入,吊销时清除)// Logan.Li */
|
||||
apiAccessKey: 'aiops_api_access_key',
|
||||
} as const;
|
||||
|
||||
export function getItem(key: string): string | null {
|
||||
@@ -48,4 +50,34 @@ export function setToken(token: string): void {
|
||||
export function clearToken(): void {
|
||||
removeItem(STORAGE_KEYS.token);
|
||||
useUserStore.getState().setToken(null);
|
||||
}
|
||||
|
||||
/** 本机缓存的 API 密钥明文(用于 CI/CD curl 自动填入)// Logan.Li */
|
||||
export function getSessionApiAccessKey(): string {
|
||||
const cached = getItem(STORAGE_KEYS.apiAccessKey);
|
||||
if (cached) return cached;
|
||||
// 迁移旧版 sessionStorage 缓存 // Logan.Li
|
||||
if (typeof window === 'undefined') return '';
|
||||
try {
|
||||
const legacy = sessionStorage.getItem(STORAGE_KEYS.apiAccessKey);
|
||||
if (legacy) {
|
||||
setItem(STORAGE_KEYS.apiAccessKey, legacy);
|
||||
sessionStorage.removeItem(STORAGE_KEYS.apiAccessKey);
|
||||
return legacy;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function setSessionApiAccessKey(token: string): void {
|
||||
if (token) setItem(STORAGE_KEYS.apiAccessKey, token);
|
||||
else removeItem(STORAGE_KEYS.apiAccessKey);
|
||||
if (typeof window === 'undefined') return;
|
||||
try {
|
||||
sessionStorage.removeItem(STORAGE_KEYS.apiAccessKey);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user