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