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