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

@@ -0,0 +1,144 @@
// 站点拨测辅助函数 // Logan.Li
export const PROBE_TYPE_OPTIONS = ['HTTP', 'SSL', 'TCP', 'DNS', 'PING'] as const;
export type ProbeType = (typeof PROBE_TYPE_OPTIONS)[number];
export interface SiteTask {
id: number;
name: string;
url: string;
type: string;
frequency_sec: number;
timeout_sec: number;
expected_status_code: number;
enabled: boolean;
last_status?: string;
last_response_time_ms?: number;
last_checked_at?: string;
success_rate?: number;
alert_status?: 'normal' | 'alerting';
created_at: string;
ssl_days_remaining?: number;
ssl_cert_expiry?: string;
}
export interface SiteResult {
id: number;
task_id: number;
status: string;
http_status_code?: number;
response_time?: number;
total_time: number;
remote_ip?: string;
error_message?: string;
checked_at: string;
ssl_days_remaining?: number;
ssl_cert_expiry?: string;
ssl_cert_issuer?: string;
}
export interface SiteStats {
total_tasks: number;
enabled_tasks: number;
up_count: number;
down_count: number;
availability: number;
probe_count: number;
}
export type SiteTimeRange = '1h' | '6h' | '24h' | '7d';
export const SITE_TIME_RANGE_HOURS: Record<SiteTimeRange, number> = {
'1h': 1,
'6h': 6,
'24h': 24,
'7d': 168,
};
export const SITE_TIME_RANGE_LABELS: Record<SiteTimeRange, string> = {
'1h': '近 1 小时',
'6h': '近 6 小时',
'24h': '近 24 小时',
'7d': '近 7 天',
};
export const probeTypeMeta: Record<ProbeType, { label: string; placeholder: string; hint: string }> = {
HTTP: { label: '目标 URL', placeholder: 'https://example.com/health', hint: 'Web 请求(支持 http/https),检测状态码与响应时间' },
SSL: { label: 'SSL 地址', placeholder: 'example.com:443 或 https://example.com', hint: 'TLS 握手检测证书(支持 host:port / https:// 域名,不发起 HTTP 请求)' },
TCP: { label: '主机:端口', placeholder: '192.168.1.1:8080 或 example.com:443', hint: 'TCP 端口连通性' },
DNS: { label: '域名', placeholder: 'example.com', hint: 'DNS 解析延迟与结果' },
PING: { label: '主机地址', placeholder: '192.168.1.1 或 example.com', hint: 'ICMP 连通性与延迟' },
};
/** 拨测频率展示 // Logan.Li */
export function fmtFrequency(sec?: number): string {
if (!sec) return '--';
if (sec < 60) return `${sec}秒`;
if (sec < 3600) return `${Math.round(sec / 60)}分钟`;
return `${(sec / 3600).toFixed(1)}小时`;
}
/** 可用率 / 响应时间组合展示 // Logan.Li */
export function fmtAvailResponse(task: Pick<SiteTask, 'success_rate' | 'last_response_time_ms'>): string {
const avail = task.success_rate != null && task.success_rate >= 0
? `${task.success_rate.toFixed(1)}%`
: '--';
return `${avail} / ${fmtMs(task.last_response_time_ms)}`;
}
export function fmtMs(ms?: number): string {
if (ms == null) return '--';
if (ms < 1) return '0ms';
if (ms < 1000) return `${Math.round(ms)}ms`;
return `${(ms / 1000).toFixed(2)}s`;
}
/** 列表时间:MM-DD HH:mm // Logan.Li */
export function fmtTime(iso?: string): string {
if (!iso) return '--';
const d = new Date(iso);
const pad = (n: number) => String(n).padStart(2, '0');
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
/** SSL 证书预警阈值(天)// Logan.Li */
export const SSL_EXPIRY_WARN_DAYS = 30;
/** 是否为 SSL 证书拨测任务 // Logan.Li */
export function isSslProbeTask(task: Pick<SiteTask, 'type'>): boolean {
const t = task.type?.toUpperCase();
return t === 'SSL' || t === 'HTTPS';
}
/** 证书是否进入预警期(默认 < 30 天)// Logan.Li */
export function isSslExpiringSoon(days?: number, threshold = SSL_EXPIRY_WARN_DAYS): boolean {
return days != null && days < threshold;
}
/** SSL 证书剩余天数展示 // Logan.Li */
export function fmtSslDays(days?: number): { text: string; tone: string } {
if (days == null) return { text: '--', tone: 'text-[var(--dg-text-muted)]' };
if (days < 0) return { text: '已过期', tone: 'dg-text-red' };
if (days <= 7) return { text: `${days}天`, tone: 'dg-text-red' };
if (days <= 30) return { text: `${days}天`, tone: 'dg-text-yellow' };
return { text: `${days}天`, tone: 'dg-text-green' };
}
/** 归一化拨测协议类型 // Logan.Li */
export function normalizeProbeType(raw?: string): ProbeType {
const upper = (raw || 'HTTP').toUpperCase();
if (upper === 'HTTPS') return 'HTTP';
if (PROBE_TYPE_OPTIONS.includes(upper as ProbeType)) return upper as ProbeType;
return 'HTTP';
}
export function isHttpProbeTask(task: Pick<SiteTask, 'type'>): boolean {
return normalizeProbeType(task.type) === 'HTTP';
}
export function fmtChartTime(iso?: string): string {
if (!iso) return '';
const d = new Date(iso);
const pad = (n: number) => String(n).padStart(2, '0');
return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}