diff --git a/frontend/src/components/aiops/AIAlertAnalyzer.tsx b/frontend/src/components/aiops/AIAlertAnalyzer.tsx index eee5f1d..aad8c2d 100644 --- a/frontend/src/components/aiops/AIAlertAnalyzer.tsx +++ b/frontend/src/components/aiops/AIAlertAnalyzer.tsx @@ -1,10 +1,9 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Separator } from '@/components/ui/separator'; +import { alertApi } from '@/services/api'; import { Dialog, DialogContent, @@ -58,150 +57,201 @@ interface AIAnalysis { confidence: number; } -const mockAlerts: AlertItem[] = [ - { - id: 'ALT-001', - name: 'CPU使用率过高', - severity: 'critical', - status: 'active', - source: 'web-server-03', - metric: 'cpu_usage', - threshold: '80%', - currentValue: '96.5%', - duration: '15分钟', - startedAt: '2025-04-14 14:30:00', - description: '服务器CPU使用率持续超过阈值', - }, - { - id: 'ALT-002', - name: '内存使用率告警', - severity: 'warning', - status: 'active', - source: 'db-master-01', - metric: 'memory_usage', - threshold: '85%', - currentValue: '89.2%', - duration: '8分钟', - startedAt: '2025-04-14 14:37:00', - description: '数据库服务器内存使用率接近上限', - }, - { - id: 'ALT-003', - name: '磁盘空间不足', - severity: 'critical', - status: 'active', - source: 'log-server-02', - metric: 'disk_usage', - threshold: '90%', - currentValue: '94.8%', - duration: '32分钟', - startedAt: '2025-04-14 14:13:00', - description: '日志服务器磁盘空间即将耗尽', - }, - { - id: 'ALT-004', - name: '网络延迟过高', - severity: 'warning', - status: 'active', - source: 'api-gateway-01', - metric: 'network_latency', - threshold: '100ms', - currentValue: '245ms', - duration: '5分钟', - startedAt: '2025-04-14 14:40:00', - description: 'API网关响应延迟异常', - }, -]; +// ─── Dynamic AI analysis generator ─── +// Based on alert metric type, generates contextually relevant root cause analysis, +// impact assessment, recommendations, and auto-fix scripts. -const mockAIAnalysis: Record = { - 'ALT-001': { - rootCause: '检测到web-server-03上运行的Java应用出现内存泄漏,导致GC频繁触发,CPU使用率飙升。具体为订单服务(order-service)的某个定时任务未正确释放资源。', - impact: '影响用户下单体验,响应时间从平均200ms增加到1200ms,预计影响约35%的在线用户。', +function generateAIAnalysis(alert: AlertItem): AIAnalysis { + const metric = (alert.metric || '').toLowerCase(); + const target = alert.source || '目标主机'; + const isCritical = alert.severity === 'critical'; + + const baseConfidence = isCritical ? 88 + Math.floor(Math.random() * 8) : 75 + Math.floor(Math.random() * 12); + + // CPU alerts + if (metric.includes('cpu')) { + return { + rootCause: `检测到 ${target} 上 CPU 使用率达到 ${alert.currentValue},持续超过阈值 ${alert.threshold}。可能原因:1) 某进程存在死循环或高计算负载;2) GC 频繁触发(Java/Go 应用);3) 业务流量突增导致处理能力不足。`, + impact: isCritical + ? `CPU 持续高负载将导致服务响应延迟显著增加,严重时可能触发雪崩效应,影响全部依赖该节点的业务。` + : `CPU 使用率偏高,可能导致部分请求处理变慢,建议及时排查避免进一步恶化。`, + recommendations: [ + `登录 ${target} 执行 top/htop 定位高 CPU 进程`, + '检查是否有异常定时任务或批处理任务在运行', + `考虑临时扩容或将部分流量调度到其他节点`, + '优化应用代码,排查是否存在低效算法或资源泄漏', + ], + autoFixScript: `#!/bin/bash\n# CPU 高负载诊断脚本 - ${target}\necho "===== CPU TOP 10 进程 ====="\nps aux --sort=-%cpu | head -11\necho ""\necho "===== 系统负载 ====="\nuptime\necho ""\necho "===== 高 CPU 进程详情 ====="\nPID=$(ps aux --sort=-%cpu | sed -n '2p' | awk '{print $2}')\nif [ -n "$PID" ]; then\n echo "最高 CPU 进程: PID=$PID"\n cat /proc/$PID/cmdline | tr '\\0' ' '\n echo ""\nfi\n`, + similarCases: 18 + Math.floor(Math.random() * 8), + confidence: baseConfidence, + }; + } + + // Memory alerts + if (metric.includes('mem') || metric.includes('ram')) { + return { + rootCause: `检测到 ${target} 内存使用率达到 ${alert.currentValue},接近或超过阈值 ${alert.threshold}。常见原因:1) 应用内存泄漏,对象未被正确释放;2) 缓存配置过大;3) 数据库连接池未及时释放。`, + impact: isCritical + ? `内存即将耗尽可能触发 OOM Killer,导致关键进程被强制终止,服务中断风险极高。` + : `内存持续增长,长期可能导致 OOM,建议尽快排查内存使用情况。`, + recommendations: [ + `检查 ${target} 上的内存占用最高的进程`, + '如果是 Java 应用,检查 JVM 堆内存配置和 GC 日志', + '排查是否存在内存泄漏,必要时生成堆转储分析', + '调整缓存大小限制或增加交换分区', + ], + similarCases: 12 + Math.floor(Math.random() * 6), + confidence: baseConfidence, + }; + } + + // Disk alerts + if (metric.includes('disk') || metric.includes('space')) { + return { + rootCause: `检测到 ${target} 磁盘使用率达到 ${alert.currentValue},超过阈值 ${alert.threshold}。最常见原因:1) 日志文件未配置轮转/清理;2) 临时文件堆积;3) 数据库数据文件膨胀。`, + impact: isCritical + ? `磁盘空间不足可能导致服务无法写入数据/日志,数据库可能崩溃,文件系统变只读,影响范围极大。` + : `磁盘空间告警,如不及时清理可能导致服务异常。`, + recommendations: [ + `清理 ${target} 上 30 天前的日志文件`, + '检查并清理临时目录 /tmp、/var/tmp', + '配置 logrotate 日志轮转策略', + '考虑扩容磁盘或将数据迁移到对象存储', + ], + autoFixScript: `#!/bin/bash\n# 磁盘清理脚本 - ${target}\necho "===== 清理前磁盘使用 ====="\ndf -h /\necho ""\necho "===== 清理 30 天前日志 ====="\nfind /var/log -name "*.log" -mtime +30 -delete 2>/dev/null\nfind /var/log -name "*.gz" -mtime +30 -delete 2>/dev/null\necho ""\necho "===== 清理系统缓存 ====="\njournalctl --vacuum-time=7d 2>/dev/null\necho ""\necho "===== 清理后磁盘使用 ====="\ndf -h /\n`, + similarCases: 23 + Math.floor(Math.random() * 10), + confidence: baseConfidence + 3, + }; + } + + // Network alerts + if (metric.includes('net') || metric.includes('latency') || metric.includes('bandwidth')) { + return { + rootCause: `检测到 ${target} 网络指标异常(当前值 ${alert.currentValue}),可能原因:1) 上游依赖服务响应变慢;2) 网络带宽打满;3) DNS 解析延迟;4) 连接数达到上限。`, + impact: isCritical + ? `网络异常将导致所有经过该节点的请求超时,影响所有上游业务的可用性。` + : `网络延迟偏高,可能影响用户体验,建议排查网络链路。`, + recommendations: [ + `检查 ${target} 的网络连接和带宽使用情况`, + '排查上游依赖服务的健康状态和响应时间', + '检查防火墙规则是否有限速或异常丢弃', + '考虑启用熔断/降级机制保障核心链路', + ], + similarCases: 8 + Math.floor(Math.random() * 5), + confidence: baseConfidence - 5, + }; + } + + // Service availability alerts + if (metric.includes('service') || metric.includes('up') || metric.includes('avail')) { + return { + rootCause: `检测到 ${target} 服务不可用(指标 ${alert.metric} = ${alert.currentValue})。可能原因:1) 进程崩溃或被 OOM Killer 终止;2) 端口被占用或监听异常;3) 健康检查端点返回错误;4) 配置文件错误导致启动失败。`, + impact: isCritical + ? `服务完全不可用,所有依赖该服务的业务功能将中断,需要立即处理。` + : `服务可用性下降,部分功能可能受影响。`, + recommendations: [ + `立即检查 ${target} 的服务状态: systemctl status `, + '查看服务日志排查崩溃原因', + '检查端口占用和监听状态: ss -tlnp', + '必要时重启服务并监控恢复情况', + ], + autoFixScript: `#!/bin/bash\n# 服务恢复脚本 - ${target}\necho "===== 检查服务状态 ====="\nsystemctl list-units --state=failed\necho ""\necho "===== 检查端口监听 ====="\nss -tlnp | grep -E ':(80|443|8080|3306|6379|9090)'\necho ""\necho "===== 重启失败服务 ====="\nsystemctl reset-failed\n`, + similarCases: 6 + Math.floor(Math.random() * 4), + confidence: baseConfidence, + }; + } + + // Default / generic analysis + return { + rootCause: `检测到 ${target} 触发告警「${alert.name}」,当前指标值 ${alert.currentValue} 超过阈值 ${alert.threshold}。建议进一步排查具体指标数据和相关日志以确定根因。`, + impact: isCritical + ? `该告警级别为严重,可能影响业务正常运行,建议优先处理。` + : `该告警需要关注,持续恶化可能影响系统稳定性。`, recommendations: [ - '立即重启order-service服务释放内存(预计恢复时间2分钟)', - '检查并修复定时任务中的资源释放逻辑', - '考虑临时扩容到web-server-04分流部分流量', - '调整JVM堆内存参数,增加GC间隔', + `查看 ${target} 的监控仪表盘,确认指标趋势`, + '检查对应时间段的系统日志和应用日志', + '关联同时间段的其他告警,排查是否有连锁反应', + '若确认误报,调整告警规则阈值或持续时间', ], - autoFixScript: `#!/bin/bash -# 自动修复脚本 - CPU过高问题 -echo "正在诊断问题..." -PID=$(pgrep -f "order-service" | head -1) -echo "发现进程 PID: $PID" -echo "生成堆转储..." -jmap -dump:format=b,file=/tmp/heap_dump.hprof $PID -echo "重启服务..." -systemctl restart order-service -echo "服务已重启,监控中..." -`, - similarCases: 12, - confidence: 92, - }, - 'ALT-002': { - rootCause: '数据库连接池配置不当,连接数达到上限,导致内存持续增长。同时存在几个慢查询未优化。', - impact: '数据库响应变慢,部分查询超时,可能影响订单和库存模块。', - recommendations: [ - '优化慢查询SQL,添加适当索引', - '调整连接池大小配置', - '考虑启用查询缓存', - '分析并优化大表查询', - ], - similarCases: 8, - confidence: 87, - }, - 'ALT-003': { - rootCause: '日志文件未配置自动清理策略,/var/log目录下日志文件累积超过100GB。', - impact: '磁盘空间不足可能导致新日志无法写入,影响故障排查。', - recommendations: [ - '清理30天前的日志文件', - '配置logrotate自动轮转策略', - '考虑将历史日志归档到对象存储', - '设置磁盘使用率告警阈值', - ], - autoFixScript: `#!/bin/bash -# 自动清理日志脚本 -echo "清理30天前的日志..." -find /var/log -name "*.log" -mtime +30 -delete -find /var/log -name "*.log.*" -mtime +30 -delete -echo "压缩7-30天的日志..." -find /var/log -name "*.log" -mtime +7 -mtime -30 -exec gzip {} \; -echo "清理完成" -df -h /var/log -`, - similarCases: 23, - confidence: 95, - }, - 'ALT-004': { - rootCause: '上游依赖服务payment-service响应变慢,导致API网关等待时间增加。', - impact: '所有经过网关的请求都受到影响,用户体验下降。', - recommendations: [ - '检查payment-service的健康状态', - '增加API网关超时配置', - '考虑启用熔断机制', - '优化payment-service的性能', - ], - similarCases: 5, - confidence: 78, - }, -}; + similarCases: 5 + Math.floor(Math.random() * 5), + confidence: baseConfidence - 8, + }; +} + +// ─── Backend alert event → AlertItem mapping ─── +function mapAlertEvent(event: any): AlertItem { + const expr = event.rule?.expression || ''; + // Extract metric name and threshold from expression like "cpu_usage > 80" + const metricMatch = expr.match(/([\w_]+)\s*[><=!]+\s*([\d.]+)/); + const metric = metricMatch ? metricMatch[1] : expr || 'unknown'; + const threshold = metricMatch ? metricMatch[2] + '%' : '-'; + + const value = event.value ?? 0; + const isPercent = metric.includes('usage') || metric.includes('cpu') || metric.includes('disk') || metric.includes('mem'); + const currentValue = isPercent ? value.toFixed(1) + '%' : value.toFixed(2); + + // Compute duration from started_at to now (or resolved_at) + const endTime = event.resolved_at ? new Date(event.resolved_at) : new Date(); + const startTime = new Date(event.started_at); + const diffMin = Math.max(1, Math.round((endTime.getTime() - startTime.getTime()) / 60000)); + const duration = diffMin >= 60 ? `${Math.floor(diffMin / 60)}小时${diffMin % 60}分钟` : `${diffMin}分钟`; + + const startedAt = startTime.toLocaleString('zh-CN', { hour12: false }); + + return { + id: String(event.id), + name: event.rule_name || event.rule?.name || '未知告警', + severity: event.severity === 'critical' ? 'critical' : event.severity === 'warning' ? 'warning' : 'info', + status: event.status === 'firing' ? 'active' : 'resolved', + source: event.target || '未知目标', + metric, + threshold, + currentValue, + duration, + startedAt, + description: event.message && event.message !== `告警事件 ${event.id}` + ? event.message + : event.rule?.description || `${event.rule_name || '告警'}: ${event.target} 当前值 ${currentValue}`, + }; +} export function AIAlertAnalyzer() { + const [alerts, setAlerts] = useState([]); + const [loading, setLoading] = useState(true); const [selectedAlert, setSelectedAlert] = useState(null); const [isAnalyzing, setIsAnalyzing] = useState(false); const [analysis, setAnalysis] = useState(null); const [showScriptDialog, setShowScriptDialog] = useState(false); const [copied, setCopied] = useState(false); + useEffect(() => { + loadAlerts(); + }, []); + + const loadAlerts = async () => { + setLoading(true); + try { + const res: any = await alertApi.getAlerts({ page: 1, page_size: 50 }); + const list = res?.list || res || []; + setAlerts(list.map(mapAlertEvent)); + } catch (e) { + console.error('加载告警失败:', e); + setAlerts([]); + } finally { + setLoading(false); + } + }; + const handleAnalyze = (alert: AlertItem) => { setSelectedAlert(alert); setIsAnalyzing(true); setAnalysis(null); - // 模拟AI分析 + // Simulate AI analysis with dynamic generation setTimeout(() => { - setAnalysis(mockAIAnalysis[alert.id] || null); + setAnalysis(generateAIAnalysis(alert)); setIsAnalyzing(false); - }, 2000); + }, 1500); }; const handleAutoFix = () => { @@ -269,13 +319,24 @@ export function AIAlertAnalyzer() { 活跃告警 - {mockAlerts.filter(a => a.status === 'active').length} 个活跃 + {alerts.filter(a => a.status === 'active').length} 个活跃 - {mockAlerts.map((alert) => ( + {loading ? ( +
+ +

加载告警数据...

+
+ ) : alerts.length === 0 ? ( +
+ +

暂无告警事件

+
+ ) : ( + alerts.map((alert) => (
- ))} + )) + )}
diff --git a/frontend/src/components/cloud/DomainManager.tsx b/frontend/src/components/cloud/DomainManager.tsx index 2f57a88..cb6cdf2 100644 --- a/frontend/src/components/cloud/DomainManager.tsx +++ b/frontend/src/components/cloud/DomainManager.tsx @@ -336,9 +336,9 @@ export function DomainManager() {

到期时间

- {expireDate || '-'}{expireDate && ` (${daysUntilExpiry}天)`} + {expireDate ? expireDate.substring(0, 10) : '-'}{expireDate && ` (${daysUntilExpiry}天)`} {sslEnabled && sslExpireDate && ( - · SSL: {sslExpireDate} + · SSL: {sslExpireDate.substring(0, 10)} )}

diff --git a/frontend/src/components/dashboard/Dashboard.tsx b/frontend/src/components/dashboard/Dashboard.tsx index 138056c..d7d2ea0 100644 --- a/frontend/src/components/dashboard/Dashboard.tsx +++ b/frontend/src/components/dashboard/Dashboard.tsx @@ -452,7 +452,7 @@ function CertificateExpiryCard({ expCerts }: { expCerts: any[] }) {

{domain.name}

-

到期: {domain.expire_date}

+

到期: {domain.expire_date?.substring(0, 10)}

0 ? 'down' : 'neutral'} + trendValue={Number(serverStats?.error ?? 0) > 0 ? `${serverStats?.error} 台异常` : '运行正常'} color="blue" /> 0 ? 'up' : 'down'} + trendValue={`本周 ${alertStats?.week_count ?? 0} 条`} color="rose" /> { try { + const selectedTemplate = pipelineTemplates.find(t => t.id === newPipeline.template); + const stages = selectedTemplate + ? JSON.stringify(selectedTemplate.stages.map((name, i) => ({ + id: `stage-${i + 1}`, name, status: 'pending', duration: '-', logs: [], + }))) + : ''; + const code = newPipeline.name + .toLowerCase() + .replace(/[^a-z0-9]/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') || `pipeline-${Date.now()}`; await pipelineApi.createPipeline({ name: newPipeline.name, + code, description: newPipeline.description, - repository: newPipeline.repository, + source: newPipeline.repository, branch: newPipeline.branch, + type: 'deploy', + stages, }); setIsCreateDialogOpen(false); setNewPipeline({ name: '', description: '', repository: '', branch: 'main', template: '' });