import { useState, useEffect, useCallback } from 'react' import { FileText, Power, RefreshCw, X } from 'lucide-react' import { getSecurityAlerts, getSecurityLogs, getSecuritySettings, SecurityAlert, SecurityLog, updateSecuritySettings } from '../services/api' const typeLabels: Record = { port_scan: '端口扫描', horizontal_scan: '横向扫描', brute_force: '暴力破解', ddos: 'DDoS/大规模扫描', spam: '垃圾邮件', malware: '恶意软件', mining: '挖矿连接', proxy: '代理/VPN/Tor', reflection: 'UDP反射放大', } const severityLabels: Record = { critical: '严重', high: '高危', medium: '中危', low: '低危', } export default function Security() { const [alerts, setAlerts] = useState([]) const [autoShutdown, setAutoShutdown] = useState(false) const [loading, setLoading] = useState(true) const [savingSettings, setSavingSettings] = useState(false) const [logAlert, setLogAlert] = useState(null) const [logs, setLogs] = useState([]) const [logsLoading, setLogsLoading] = useState(false) const fetchData = useCallback(async () => { try { const [alertRes, settingsRes] = await Promise.all([getSecurityAlerts(), getSecuritySettings()]) if (alertRes.data.data) setAlerts(alertRes.data.data) if (settingsRes.data.data) setAutoShutdown(settingsRes.data.data.auto_shutdown) } catch (err) { console.error(err) } finally { setLoading(false) } }, []) useEffect(() => { fetchData() const interval = setInterval(fetchData, 10000) return () => clearInterval(interval) }, [fetchData]) const handleAutoShutdownChange = async () => { const next = !autoShutdown setAutoShutdown(next) setSavingSettings(true) try { const res = await updateSecuritySettings({ auto_shutdown: next }) if (res.data.data) setAutoShutdown(res.data.data.auto_shutdown) } catch (err) { console.error(err) setAutoShutdown(!next) } finally { setSavingSettings(false) } } const openLogs = async (alert: SecurityAlert) => { setLogAlert(alert) setLogs([]) setLogsLoading(true) try { const res = await getSecurityLogs(alert.container_name) setLogs(filterRelatedLogs(res.data.data || [], alert)) } catch (err) { console.error(err) setLogs([]) } finally { setLogsLoading(false) } } if (loading) { return (
) } return (

安全告警

告警列表 ({alerts.length})

{alerts.length === 0 ? (
暂无安全告警
) : (
{alerts.map((alert) => ( ))}
时间 等级 类型 容器 源IP 目标 次数 详情
{alert.timestamp} {typeLabels[alert.type] || alert.type} {alert.container_name} {alert.source_ip || '-'} {formatTarget(alert)} {alert.count}
{alert.detail}
)}
{logAlert && (

相关连接记录

{logAlert.container_name} · {typeLabels[logAlert.type] || logAlert.type} · {formatTarget(logAlert)}

{logAlert.log_line && (
告警原始记录
{logAlert.log_line}
)} {logsLoading ? (
正在加载连接记录...
) : logs.length === 0 ? (
暂无可用连接记录。历史告警对应的 conntrack 记录可能已经过期。
) : ( {logs.map((log, index) => ( ))}
协议 状态 源地址 目标地址
{log.protocol || '-'} {log.state || '-'} {formatEndpoint(log.src_ip, log.src_port)} {formatEndpoint(log.dst_ip, log.dst_port)}
)}
)}
) } function SeverityBadge({ severity }: { severity: string }) { const colors: Record = { critical: 'bg-red-100 text-red-700', high: 'bg-amber-100 text-amber-700', medium: 'bg-gray-100 text-gray-700', low: 'bg-gray-50 text-gray-500', } return ( {severityLabels[severity] || severity} ) } function formatTarget(alert: SecurityAlert): string { if (alert.target_ip === '*') return '*' if (!alert.target_ip) return '-' return alert.target_port > 0 ? `${alert.target_ip}:${alert.target_port}` : alert.target_ip } function filterRelatedLogs(logs: SecurityLog[], alert: SecurityAlert): SecurityLog[] { return logs.filter((log) => { if (alert.source_ip && log.src_ip !== alert.source_ip) return false if (alert.target_ip && alert.target_ip !== '*' && log.dst_ip !== alert.target_ip) return false if (alert.target_port > 0 && log.dst_port !== alert.target_port) return false return true }) } function formatEndpoint(ip: string, port: number): string { if (!ip) return '-' return port > 0 ? `${ip}:${port}` : ip }