docs: update README, API reference, dev-log, project structure with Agent monitoring + quick-alarm + latest fixes

- README.md: React 19, default pwd 123456, Docker Compose deploy, correct module list
- api-reference.md: add Agent monitoring section (report/latest/rules/quick-setup/alerts/tasks/kline)
- dev-log.md: new entry documenting hostname/IP fix, system collector, offline detection, quick alarm
- project-structure.md: add agent/ directory, dev-log.md link, React 19
This commit is contained in:
attacker committed 2026-06-18 16:11:13 +00:00
1 parent c51d0b8b8b
commit 4e3f8cb306
20 files changed
+1315 -1344

No files matched your search

@@ -2,7 +2,7 @@
import React, { useState, useEffect } from 'react';
import {
AlertTriangle, AlertCircle, CheckCircle, Zap, Search, RefreshCw,
Eye, Check, VolumeX,
Eye, Check, VolumeX, Plus, Rocket,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -14,14 +14,17 @@ import {
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Switch } from '@/components/ui/switch';
import { toast } from 'sonner';
import { alertCenterApi } from '@/services/api';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { alertCenterApi, agentApi } from '@/services/api';
import {
type Alert, type AlertRule, mapAlert, getSeverityBadge, getStatusBadge,
} from './alert-center-helpers';
import { AlertDetailDialog, AlertSilenceDialog } from './AlertCenterDetail';
import { AlertChannelsTab } from './AlertChannelsTab';
import { AlertRuleDialog } from '@/components/monitor/AlertRuleDialog';
const AlertCenter: React.FC = () => {
const _confirm = useConfirm();
const [alerts, setAlerts] = useState<Alert[]>([]);
const [rules, setRules] = useState<AlertRule[]>([]);
const [loading, setLoading] = useState(false);
@@ -33,6 +36,10 @@ const AlertCenter: React.FC = () => {
const [detailOpen, setDetailOpen] = useState(false);
const [silenceOpen, setSilenceOpen] = useState(false);
const [stats, setStats] = useState({ firing: 0, critical: 0, warning: 0, resolved: 0 });
// 规则弹框
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
const [editingRule, setEditingRule] = useState<any>(null);
const [quickSetupLoading, setQuickSetupLoading] = useState(false);
useEffect(() => {
fetchData();
@@ -67,8 +74,19 @@ const AlertCenter: React.FC = () => {
const fetchRules = async () => {
try {
const res = await alertCenterApi.getRules();
setRules(res.list || []);
const res = await agentApi.listRules();
// agentApi.listRules returns {code, data: AgentRule[]} — map to AlertRule for UI
const raw = (Array.isArray(res) ? res : (res.list || [])) as any[];
setRules(raw.map(r => ({
id: r.id,
name: r.name,
description: `${r.metric} ${r.operator} ${r.threshold}%`,
enabled: r.enabled,
severity: 'warning',
type: r.metric,
hostname: r.hostname || '全部主机',
threshold: r.threshold,
})) as AlertRule[]);
} catch {
toast.error('获取告警规则失败');
}
@@ -107,7 +125,7 @@ const AlertCenter: React.FC = () => {
const toggleRule = async (ruleId: string, enabled: boolean) => {
try {
await alertCenterApi.toggleRule(ruleId);
await agentApi.updateRule(Number(ruleId), { enabled });
setRules(rules.map(r => String(r.id) === ruleId ? { ...r, enabled } : r));
toast.success('规则状态已更新');
} catch {
@@ -115,6 +133,33 @@ const AlertCenter: React.FC = () => {
}
};
const handleOpenAddRule = () => { setEditingRule(null); setRuleDialogOpen(true); };
const handleEditRule = (rule: AlertRule) => { setEditingRule(rule); setRuleDialogOpen(true); };
const handleRuleDialogClose = () => { setRuleDialogOpen(false); setEditingRule(null); };
const handleQuickSetup = async () => {
const ok = await _confirm({
title: '一键报警',
description: '将为 Agent 在线的全部主机创建 CPU使用率、内存使用率、磁盘使用率、网络带宽使用率 四条默认告警规则(阈值 80%,连续 5 次触发)。',
confirmText: '确认创建',
});
if (!ok) return;
setQuickSetupLoading(true);
try {
const res = await agentApi.quickSetup('ecs');
toast.success(typeof res === 'string' ? res : (res?.message || '默认告警规则创建成功'));
fetchRules();
} catch {
toast.error('创建失败,请检查 Agent 主机是否在线');
} finally { setQuickSetupLoading(false); }
};
const handleDeleteRule = async (rule: AlertRule) => {
const ok = await _confirm({ title: '删除规则', description: `确定删除规则「${rule.name}」?`, confirmText: '删除', variant: 'destructive' });
if (!ok) return;
try { await agentApi.deleteRule(Number(rule.id)); toast.success('已删除'); fetchRules(); } catch { toast.error('删除失败'); }
};
const filteredAlerts = alerts.filter(alert => {
const matchSearch = alert.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
alert.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -311,11 +356,27 @@ const AlertCenter: React.FC = () => {
{/* 告警规则 Tab */}
<TabsContent value="rules" className="space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<Button onClick={handleOpenAddRule}>
<Plus className="w-4 h-4 mr-1" />添加规则
</Button>
<Button variant="secondary" onClick={handleQuickSetup} disabled={quickSetupLoading}>
<Rocket className="w-4 h-4 mr-1" />
{quickSetupLoading ? '创建中…' : '一键报警'}
</Button>
</div>
<Button variant="outline" onClick={fetchRules}>
<RefreshCw className="w-4 h-4 mr-2" />
刷新
</Button>
</div>
{rules.length === 0 ? (
<div className="text-center py-12 text-muted-foreground">
<AlertTriangle className="w-10 h-10 mx-auto mb-2 opacity-50" />
<p className="text-sm">暂无告警规则</p>
<p className="text-xs mt-1">点击「添加规则」或「一键报警」快速创建</p>
</div>
) : (
<div className="grid grid-cols-1 gap-4">
{rules.map((rule) => (
<Card key={rule.id}>
@@ -353,6 +414,8 @@ const AlertCenter: React.FC = () => {
})()}
</div>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={() => handleEditRule(rule)}>编辑</Button>
<Button variant="ghost" size="sm" className="text-red-400" onClick={() => handleDeleteRule(rule)}>删除</Button>
<Switch
checked={rule.enabled}
onCheckedChange={(checked) => toggleRule(String(rule.id), checked)}
@@ -363,6 +426,7 @@ const AlertCenter: React.FC = () => {
</Card>
))}
</div>
)}
</TabsContent>
{/* 通知渠道 Tab — 自包含子组件 */}
@@ -383,6 +447,13 @@ const AlertCenter: React.FC = () => {
onOpenChange={setSilenceOpen}
onSilence={handleSilence}
/>
<AlertRuleDialog
open={ruleDialogOpen}
onOpenChange={handleRuleDialogClose}
editingRule={editingRule}
notifyChannels={[]}
onSaved={() => { handleRuleDialogClose(); fetchRules(); }}
/>
</div>
);
};