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:
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>
|
||||
|
||||
|
||||
Reference in new issue
Block a user