whois
This commit is contained in:
+200
-148
@@ -202,7 +202,7 @@ async function loadThreatsDatabase() {
|
||||
console.log('尝试加载威胁数据库...');
|
||||
|
||||
// 使用新的API端点获取威胁数据库
|
||||
const response = await fetch('/api/domain-info?threats');
|
||||
const response = await fetch('/api/threat/domain');
|
||||
console.log('威胁数据库API请求状态:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -217,12 +217,12 @@ async function loadThreatsDatabase() {
|
||||
}
|
||||
|
||||
// 解析API返回的数据
|
||||
threatsDatabase = data.map(item => ({
|
||||
type: item.type || '',
|
||||
name: item.name || '',
|
||||
riskLevel: item.level || '',
|
||||
domain: item.domain || ''
|
||||
})).filter(item => item.type && item.name && item.riskLevel && item.domain);
|
||||
threatsDatabase = data.map(domain => ({
|
||||
type: '未知',
|
||||
name: '未知',
|
||||
riskLevel: '2',
|
||||
domain: domain
|
||||
})).filter(item => item.domain);
|
||||
|
||||
isDatabaseLoaded = true;
|
||||
console.log('威胁数据库加载成功:', threatsDatabase.length, '条记录');
|
||||
@@ -348,150 +348,84 @@ function getMockDNSLogs() {
|
||||
];
|
||||
}
|
||||
|
||||
// 匹配威胁数据库与 DNS 查询日志
|
||||
// 从威胁告警 API 获取威胁告警数据
|
||||
async function matchThreatsWithLogs() {
|
||||
try {
|
||||
// 加载 DNS 查询日志
|
||||
const logs = await loadDNSLogs();
|
||||
console.log('DNS 查询日志加载完成,记录数:', logs.length);
|
||||
console.log('开始从威胁告警 API 获取数据...');
|
||||
|
||||
// 提取所有唯一的域名
|
||||
const uniqueDomains = [...new Set(logs.filter(log => log && log.domain).map(log => log.domain))];
|
||||
console.log('唯一域名数量:', uniqueDomains.length);
|
||||
// 使用新的威胁告警 API 端点
|
||||
const response = await fetchWithRetry('/api/alert');
|
||||
|
||||
if (uniqueDomains.length === 0) {
|
||||
console.log('没有有效的域名,返回空结果');
|
||||
console.log('威胁告警 API 返回数据:', response);
|
||||
|
||||
if (!response || !response.alerts) {
|
||||
console.error('威胁告警 API 返回数据格式错误');
|
||||
return [];
|
||||
}
|
||||
|
||||
// 检查缓存
|
||||
const cachedResults = threatCache.getCachedResult();
|
||||
if (cachedResults) {
|
||||
console.log('使用缓存的威胁数据');
|
||||
// 使用缓存数据匹配
|
||||
return matchThreatsFromCache(logs, cachedResults);
|
||||
}
|
||||
const alerts = response.alerts;
|
||||
console.log('威胁告警 API 返回告警数:', alerts.length);
|
||||
|
||||
console.log('开始批量查询威胁数据库...');
|
||||
|
||||
// 批量查询威胁
|
||||
const response = await fetchWithRetry('/api/threat/batch', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domains: uniqueDomains
|
||||
})
|
||||
// 转换告警数据格式
|
||||
const matchedThreats = alerts.map((alert, index) => {
|
||||
// 转换风险等级格式
|
||||
let risk;
|
||||
switch (alert.level) {
|
||||
case 'high':
|
||||
risk = 'high';
|
||||
break;
|
||||
case 'medium':
|
||||
risk = 'medium';
|
||||
break;
|
||||
case 'low':
|
||||
risk = 'low';
|
||||
break;
|
||||
default:
|
||||
risk = 'medium';
|
||||
}
|
||||
|
||||
// 转换威胁类型格式
|
||||
let typeStr;
|
||||
switch (alert.type) {
|
||||
case 'phishing':
|
||||
typeStr = 'phishing';
|
||||
break;
|
||||
case 'malware':
|
||||
typeStr = 'malware';
|
||||
break;
|
||||
case 'botnet':
|
||||
typeStr = 'botnet';
|
||||
break;
|
||||
case 'dga':
|
||||
typeStr = 'dga';
|
||||
break;
|
||||
default:
|
||||
typeStr = 'suspicious';
|
||||
}
|
||||
|
||||
return {
|
||||
id: alert.id || index + 1,
|
||||
timestamp: alert.timestamp,
|
||||
type: typeStr,
|
||||
domain: alert.domain,
|
||||
sourceIp: alert.sourceIP,
|
||||
risk: risk,
|
||||
status: alert.resolved ? (alert.action === 'blocked' ? 'blocked' : 'allowed') : 'monitored',
|
||||
threatName: alert.description,
|
||||
threatType: alert.type,
|
||||
riskLevel: alert.level
|
||||
};
|
||||
});
|
||||
|
||||
const { results } = response;
|
||||
console.log('批量查询完成,结果数:', results.length);
|
||||
|
||||
// 缓存结果
|
||||
threatCache.cacheResults(results);
|
||||
|
||||
// 匹配日志和威胁信息
|
||||
return matchThreatsFromResults(logs, results);
|
||||
console.log('威胁告警数据转换完成,记录数:', matchedThreats.length);
|
||||
return matchedThreats;
|
||||
} catch (error) {
|
||||
console.error('匹配威胁失败:', error);
|
||||
console.error('获取威胁告警失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 从缓存结果匹配威胁
|
||||
function matchThreatsFromCache(logs, cachedResults) {
|
||||
// 构建域名到威胁信息的映射
|
||||
const threatMap = new Map();
|
||||
cachedResults.forEach(result => {
|
||||
if (result.isThreat && result.data) {
|
||||
threatMap.set(result.domain, result.data);
|
||||
}
|
||||
});
|
||||
|
||||
return matchThreatsFromMap(logs, threatMap);
|
||||
}
|
||||
|
||||
// 从批量查询结果匹配威胁
|
||||
function matchThreatsFromResults(logs, results) {
|
||||
// 构建域名到威胁信息的映射
|
||||
const threatMap = new Map();
|
||||
results.forEach(result => {
|
||||
if (result.isThreat && result.data) {
|
||||
threatMap.set(result.domain, result.data);
|
||||
console.log('发现威胁域名:', result.domain);
|
||||
}
|
||||
});
|
||||
|
||||
return matchThreatsFromMap(logs, threatMap);
|
||||
}
|
||||
|
||||
// 从威胁映射匹配日志
|
||||
function matchThreatsFromMap(logs, threatMap) {
|
||||
const matchedThreats = [];
|
||||
let id = 1;
|
||||
|
||||
logs.forEach(log => {
|
||||
const threatData = threatMap.get(log.domain);
|
||||
if (threatData) {
|
||||
const parts = threatData.split(',');
|
||||
if (parts.length >= 4) {
|
||||
const [type, name, riskLevel, domain] = parts;
|
||||
|
||||
// 转换风险等级格式
|
||||
let risk;
|
||||
switch (riskLevel) {
|
||||
case '1':
|
||||
risk = 'high';
|
||||
break;
|
||||
case '2':
|
||||
risk = 'medium';
|
||||
break;
|
||||
case '3':
|
||||
risk = 'low';
|
||||
break;
|
||||
default:
|
||||
risk = 'medium';
|
||||
}
|
||||
|
||||
// 转换威胁类型格式
|
||||
let typeStr;
|
||||
switch (type) {
|
||||
case '钓鱼网站':
|
||||
typeStr = 'phishing';
|
||||
break;
|
||||
case '木马':
|
||||
typeStr = 'malware';
|
||||
break;
|
||||
case '僵尸网络':
|
||||
typeStr = 'botnet';
|
||||
break;
|
||||
case 'DGA 域名':
|
||||
typeStr = 'dga';
|
||||
break;
|
||||
default:
|
||||
typeStr = 'suspicious';
|
||||
}
|
||||
|
||||
matchedThreats.push({
|
||||
id: id++,
|
||||
timestamp: log.timestamp,
|
||||
type: typeStr,
|
||||
domain: log.domain,
|
||||
sourceIp: log.clientIP,
|
||||
risk: risk,
|
||||
status: log.result === 'blocked' ? 'blocked' : 'monitored',
|
||||
threatName: name,
|
||||
threatType: type,
|
||||
riskLevel: riskLevel
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('威胁匹配完成,匹配记录数:', matchedThreats.length);
|
||||
return matchedThreats;
|
||||
}
|
||||
|
||||
// 生成威胁统计数据
|
||||
function generateThreatStats(threats) {
|
||||
@@ -575,19 +509,29 @@ function showLoadingStates() {
|
||||
|
||||
// 图表区域显示加载状态
|
||||
const trendChart = document.getElementById('threat-trend-chart');
|
||||
if (trendChart && trendChart.parentNode) {
|
||||
trendChart.parentNode.innerHTML = '<div class="flex items-center justify-center h-48 sm:h-64"><i class="fa fa-spinner fa-spin text-4xl text-primary"></i></div>';
|
||||
if (trendChart) {
|
||||
trendChart.style.display = 'none';
|
||||
const loadingDiv = document.createElement('div');
|
||||
loadingDiv.id = 'trend-chart-loading';
|
||||
loadingDiv.className = 'flex items-center justify-center h-48 sm:h-64';
|
||||
loadingDiv.innerHTML = '<i class="fa fa-spinner fa-spin text-4xl text-primary"></i>';
|
||||
trendChart.parentNode.appendChild(loadingDiv);
|
||||
}
|
||||
|
||||
const riskChart = document.getElementById('risk-distribution-chart');
|
||||
if (riskChart && riskChart.parentNode) {
|
||||
riskChart.parentNode.innerHTML = '<div class="flex items-center justify-center h-48 sm:h-64"><i class="fa fa-spinner fa-spin text-4xl text-primary"></i></div>';
|
||||
if (riskChart) {
|
||||
riskChart.style.display = 'none';
|
||||
const loadingDiv = document.createElement('div');
|
||||
loadingDiv.id = 'risk-chart-loading';
|
||||
loadingDiv.className = 'flex items-center justify-center h-48 sm:h-64';
|
||||
loadingDiv.innerHTML = '<i class="fa fa-spinner fa-spin text-4xl text-primary"></i>';
|
||||
riskChart.parentNode.appendChild(loadingDiv);
|
||||
}
|
||||
|
||||
// 列表区域显示加载状态和进度提示
|
||||
const threatList = document.getElementById('threat-list');
|
||||
if (threatList) {
|
||||
threatList.innerHTML = '<tr><td colspan="7" class="py-8 text-center text-gray-500"><i class="fa fa-spinner fa-spin mr-2"></i>正在查询威胁数据,请稍候...</td></tr>';
|
||||
threatList.innerHTML = '<tr><td colspan="8" class="py-8 text-center text-gray-500"><i class="fa fa-spinner fa-spin mr-2"></i>正在查询威胁数据,请稍候...</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,13 +584,9 @@ async function initThreatsPage() {
|
||||
const database = await loadThreatsDatabase();
|
||||
console.log('威胁数据库加载完成,记录数:', database.length);
|
||||
|
||||
console.log('开始加载 DNS 查询日志...');
|
||||
const logs = await loadDNSLogs();
|
||||
console.log('DNS 查询日志加载完成,记录数:', logs.length);
|
||||
|
||||
console.log('开始匹配威胁...');
|
||||
console.log('开始获取威胁告警数据...');
|
||||
const threats = await matchThreatsWithLogs();
|
||||
console.log('威胁匹配完成,匹配记录数:', threats.length);
|
||||
console.log('威胁告警数据获取完成,记录数:', threats.length);
|
||||
|
||||
// 更新威胁数据
|
||||
threatData.threats = threats;
|
||||
@@ -670,6 +610,7 @@ async function initThreatsPage() {
|
||||
|
||||
populateThreatList();
|
||||
bindFilterEvents();
|
||||
bindAlertActionEvents();
|
||||
console.log('页面组件初始化完成');
|
||||
} catch (error) {
|
||||
console.error('初始化威胁告警页面失败:', error);
|
||||
@@ -707,6 +648,15 @@ function renderThreatTrendChart() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除加载动画
|
||||
const loadingDiv = document.getElementById('trend-chart-loading');
|
||||
if (loadingDiv) {
|
||||
loadingDiv.remove();
|
||||
}
|
||||
|
||||
// 显示 canvas 元素
|
||||
canvas.style.display = 'block';
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 销毁已存在的图表
|
||||
@@ -769,6 +719,15 @@ function renderRiskDistributionChart() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除加载动画
|
||||
const loadingDiv = document.getElementById('risk-chart-loading');
|
||||
if (loadingDiv) {
|
||||
loadingDiv.remove();
|
||||
}
|
||||
|
||||
// 显示 canvas 元素
|
||||
canvas.style.display = 'block';
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const summary = threatData.summary;
|
||||
|
||||
@@ -832,7 +791,7 @@ function populateThreatList(filteredThreats = null) {
|
||||
|
||||
// 如果没有数据
|
||||
if (currentPageData.length === 0) {
|
||||
threatList.innerHTML = '<tr><td colspan="7" class="py-8 text-center text-gray-500">暂无威胁告警数据</td></tr>';
|
||||
threatList.innerHTML = '<tr><td colspan="8" class="py-8 text-center text-gray-500">暂无威胁告警数据</td></tr>';
|
||||
updatePaginationState(threats.length);
|
||||
renderPagination();
|
||||
return;
|
||||
@@ -861,7 +820,8 @@ function populateThreatList(filteredThreats = null) {
|
||||
// 获取状态显示文本和样式
|
||||
const statusInfo = {
|
||||
'blocked': { text: '已屏蔽', class: 'bg-green-100 text-green-800' },
|
||||
'monitored': { text: '监控中', class: 'bg-yellow-100 text-yellow-800' }
|
||||
'monitored': { text: '监控中', class: 'bg-yellow-100 text-yellow-800' },
|
||||
'allowed': { text: '已放行', class: 'bg-blue-100 text-blue-800' }
|
||||
}[threat.status];
|
||||
|
||||
// 设置行内容
|
||||
@@ -877,6 +837,14 @@ function populateThreatList(filteredThreats = null) {
|
||||
<td class="py-2 sm:py-3 px-2 sm:px-4 text-sm">
|
||||
<span class="px-2 py-1 rounded-full text-xs ${statusInfo.class}">${statusInfo.text}</span>
|
||||
</td>
|
||||
<td class="py-2 sm:py-3 px-2 sm:px-4 text-sm">
|
||||
${!threat.resolved ? `
|
||||
<div class="flex space-x-2">
|
||||
<button class="alert-action-btn block px-2 py-1 bg-red-500 text-white text-xs rounded hover:bg-red-600" data-alert-id="${threat.id}" data-action="blocked">屏蔽</button>
|
||||
<button class="alert-action-btn block px-2 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600" data-alert-id="${threat.id}" data-action="allowed">放行</button>
|
||||
</div>
|
||||
` : '<span class="text-gray-500 text-xs">已处理</span>'}
|
||||
</td>
|
||||
`;
|
||||
|
||||
// 添加行到列表
|
||||
@@ -888,6 +856,9 @@ function populateThreatList(filteredThreats = null) {
|
||||
|
||||
// 渲染分页控件
|
||||
renderPagination();
|
||||
|
||||
// 绑定告警操作事件
|
||||
bindAlertActionEvents();
|
||||
}
|
||||
|
||||
// 更新分页状态
|
||||
@@ -1079,6 +1050,87 @@ function bindFilterEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定告警操作事件
|
||||
function bindAlertActionEvents() {
|
||||
const actionButtons = document.querySelectorAll('.alert-action-btn');
|
||||
|
||||
actionButtons.forEach(button => {
|
||||
button.addEventListener('click', async function() {
|
||||
const alertId = this.getAttribute('data-alert-id');
|
||||
const action = this.getAttribute('data-action');
|
||||
|
||||
console.log(`处理告警 ${alertId},动作: ${action}`);
|
||||
|
||||
// 显示加载状态
|
||||
const originalText = this.textContent;
|
||||
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
|
||||
this.disabled = true;
|
||||
|
||||
try {
|
||||
// 发送请求到告警解决 API
|
||||
const response = await fetchWithRetry('/api/alert/resolve', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
alertId: alertId,
|
||||
action: action
|
||||
})
|
||||
});
|
||||
|
||||
console.log('告警解决 API 返回:', response);
|
||||
|
||||
if (response.status === 'success') {
|
||||
// 刷新威胁告警数据
|
||||
await refreshThreatData();
|
||||
console.log('告警处理成功');
|
||||
} else {
|
||||
throw new Error('告警处理失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理告警失败:', error);
|
||||
alert('处理告警失败,请重试');
|
||||
} finally {
|
||||
this.textContent = originalText;
|
||||
this.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新威胁告警数据
|
||||
async function refreshThreatData() {
|
||||
try {
|
||||
console.log('刷新威胁告警数据...');
|
||||
|
||||
// 清除缓存
|
||||
threatCache.clear();
|
||||
|
||||
// 重新加载数据
|
||||
const threats = await matchThreatsWithLogs();
|
||||
|
||||
// 更新威胁数据
|
||||
threatData.threats = threats;
|
||||
threatData.summary = generateThreatStats(threats);
|
||||
threatData.trends = generateThreatTrends(threats);
|
||||
|
||||
// 重置分页
|
||||
paginationState.currentPage = 1;
|
||||
|
||||
// 重新渲染页面
|
||||
populateThreatStats();
|
||||
renderThreatTrendChart();
|
||||
renderRiskDistributionChart();
|
||||
applyFilters();
|
||||
|
||||
console.log('威胁数据刷新完成');
|
||||
} catch (error) {
|
||||
console.error('刷新威胁数据失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 应用过滤器
|
||||
function applyFilters() {
|
||||
const riskFilter = document.getElementById('threat-filter-risk')?.value || 'all';
|
||||
|
||||
Reference in New Issue
Block a user