refactor(frontend): api/components/pages 目录架构统一对齐菜单域 + 日志中心修复

1. 目录架构统一(API = components = 菜单域,一域一目录 + barrel)
   - log/ 提为独立顶层域(日志中心在菜单中与监控中心平级,不再塞在 monitor/)
   - task/ 拆分原 automation.ts → cron-job / script / remote-exec
   - aiops/ 合并 ai.ts + agentCli.ts → assistant + agent-cli
   - agent/ approval/ audit/ 提升为域目录
   - auth 拆分:authApi(认证自服务) + system/user.ts(userApi+ldapApi 管理端)
   - components/automation → components/task,菜单"自动化运维"→"作业任务"
   - pages/ 按域分子目录(approval/audit/monitor),删孤儿 AuditPage.tsx
   - 所有 API 符号名不变,组件经 @/api 聚合引用,运行时行为零变化

2. Loki 查询无返回修复:aiops 容器 DNS 解析外网域名失败
   (宿主机 systemd-resolved 转发链路异常),docker-compose 显式配 dns 上游

3. ES 查询框布局:工具栏单行挤 7 控件(ES 多 150px 索引框),拆两行——
   查询输入独占一行(+查询按钮),次级控件换行 flex-wrap

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
attackerandClaude committed 2026-06-28 06:06:48 +00:00
1 parent da01cbb119
commit c0ee8e4ddd
74 files changed
+229 -216

No files matched your search

