feat: 监控告警体系统一重构 + 自动化任务调度引擎 + 监控大屏拆分

告警中心
- 新增统一告警(alert_unified)、监控告警规则(monitor_alert_rule)、指标目录(alert_metric_catalog)
- 通知策略与消息分发(alert_notify_policy/dispatch/message)、静默规则
- 告警引擎按阈值/SNMP 拆分(alert_engine_monitor/snmp)

监控采集
- SNMP 常量/模板与历史指标(snmp templates/constants、snmp_metrics_history、snmp_collect)
- 中间件监控 Redis 采集与种子(middleware_monitor_redis/seed)
- 站点探测 pkg(siteprobe) + 站点视图(site_view)

自动化任务
- cron 调度器(cronscheduler)、脚本运行器(scriptrunner)、系统任务(systask)及运行记录(cron_job_run、system_task)

审计与凭证
- 审计设置(audit_setting) + 历史清理(auditcleaner)

前端
- 监控页面(host/middleware/site/snmp)拆分为子模块并大屏化
- 告警中心多 Tab(规则/指标目录/通知策略/静默)、任务管理组件重构、Agent CLI Markdown 渲染
- 移除旧脚本卡片/详情/日志组件,统一为 ScriptTable/ScriptCodeEditor/TerminalOutput

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
LoganandClaude committed 2026-07-01 23:44:45 +08:00
1 parent 092d0e4b9e
commit 471353e3d7
190 files changed
+16640 -3484

No files matched your search

