fix: 修复架构分析报告指出的P0问题

**AI 告警分析(P0):**
- 替换 setTimeout 伪AI为真实 LLM 调用 (aiApi.chat)
- LLM 不可用时回退到规则模板(generateAIAnalysis)
- 移除 Math.random() 随机置信度/相似案例
- 分析结果从 LLM JSON 响应解析

**SSH HostKey 验证(P0):**
- webshell.go: InsecureIgnoreHostKey → TOFU 模式
- agent_install.go: 同步修复
- 首次连接记录 SHA256 fingerprint 到日志
- 后续连接校验 HostKey 一致性

**Ansible 执行引擎(P0):**
- ExecutePlaybook 从模拟改为真实 ansible-playbook 调用
- 支持 extra-vars 参数传递
- 5分钟超时保护
- ansible-playbook 未安装时返回明确错误
This commit is contained in:
attacker committed 2026-06-19 11:51:03 +00:00
1 parent a3cd0d3910
commit 9af0685e90
5 files changed
+167 -43

No files matched your search

@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { alertApi } from '@/services/api';
import { alertApi, aiApi } from '@/services/api';
import {
AlertTriangle,
Brain,
@@ -26,6 +26,26 @@ import {
import { AIAnalysisResult } from './AIAnalysisResult';
import { AutoFixDialog } from './AutoFixDialog';
// Parse LLM JSON response (may have markdown fences) into AIAnalysis
function parseAIResponse(raw: string, fallback: AIAnalysis): AIAnalysis {
try {
// Strip markdown code fences if present
let json = raw.replace(/```(?:json)?\s*\n?/g, '').replace(/```$/, '').trim();
const parsed = JSON.parse(json);
return {
rootCause: parsed.root_cause || parsed.rootCause || fallback.rootCause,
impact: parsed.impact || fallback.impact,
recommendations: Array.isArray(parsed.recommendations) ? parsed.recommendations : fallback.recommendations,
autoFixScript: parsed.auto_fix_script || parsed.autoFixScript || fallback.autoFixScript,
similarCases: typeof parsed.similar_cases === 'number' ? parsed.similar_cases : (parsed.similarCases ?? fallback.similarCases),
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : fallback.confidence,
};
} catch {
// Not valid JSON — return raw text as rootCause
return { ...fallback, rootCause: raw };
}
}
export function AIAlertAnalyzer() {
const [alerts, setAlerts] = useState<AlertItem[]>([]);
const [loading, setLoading] = useState(true);
@@ -53,16 +73,47 @@ export function AIAlertAnalyzer() {
}
};
// 触发 AI 分析(模拟 1.5s 思考,实际由 generateAIAnalysis 动态生成上下文相关结果)
const handleAnalyze = (alert: AlertItem) => {
// 触发 AI 分析 — 优先调用 LLM,失败/未配置时回退到规则模板
const handleAnalyze = async (alert: AlertItem) => {
setSelectedAlert(alert);
setIsAnalyzing(true);
setAnalysis(null);
setTimeout(() => {
setAnalysis(generateAIAnalysis(alert));
const fallback = generateAIAnalysis(alert);
try {
const prompt = `你是运维专家。请分析以下告警并以 JSON 格式回复(不要 markdown 代码块)。
告警名称:${alert.name}
严重程度:${alert.severity}
目标:${alert.source}
指标:${alert.metric} = ${alert.currentValue}(阈值 ${alert.threshold})
持续:${alert.duration}
描述:${alert.description}
返回 JSON 格式(字段名用 snake_case):
{
"root_cause": "根因分析(2-3句)",
"impact": "影响评估(1-2句)",
"recommendations": ["建议1", "建议2", "建议3"],
"auto_fix_script": "#!/bin/bash\\n...\\n(可选的自动修复脚本,无则 null)",
"similar_cases": 数字(类似案例数预估),
"confidence": 数字(0-100 置信度)
}`;
const res = await aiApi.chat([{ role: 'user', content: prompt }]);
const content = (res as any)?.content || (res as any)?.message?.content || '';
if (content) {
setAnalysis(parseAIResponse(content, fallback));
} else {
setAnalysis(fallback);
}
} catch {
// LLM 未配置或调用失败 → 回退到规则模板
setAnalysis(fallback);
} finally {
setIsAnalyzing(false);
}, 1500);
}
};
const handleAutoFix = () => {
@@ -25,15 +25,14 @@ export interface AIAnalysis {
confidence: number;
}
// ─── Dynamic AI analysis generator ───
// 基于告警指标类型,生成上下文相关的根因分析、影响评估、修复建议与自动修复脚本。
// ─── Dynamic AI analysis generator (rule-based fallback) ───
// 当 LLM 未配置或调用失败时,基于告警指标类型生成上下文相关的分析模板。
// confidence 设 0 表示规则模板(非 LLM),前端据此显示不同标识。
export 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 告警
if (metric.includes('cpu')) {
return {
@@ -48,8 +47,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'优化应用代码,排查是否存在低效算法或资源泄漏',
],
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,
similarCases: 0,
confidence: 0,
};
}
@@ -66,8 +65,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'排查是否存在内存泄漏,必要时生成堆转储分析',
'调整缓存大小限制或增加交换分区',
],
similarCases: 12 + Math.floor(Math.random() * 6),
confidence: baseConfidence,
similarCases: 0,
confidence: 0,
};
}
@@ -85,8 +84,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'考虑扩容磁盘或将数据迁移到对象存储',
],
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,
similarCases: 0,
confidence: 0,
};
}
@@ -103,8 +102,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'检查防火墙规则是否有限速或异常丢弃',
'考虑启用熔断/降级机制保障核心链路',
],
similarCases: 8 + Math.floor(Math.random() * 5),
confidence: baseConfidence - 5,
similarCases: 0,
confidence: 0,
};
}
@@ -122,8 +121,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'必要时重启服务并监控恢复情况',
],
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,
similarCases: 0,
confidence: 0,
};
}
@@ -139,8 +138,8 @@ export function generateAIAnalysis(alert: AlertItem): AIAnalysis {
'关联同时间段的其他告警,排查是否有连锁反应',
'若确认误报,调整告警规则阈值或持续时间',
],
similarCases: 5 + Math.floor(Math.random() * 5),
confidence: baseConfidence - 8,
similarCases: 0,
confidence: 0,
};
}