+6
View File
@@ -64,6 +64,12 @@ services:
restart: unless-stopped
ports:
- "8080:8080"
# 容器 DNS:宿主机 systemd-resolved(127.0.0.53) 转发链路异常,导致外网域名
# (如 Loki 数据源 loki-cex.51aipay.com)在容器内解析失败 → 查询无返回。
# 显式指定公网上游 DNS 修复 // Logan.Li
dns:
- 223.5.5.5
- 8.8.8.8
environment:
TZ: Asia/Shanghai
MODE: release
+12 -12
View File
@@ -33,16 +33,16 @@ const CapacityPage = lazy(() => import('@/components/kubernetes/CapacityPage').t
const K8sShortcutRedirect = lazy(() => import('@/components/kubernetes/K8sShortcutRedirect').then(m => ({ default: m.default })));
const AlertManager = lazy(() => import('@/components/monitor/AlertManager').then(m => ({ default: m.AlertManager })));
const ApprovalFlowManager = lazy(() => import('@/components/approval/ApprovalFlow').then(m => ({ default: m.ApprovalFlowManager })));
const ApprovalResourcePage = lazy(() => import('@/pages/ApprovalResourcePage').then(m => ({ default: m.ApprovalResourcePage })));
const ApprovalAppPage = lazy(() => import('@/pages/ApprovalAppPage').then(m => ({ default: m.ApprovalAppPage })));
const ApprovalPermissionPage = lazy(() => import('@/pages/ApprovalPermissionPage').then(m => ({ default: m.ApprovalPermissionPage })));
const ApprovalWorkflowPage = lazy(() => import('@/pages/ApprovalWorkflowPage').then(m => ({ default: m.default })));
const ScriptManager = lazy(() => import('@/components/automation/ScriptManager').then(m => ({ default: m.ScriptManager })));
const ApprovalResourcePage = lazy(() => import('@/pages/approval/ApprovalResourcePage').then(m => ({ default: m.ApprovalResourcePage })));
const ApprovalAppPage = lazy(() => import('@/pages/approval/ApprovalAppPage').then(m => ({ default: m.ApprovalAppPage })));
const ApprovalPermissionPage = lazy(() => import('@/pages/approval/ApprovalPermissionPage').then(m => ({ default: m.ApprovalPermissionPage })));
const ApprovalWorkflowPage = lazy(() => import('@/pages/approval/ApprovalWorkflowPage').then(m => ({ default: m.default })));
const ScriptManager = lazy(() => import('@/components/task/ScriptManager').then(m => ({ default: m.ScriptManager })));
const SystemManager = lazy(() => import('@/components/system/SystemManager').then(m => ({ default: m.SystemManager })));
const ProfilePage = lazy(() => import('@/components/user/ProfilePage'));
const TerminalAuditTab = lazy(() => import('@/pages/TerminalAuditTab').then(m => ({ default: m.TerminalAuditTab })));
const AuditOperationsTab = lazy(() => import('@/pages/AuditOperationsTab').then(m => ({ default: m.AuditOperationsTab })));
const LoginLogsTab = lazy(() => import('@/pages/LoginLogsTab').then(m => ({ default: m.LoginLogsTab })));
const TerminalAuditTab = lazy(() => import('@/pages/audit/TerminalAuditTab').then(m => ({ default: m.TerminalAuditTab })));
const AuditOperationsTab = lazy(() => import('@/pages/audit/AuditOperationsTab').then(m => ({ default: m.AuditOperationsTab })));
const LoginLogsTab = lazy(() => import('@/pages/audit/LoginLogsTab').then(m => ({ default: m.LoginLogsTab })));
const CredentialManager = lazy(() => import('@/components/deploy/CredentialManager').then(m => ({ default: m.default })));
const PipelineManager = lazy(() => import('@/components/deploy/PipelineManager').then(m => ({ default: m.PipelineManager })));
const DeployCredentials = lazy(() => import('@/components/deploy/DeployCredentials').then(m => ({ default: m.default })));
@@ -50,7 +50,7 @@ const PipelineEditor = lazy(() => import('@/components/deploy/PipelineEditor').t
const PipelineRunDetail = lazy(() => import('@/components/deploy/PipelineRunDetail').then(m => ({ default: m.default })));
const DeployRBAC = lazy(() => import('@/components/deploy/DeployRBAC').then(m => ({ default: m.default })));
const UserGroupsManager = lazy(() => import('@/components/deploy/UserGroupsManager').then(m => ({ default: m.default })));
const CronJobManager = lazy(() => import('@/components/automation/CronJobManager').then(m => ({ default: m.CronJobManager })));
const CronJobManager = lazy(() => import('@/components/task/CronJobManager').then(m => ({ default: m.CronJobManager })));
const AIOpsAssistant = lazy(() => import('@/components/aiops/AIOpsAssistant').then(m => ({ default: m.AIOpsAssistant })));
const AIAlertAnalyzer = lazy(() => import('@/components/aiops/AIAlertAnalyzer').then(m => ({ default: m.AIAlertAnalyzer })));
const AIPredictiveAnalytics = lazy(() => import('@/components/aiops/AIPredictiveAnalytics').then(m => ({ default: m.AIPredictiveAnalytics })));
@@ -58,10 +58,10 @@ const AgentCLI = lazy(() => import('@/components/aiops/AgentCLI').then(m => ({ d
const LLMConfigPage = lazy(() => import('@/components/aiops/LLMConfig').then(m => ({ default: m.default })));
const AlertCenter = lazy(() => import('@/components/monitor/alert-center/AlertCenter').then(m => ({ default: m.default })));
const TraceManager = lazy(() => import('@/components/monitor/trace/TraceManager').then(m => ({ default: m.default })));
const HostMonitor = lazy(() => import('@/pages/HostMonitor').then(m => ({ default: m.HostMonitor })));
const HostMonitor = lazy(() => import('@/pages/monitor/HostMonitor').then(m => ({ default: m.HostMonitor })));
const GrafanaDashboard = lazy(() => import('@/components/monitor/dashboard/GrafanaDashboard').then(m => ({ default: m.default })));
const MonitorCenter = lazy(() => import('@/pages/MonitorCenter').then(m => ({ default: m.MonitorCenter })));
const MetricsExplorer = lazy(() => import('@/pages/MetricsExplorer').then(m => ({ default: m.MetricsExplorer })));
const MonitorCenter = lazy(() => import('@/pages/monitor/MonitorCenter').then(m => ({ default: m.MonitorCenter })));
const MetricsExplorer = lazy(() => import('@/pages/monitor/MetricsExplorer').then(m => ({ default: m.MetricsExplorer })));
const DatasourcePage = lazy(() => import('@/components/monitor/datasource/DatasourcePage').then(m => ({ default: m.default })));
const LogCenter = lazy(() => import('@/components/monitor/log-center/LogCenter'));
File renamed without changes.
+3
View File
@@ -0,0 +1,3 @@
// 资源监控 Agent API 聚合出口 // Logan.Li
export * from './agent';
File renamed without changes.
File renamed without changes.
+4
View File
@@ -0,0 +1,4 @@
// AIOps 智能 API 聚合出口 // Logan.Li
export * from './assistant';
export * from './agent-cli';
File renamed without changes.
+3
View File
@@ -0,0 +1,3 @@
// 资源申请 API 聚合出口 // Logan.Li
export * from './approval';
File renamed without changes.
+4
View File
@@ -0,0 +1,4 @@
// 安全审计 API 聚合出口 // Logan.Li
export * from './audit';
export * from './terminal';
File renamed without changes.
+4 -82
View File
@@ -1,10 +1,9 @@
// 用户认证 & LDAP
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
// 用户认证 API — login / profile / password / api-key(自服务)// Logan.Li
// 用户·角色·LDAP 管理已拆到 system/user.ts
import { http, getApiBase, type ListResponse } from '@/utils/request';
import { useUserStore } from '@/store';
import { http } from '@/utils/request';
export const userApi = {
export const authApi = {
login: async (username: string, password: string) => {
return http.post<{ token: string; expires_in: number; user: unknown; permissions?: string[] }>(
'/auth/login',
@@ -80,81 +79,4 @@ export const userApi = {
revokeApiAccessKey: async () => {
return http.delete('/user/api-access-key');
},
resetUserPassword: async (id: number | string, new_password: string) => {
return http.post(`/admin/users/${id}/reset-password`, { new_password });
},
getUsers: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
return http.get<ListResponse>('/admin/users', params);
},
createUser: async ( data: Record<string, unknown>) => {
return http.post('/admin/users', data);
},
updateUser: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/admin/users/${id}`, data);
},
deleteUser: async (id: number | string) => {
return http.delete(`/admin/users/${id}`);
},
batchImport: async (data: { users: Record<string, unknown>[]; skip_existing?: boolean }) => {
return http.post('/admin/users/batch-import', data);
},
downloadTemplate: () => {
const token = useUserStore.getState().token || localStorage.getItem('token') || '';
window.open(`${getApiBase()}/admin/users/import-template?token=${token}`, '_blank');
},
// 角色列表 // Logan.Li
getRoles: async () => {
return http.get<ListResponse>('/user/roles');
},
// 角色管理(管理员) // Logan.Li
createRole: async (data: { name: string; code: string; description?: string; permission_ids?: number[] }) => {
return http.post('/admin/roles', data);
},
updateRole: async (id: number | string, data: { name?: string; code?: string; description?: string; permission_ids?: number[] }) => {
return http.put(`/admin/roles/${id}`, data);
},
deleteRole: async (id: number | string) => {
return http.delete(`/admin/roles/${id}`);
},
// 权限列表(供角色权限选择)
getPermissions: async () => {
return http.get<ListResponse>('/admin/permissions');
},
};
// ==================== LDAP 配置 API ====================
export const ldapApi = {
getConfig: async () => {
return http.get('/admin/ldap-config');
},
updateConfig: async ( data: object) => {
return http.put('/admin/ldap-config', data);
},
testConnection: async (data?: { test_user?: string; test_password?: string }) => {
return http.post('/admin/ldap-config/test', data || {});
},
listUsers: async (ouDn?: string) => {
return http.post('/admin/ldap-config/users', { ou_dn: ouDn || '' });
},
batchImport: async ( users: Record<string, unknown>[]) => {
return http.post('/admin/ldap-config/import', { users });
},
listOUs: async () => {
return http.post('/admin/ldap-config/ou-list', {});
},
createOU: async (data: { ou_name: string; parent_dn?: string; description?: string }) => {
return http.post('/admin/ldap-config/create-ou', data);
},
createUser: async (data: { username: string; password: string; display_name?: string; email?: string; ou_dn?: string }) => {
return http.post('/admin/ldap-config/create-user', data);
},
deleteUser: async (dn: string) => {
return http.post('/admin/ldap-config/delete-user', { dn });
},
updateUser: async (data: { dn: string; cn?: string; mail?: string; phone?: string; title?: string }) => {
return http.post('/admin/ldap-config/update-user', data);
},
};
// ==================== 仪表盘 API ====================
+6 -4
View File
@@ -4,12 +4,14 @@
export * from './auth';
export * from './dashboard';
export * from './server';
export * from './cloud';
export * from './kubernetes';
export * from './deploy';
export * from './automation';
export * from './monitor';
export * from './log';
export * from './task';
export * from './aiops';
export * from './agent';
export * from './cloud';
export * from './approval';
export * from './audit';
export * from './system';
export * from './ai';
export * from './agentCli';
+8
View File
@@ -0,0 +1,8 @@
// 日志中心 API 聚合出口 // Logan.Li
export {
logCenterApi,
type LogDataSource,
type LogQueryEntry,
type TestConnectionResult,
} from './log-center';
File renamed without changes.
-1
View File
@@ -7,5 +7,4 @@ export { traceApi } from './trace';
export { traceDatasourceApi, type TraceDataSource } from './trace-datasource';
export { dashboardApi } from './grafana';
export { promDatasourceApi, zabbixDatasourceApi } from './datasource';
export { logCenterApi, type LogDataSource, type LogQueryEntry } from './log-center';
export { metricsApi, snmpApi, prometheusProxyApi, zabbixProxyApi } from './metrics';
+1 -8
View File
@@ -1,10 +1,3 @@
// 系统域 API — 子模块 re-export // Logan.Li
export {
auditApi,
terminalSessionApi,
sessionCommandApi,
loginLogApi,
approvalWorkflowApi,
approvalApi,
} from './system/index';
export * from './system/index';
+2 -4
View File
@@ -1,5 +1,3 @@
// 系统域 API 聚合出口 // Logan.Li
// 系统管理 API 聚合出口 // Logan.Li
export { auditApi } from './audit';
export { terminalSessionApi, sessionCommandApi, loginLogApi } from './terminal';
export { approvalWorkflowApi, approvalApi } from './approval';
export { userApi, ldapApi } from './user';
+84
View File
@@ -0,0 +1,84 @@
// 系统管理 — 用户 / 角色 / LDAP 管理 API(admin)// Logan.Li
// 从 auth.ts 拆出:用户·角色管理(admin)+ LDAP 配置
import { http, getApiBase, type ListResponse } from '@/utils/request';
import { useUserStore } from '@/store';
// 用户 & 角色管理(管理员)
export const userApi = {
resetUserPassword: async (id: number | string, new_password: string) => {
return http.post(`/admin/users/${id}/reset-password`, { new_password });
},
getUsers: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
return http.get<ListResponse>('/admin/users', params);
},
createUser: async ( data: Record<string, unknown>) => {
return http.post('/admin/users', data);
},
updateUser: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/admin/users/${id}`, data);
},
deleteUser: async (id: number | string) => {
return http.delete(`/admin/users/${id}`);
},
batchImport: async (data: { users: Record<string, unknown>[]; skip_existing?: boolean }) => {
return http.post('/admin/users/batch-import', data);
},
downloadTemplate: () => {
const token = useUserStore.getState().token || localStorage.getItem('token') || '';
window.open(`${getApiBase()}/admin/users/import-template?token=${token}`, '_blank');
},
// 角色列表 // Logan.Li
getRoles: async () => {
return http.get<ListResponse>('/user/roles');
},
// 角色管理(管理员) // Logan.Li
createRole: async (data: { name: string; code: string; description?: string; permission_ids?: number[] }) => {
return http.post('/admin/roles', data);
},
updateRole: async (id: number | string, data: { name?: string; code?: string; description?: string; permission_ids?: number[] }) => {
return http.put(`/admin/roles/${id}`, data);
},
deleteRole: async (id: number | string) => {
return http.delete(`/admin/roles/${id}`);
},
// 权限列表(供角色权限选择)
getPermissions: async () => {
return http.get<ListResponse>('/admin/permissions');
},
};
// ==================== LDAP 配置 API ====================
export const ldapApi = {
getConfig: async () => {
return http.get('/admin/ldap-config');
},
updateConfig: async ( data: object) => {
return http.put('/admin/ldap-config', data);
},
testConnection: async (data?: { test_user?: string; test_password?: string }) => {
return http.post('/admin/ldap-config/test', data || {});
},
listUsers: async (ouDn?: string) => {
return http.post('/admin/ldap-config/users', { ou_dn: ouDn || '' });
},
batchImport: async ( users: Record<string, unknown>[]) => {
return http.post('/admin/ldap-config/import', { users });
},
listOUs: async () => {
return http.post('/admin/ldap-config/ou-list', {});
},
createOU: async (data: { ou_name: string; parent_dn?: string; description?: string }) => {
return http.post('/admin/ldap-config/create-ou', data);
},
createUser: async (data: { username: string; password: string; display_name?: string; email?: string; ou_dn?: string }) => {
return http.post('/admin/ldap-config/create-user', data);
},
deleteUser: async (dn: string) => {
return http.post('/admin/ldap-config/delete-user', { dn });
},
updateUser: async (data: { dn: string; cn?: string; mail?: string; phone?: string; title?: string }) => {
return http.post('/admin/ldap-config/update-user', data);
},
};
+30
View File
@@ -0,0 +1,30 @@
// 作业任务 — 定时任务 API(/cron-jobs)// Logan.Li
import { http, type ListResponse } from '@/utils/request';
export const cronJobApi = {
getJobs: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/cron-jobs', params);
},
getJob: async (id: number | string) => {
return http.get(`/cron-jobs/${id}`);
},
createJob: async ( data: Record<string, unknown>) => {
return http.post('/cron-jobs', data);
},
updateJob: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/cron-jobs/${id}`, data);
},
deleteJob: async (id: number | string) => {
return http.delete(`/cron-jobs/${id}`);
},
toggleJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/toggle`);
},
executeJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/execute`);
},
batchDelete: async (ids: number[]) => {
return http.delete('/cron-jobs/batch-delete', { ids });
},
};
+5
View File
@@ -0,0 +1,5 @@
// 作业任务 API 聚合出口 // Logan.Li
export * from './cron-job';
export * from './script';
export * from './remote-exec';
@@ -1,71 +1,15 @@
// 自动化运维 (定时任务/脚本/Ansible)
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
// 作业任务 — 远程执行 API(/ansible + /task-executions)// Logan.Li
import { http, type ListResponse } from '@/utils/request';
export const cronJobApi = {
getJobs: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/cron-jobs', params);
},
getJob: async (id: number | string) => {
return http.get(`/cron-jobs/${id}`);
},
createJob: async ( data: Record<string, unknown>) => {
return http.post('/cron-jobs', data);
},
updateJob: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/cron-jobs/${id}`, data);
},
deleteJob: async (id: number | string) => {
return http.delete(`/cron-jobs/${id}`);
},
toggleJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/toggle`);
},
executeJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/execute`);
},
batchDelete: async (ids: number[]) => {
return http.delete('/cron-jobs/batch-delete', { ids });
},
};
// ==================== 脚本 API ====================
export const scriptApi = {
getScripts: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/scripts', params);
},
getScript: async (id: number | string) => {
return http.get(`/scripts/${id}`);
},
createScript: async ( data: Record<string, unknown>) => {
return http.post('/scripts', data);
},
updateScript: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/scripts/${id}`, data);
},
deleteScript: async (id: number | string) => {
return http.delete(`/scripts/${id}`);
},
batchDelete: async (ids: number[]) => {
return http.post('/scripts/batch-delete', { ids });
},
executeScript: async (id: number | string) => {
return http.post(`/scripts/${id}/execute`);
},
};
// ==================== 任务执行记录 API ====================
// 任务执行记录 API
export const taskExecutionApi = {
getExecutions: async (params?: { task_type?: string; task_id?: string; page?: number; page_size?: number }) => {
return http.get<ListResponse>('/task-executions', params);
},
};
// ==================== 审批流程 API ====================
// Ansible / 远程执行 API
export const ansibleApi = {
// Playbook 管理
getPlaybooks: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
@@ -119,6 +63,3 @@ export const ansibleApi = {
return http.delete(`/ansible/inventories/${id}`);
},
};
// ==================== 告警中心 API(基于 /alerts 真实路由) ====================
// 说明:alertCenterApi 与 alertApi 共用同一套后端 /alerts 路由,此处独立封装以便告警中心页使用
+27
View File
@@ -0,0 +1,27 @@
// 作业任务 — 脚本库 API(/scripts)// Logan.Li
import { http, type ListResponse } from '@/utils/request';
export const scriptApi = {
getScripts: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/scripts', params);
},
getScript: async (id: number | string) => {
return http.get(`/scripts/${id}`);
},
createScript: async ( data: Record<string, unknown>) => {
return http.post('/scripts', data);
},
updateScript: async (id: number | string, data: Record<string, unknown>) => {
return http.put(`/scripts/${id}`, data);
},
deleteScript: async (id: number | string) => {
return http.delete(`/scripts/${id}`);
},
batchDelete: async (ids: number[]) => {
return http.post('/scripts/batch-delete', { ids });
},
executeScript: async (id: number | string) => {
return http.post(`/scripts/${id}/execute`);
},
};
+2 -2
View File
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { User, Lock, Palette, Check, Loader2, AlertCircle, Eye, EyeOff, Languages } from 'lucide-react';
import { useUserStore, useLanguageStore } from '@/store';
import { userApi } from '@/api';
import { authApi } from '@/api';
import { useTheme, THEME_OPTIONS } from '@/hooks/useTheme';
import { cn } from '@/lib/utils';
import { SUPPORTED_LANGUAGES, type AppLanguage } from '@/i18n';
@@ -89,7 +89,7 @@ export function Login() {
setError('');
setLoading(true);
try {
const result = await userApi.login(username, password) as { user: any; token: string; permissions?: string[] };
const result = await authApi.login(username, password) as { user: any; token: string; permissions?: string[] };
useUserStore.getState().login(result.user, result.token, result.permissions ?? null);
localStorage.setItem('token', result.token);
navigate('/', { replace: true });
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { Loader2 } from 'lucide-react';
import { CodeSnippetBlock } from '@/components/ui/code-snippet-block';
import { userApi } from '@/api';
import { authApi } from '@/api';
import { getPublicApiBase } from '@/utils/request';
import { getSessionApiAccessKey } from '@/utils/storage';
@@ -37,7 +37,7 @@ export function CICDWebhookContent({
const cached = getSessionApiAccessKey();
if (cached) setAccessKey(cached);
try {
const meta = await userApi.getApiAccessKey();
const meta = await authApi.getApiAccessKey();
if (!cancelled) {
setHasKey(!!meta?.has_token);
setKeyExpired(!!meta?.expired);
@@ -5,7 +5,7 @@ import { useThemeStore, useUserStore } from '@/store';
import { Sidebar } from './Sidebar';
import { Header } from './Header';
import { Breadcrumb } from './Breadcrumb';
import { userApi } from '@/api';
import { authApi } from '@/api';
import type { JsonData } from '@/types/common';
/**
@@ -30,7 +30,7 @@ export function MainLayout() {
useEffect(() => {
if (!token || permissions !== null) return;
let cancelled = false;
userApi.getMyPermissions()
authApi.getMyPermissions()
.then((data: JsonData) => {
if (cancelled) return;
const perms: string[] = data?.permissions ?? [];
+1 -1
View File
@@ -136,7 +136,7 @@ const menuItems: MenuItem[] = [
],
},
{
id: 'automation', name: '自动化运维', labelKey: 'menu.automation', icon: PlayCircle, permission: 'auto:cron',
id: 'automation', name: '作业任务', labelKey: 'menu.automation', icon: PlayCircle, permission: 'auto:cron',
children: [
{ id: 'cronjobs', name: '定时任务', labelKey: 'menu.cronJobs', icon: PlayCircle, path: '/cronjobs', permission: 'auto:cron' },
{ id: 'scripts', name: '脚本库', labelKey: 'menu.scripts', icon: FileCode, path: '/scripts', permission: 'auto:script' },
@@ -13,8 +13,7 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Loader2 } from 'lucide-react';
import { alertApi } from '@/api';
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
import { alertApi, logCenterApi, type LogDataSource } from '@/api';
import { parseChannels, getChannelIcon, getChannelTypeLabel } from './alertShared';
import type { JsonData } from '@/types/common';
@@ -12,7 +12,7 @@ import {
} from '@/components/ui/select';
import { Loader2, Plug, RefreshCw } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api/monitor/log-center';
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api';
import type { LogEngine } from './log-center-types';
export function LogDatasourceDialog({
@@ -8,7 +8,7 @@ import {
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api/monitor/log-center';
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api';
import { LogDatasourceDialog } from './LogDatasourceDialog';
import type { LogEngine } from './log-center-types';
@@ -11,7 +11,7 @@ import { LogClusterPanel } from './LogClusterPanel';
import { SaveQueryDialog, SaveAlertDialog } from './SaveQueryDialog';
import { QueryHistoryPanel } from './QueryHistoryPanel';
import { LogAlertFromQueryDialog } from './LogAlertFromQueryDialog';
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
import { logCenterApi, type LogDataSource } from '@/api';
import type { LogEngine, TimeRange, ResultTab, ViewMode, LogEntry, StatTable, LogCluster, QueryHistoryItem } from './types-v3';
import { ENGINE_META, buildStatTableFromEntries, buildClustersFromEntries, extractFields, generateHistogramData, historyStorage } from './types-v3';
import { useToast } from '@/hooks/use-toast';
@@ -280,8 +280,13 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
<span className="text-[11px] font-semibold" style={{ color: badge.dotColor }}>{badge.label}</span>
</div>
<SyntaxHighlightInput value={query} onChange={setQuery} engine={effEngine} placeholder={meta.queryPlaceholder} />
<button onClick={handleRun} disabled={loading || !currentSource} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} 查询
</button>
</div>
</div>
{/* 次级控件行(时间 / 数据源 / 索引 / 上限 / 另存为 / 历史)// Logan.Li */}
<div className="flex items-center gap-2 flex-wrap">
<TimeRangePicker value={timeRange} onChange={setTimeRange} />
<select value={dsId} onChange={e => setDsId(e.target.value)} className="h-10 px-2 text-xs rounded flex-shrink-0" style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}>
@@ -316,10 +321,6 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
/>
</div>
<button onClick={handleRun} disabled={loading || !currentSource} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} 查询
</button>
<div className="relative flex-shrink-0">
<button onClick={() => setSaveMenuOpen(!saveMenuOpen)} className="h-10 px-3 rounded text-xs flex items-center gap-1" style={{ border: '1px solid var(--dg-border)', color: 'var(--dg-text-secondary)', background: 'var(--dg-bg-tertiary)' }}>
<Save className="h-3.5 w-3.5" /> 另存为 <ChevronDown className="h-3 w-3" />
@@ -14,7 +14,7 @@ import {
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { userApi } from '@/api';
import { authApi } from '@/api';
import { getPublicApiBase } from '@/utils/request';
import { getSessionApiAccessKey, setSessionApiAccessKey } from '@/utils/storage';
import { cn } from '@/lib/utils';
@@ -93,7 +93,7 @@ export function ApiAccessKeySection() {
try {
const cached = getSessionApiAccessKey();
if (cached) setPlainToken(cached);
const meta = await userApi.getApiAccessKey().catch(() => ({ has_token: false }));
const meta = await authApi.getApiAccessKey().catch(() => ({ has_token: false }));
setKeyMeta(meta);
} finally {
setLoading(false);
@@ -130,7 +130,7 @@ export function ApiAccessKeySection() {
if (formMode === 'edit') {
setActionLoading(true);
try {
const res = await userApi.updateApiAccessKey({
const res = await authApi.updateApiAccessKey({
description: formDescription,
expires_at: formExpiresAt,
});
@@ -156,7 +156,7 @@ export function ApiAccessKeySection() {
setActionLoading(true);
try {
const res = await userApi.regenerateApiAccessKey({
const res = await authApi.regenerateApiAccessKey({
description: formDescription,
expires_at: formExpiresAt,
});
@@ -195,7 +195,7 @@ export function ApiAccessKeySection() {
if (!ok) return;
setActionLoading(true);
try {
await userApi.revokeApiAccessKey();
await authApi.revokeApiAccessKey();
setPlainToken('');
setSessionApiAccessKey('');
setKeyMeta({ has_token: false });
+3 -3
View File
@@ -7,7 +7,7 @@ import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { User, Shield, Loader2 } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { userApi } from '@/api';
import { authApi } from '@/api';
import { useUserStore } from '@/store';
import { ApiAccessKeySection } from './ApiAccessKeySection';
@@ -25,7 +25,7 @@ export default function ProfilePage() {
const load = useCallback(async () => {
setLoading(true);
try {
const p = await userApi.getProfile();
const p = await authApi.getProfile();
setProfile(p);
} finally {
setLoading(false);
@@ -41,7 +41,7 @@ export default function ProfilePage() {
if (!oldPwd || newPwd.length < 6) return;
setPwdSaving(true);
try {
await userApi.changePassword({ old_password: oldPwd, new_password: newPwd });
await authApi.changePassword({ old_password: oldPwd, new_password: newPwd });
toast({ title: '密码已修改' });
setOldPwd('');
setNewPwd('');
+1 -1
View File
@@ -115,7 +115,7 @@
"esQuery": "ES Query",
"logLibrary": "Log Library",
"logAlerts": "Log Alerts",
"automation": "Automation",
"automation": "Task",
"cronJobs": "Cron Jobs",
"scripts": "Scripts",
"remoteExec": "Remote Exec",
+1 -1
View File
@@ -115,7 +115,7 @@
"esQuery": "ES查询",
"logLibrary": "日志库",
"logAlerts": "日志告警",
"automation": "自动化运维",
"automation": "作业任务",
"cronJobs": "定时任务",
"scripts": "脚本库",
"remoteExec": "远程执行",
-16
View File
@@ -1,16 +0,0 @@
// 安全审计 — 内容由侧边栏路由分发 | v2 (0603)
import { useLocation } from 'react-router-dom';
import { TerminalAuditTab } from './TerminalAuditTab';
import { AuditOperationsTab } from './AuditOperationsTab';
import { LoginLogsTab } from './LoginLogsTab';
export function AuditPage() {
const { pathname } = useLocation();
console.debug('[AuditPage v2] pathname:', pathname);
if (pathname.includes('/audit/terminal')) return <TerminalAuditTab />;
if (pathname.includes('/audit/operations')) return <AuditOperationsTab />;
if (pathname.includes('/audit/login-logs')) return <LoginLogsTab />;
return <TerminalAuditTab />;
}