@@ -3,7 +3,8 @@ import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { Badge } from '@/components/ui/badge';
import { CheckCircle, XCircle, Settings } from 'lucide-react';
import { toast } from 'sonner';
import { CheckCircle, XCircle, Settings, Users, Shield } from 'lucide-react';
import { PageShell } from '@/components/layout/PageShell';
import { userApi } from '@/api';
import type { Role } from '@/types';
@@ -12,6 +13,7 @@ import { SystemRolesTab } from './SystemRolesTab';
import { SystemSettingsTab } from './SystemSettingsTab';
import { ResetPasswordDialog } from './ResetPasswordDialog';
import { useLdapManagement } from './useLdapManagement';
import { useAuditSettings } from './useAuditSettings';
import type { UserFormData } from './UserFormDialog';
// 后端用户数据转换为前端兼容格式
@@ -40,20 +42,32 @@ export function SystemManager() {
const confirm = useConfirm();
const location = useLocation();
// 侧边栏菜单路径映射到 Tab // Logan.Li
const pathToTab: Record<string, string> = {
// 侧边栏已有独立菜单项,按路由直接渲染对应页面,不再重复 Tab // Logan.Li
const pathToTab: Record<string, 'users' | 'roles' | 'settings'> = {
'/users': 'users',
'/roles': 'roles',
'/settings': 'settings',
};
const [activeTab, setActiveTab] = useState(pathToTab[location.pathname] || 'users');
const activeTab = pathToTab[location.pathname] || 'users';
// URL 变化时同步 Tab(侧边栏菜单点击触发) // Logan.Li
useEffect(() => {
const tab = pathToTab[location.pathname];
if (tab && tab !== activeTab) {
setActiveTab(tab);
}
}, [location.pathname]);
const pageMeta: Record<string, { title: string; subtitle: string; icon: React.ReactNode }> = {
users: {
title: '用户管理',
subtitle: '本地与 LDAP 用户账号、状态与角色分配',
icon: <Users className="h-5 w-5" />,
},
roles: {
title: '角色权限',
subtitle: '角色绑定权限码,同时约束前端菜单与后端 API',
icon: <Shield className="h-5 w-5" />,
},
settings: {
title: '系统设置',
subtitle: '通用与审计、安全策略、LDAP 目录服务',
icon: <Settings className="h-5 w-5" />,
},
};
const meta = pageMeta[activeTab];
const [searchQuery, setSearchQuery] = useState('');
const [users, setUsers] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
@@ -71,6 +85,7 @@ export function SystemManager() {
const [importDialogOpen, setImportDialogOpen] = useState<'csv' | 'ldap' | null>(null);
const [importPreview, setImportPreview] = useState<any[]>([]);
const [importResult, setImportResult] = useState<any>(null);
const [selectedUserIds, setSelectedUserIds] = useState<number[]>([]);
const loadUsers = async () => {
setLoading(true);
@@ -88,6 +103,7 @@ export function SystemManager() {
// LDAP 状态全部由 hook 管理(在 loadUsers 之后调用避免 TDZ) // Logan.Li
const ldap = useLdapManagement(loadUsers);
const audit = useAuditSettings();
useEffect(() => {
loadUsers();
@@ -175,9 +191,30 @@ export function SystemManager() {
if (!ok) return;
try {
await userApi.deleteUser(user.id);
setSelectedUserIds((prev) => prev.filter((id) => id !== user.id));
await loadUsers();
} catch (error) {
console.error('Failed to delete user:', error);
toast.success('用户已删除');
} catch (error: unknown) {
toast.error(error instanceof Error ? error.message : '删除失败');
}
};
const handleBatchDelete = async () => {
if (selectedUserIds.length === 0) return;
const ok = await confirm({
title: '批量删除用户',
description: `确定删除选中的 ${selectedUserIds.length} 个用户吗?此操作不可恢复。`,
confirmText: '批量删除',
variant: 'destructive',
});
if (!ok) return;
try {
const res = await userApi.batchDelete(selectedUserIds) as { message?: string };
toast.success(res?.message || `已删除 ${selectedUserIds.length} 个用户`);
setSelectedUserIds([]);
await loadUsers();
} catch (error: unknown) {
toast.error(error instanceof Error ? error.message : '批量删除失败');
}
};
@@ -231,21 +268,13 @@ export function SystemManager() {
}
};
const pageTabs = [
{ id: 'users', label: '用户管理', badge: users.length || undefined },
{ id: 'roles', label: '角色权限' },
{ id: 'settings', label: '系统设置' },
];
return (
<PageShell
variant="list"
title="系统管理"
subtitle="用户、权限与系统配置"
icon={<Settings className="h-5 w-5" />}
tabs={pageTabs}
activeTab={activeTab}
onTabChange={setActiveTab}
variant={activeTab === 'settings' ? 'config' : 'list'}
maxWidth={activeTab === 'settings' ? 'xl' : 'full'}
title={meta.title}
subtitle={meta.subtitle}
icon={meta.icon}
>
<div className="w-full">
{activeTab === 'users' && (
@@ -268,6 +297,9 @@ export function SystemManager() {
openCreate={openCreate}
openEdit={openEdit}
handleDelete={handleDelete}
handleBatchDelete={handleBatchDelete}
selectedUserIds={selectedUserIds}
setSelectedUserIds={setSelectedUserIds}
onResetPassword={(u) => { setSelectedUser(null); setResetPasswordUser(u); }}
importDialogOpen={importDialogOpen}
setImportDialogOpen={setImportDialogOpen}
@@ -286,7 +318,7 @@ export function SystemManager() {
ldapImportOUDN={ldap.ldapImportOUDN}
setLdapImportOUDN={ldap.setLdapImportOUDN}
loadLdapUsers={ldap.loadLdapUsers}
handleLdapImport={ldap.handleLdapImport}
handleLdapImport={() => ldap.handleLdapImport({ setResult: setImportResult })}
setLdapUsers={ldap.setLdapUsers}
/>
</div>
@@ -300,7 +332,18 @@ export function SystemManager() {
)}
{activeTab === 'settings' && (
<SystemSettingsTab {...ldap.settingsTabProps} />
<SystemSettingsTab
{...ldap.settingsTabProps}
auditRetentionDays={audit.retentionDays}
setAuditRetentionDays={audit.setRetentionDays}
auditRecordAll={audit.recordAll}
setAuditRecordAll={audit.setRecordAll}
auditLoading={audit.loading}
auditApplying={audit.applying}
handleAuditApply={audit.handleApply}
auditLastCleanupText={audit.lastCleanupText}
auditRetentionOptions={audit.retentionOptions}
/>
)}
</div>