This commit is contained in:
Alex Yang
2026-04-01 12:22:55 +08:00
parent 61789061ce
commit efebce3c39
46 changed files with 4797716 additions and 462145 deletions
+91 -37
View File
@@ -10,27 +10,80 @@ let logsChart = null;
let currentSortField = 'timestamp'; // 默认按时间排序,显示最新记录
let currentSortDirection = 'desc'; // 默认降序
// IP地理位置缓存(检查是否已经存在,避免重复声明)
// IP 地理位置缓存(检查是否已经存在,避免重复声明)
if (typeof ipGeolocationCache === 'undefined') {
var ipGeolocationCache = {};
var GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 缓存有效期24小时
var ipGeolocationCacheOrder = []; // 维护缓存插入顺序,用于 LRU
var GEOLOCATION_CACHE_MAX_SIZE = 1000; // 最大缓存 1000 条记录
var GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 缓存有效期 24 小时
}
// 获取IP地理位置信息
// LRU 缓存辅助函数:更新缓存顺序,将指定 IP 移到最后(最近使用)
function updateCacheOrder(ip) {
const index = ipGeolocationCacheOrder.indexOf(ip);
if (index > -1) {
// 已存在,移除旧位置
ipGeolocationCacheOrder.splice(index, 1);
}
// 添加到末尾(最近使用)
ipGeolocationCacheOrder.push(ip);
}
// LRU 缓存辅助函数:淘汰最少使用的缓存项
function evictLRUCache() {
while (ipGeolocationCacheOrder.length >= GEOLOCATION_CACHE_MAX_SIZE) {
// 淘汰最久未使用的(数组第一个)
const oldestIP = ipGeolocationCacheOrder.shift();
if (oldestIP) {
delete ipGeolocationCache[oldestIP];
}
}
}
// LRU 缓存辅助函数:清理过期缓存
function cleanupExpiredCache() {
const now = Date.now();
const expiredIPs = [];
// 找出所有过期的 IP
for (const ip of ipGeolocationCacheOrder) {
if (ipGeolocationCache[ip] && (now - ipGeolocationCache[ip].timestamp) >= GEOLOCATION_CACHE_EXPIRY) {
expiredIPs.push(ip);
}
}
// 删除过期缓存
for (const ip of expiredIPs) {
delete ipGeolocationCache[ip];
const orderIndex = ipGeolocationCacheOrder.indexOf(ip);
if (orderIndex > -1) {
ipGeolocationCacheOrder.splice(orderIndex, 1);
}
}
}
// 获取 IP 地理位置信息(优化版,带 LRU 缓存管理)
async function getIpGeolocation(ip) {
// 检查是否为内网IP
// 检查是否为内网 IP
if (isPrivateIP(ip)) {
return "内网 内网";
}
// 定期清理过期缓存(每 100 次请求清理一次)
if (Math.random() < 0.01) {
cleanupExpiredCache();
}
// 检查缓存
const now = Date.now();
if (ipGeolocationCache[ip] && (now - ipGeolocationCache[ip].timestamp) < GEOLOCATION_CACHE_EXPIRY) {
// 缓存命中,更新使用顺序
updateCacheOrder(ip);
return ipGeolocationCache[ip].location;
}
try {
// 使用whois.pconline.com.cn API获取IP地理位置
// 使用 whois.pconline.com.cn API 获取 IP 地理位置
const url = `https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`;
const response = await fetch(url, {
headers: {
@@ -47,19 +100,25 @@ async function getIpGeolocation(ip) {
let location = "未知 未知";
if (data && data.addr) {
// 直接使用addr字段作为完整的地理位置信息
// 直接使用 addr 字段作为完整的地理位置信息
location = data.addr;
}
// 如果缓存已满,先淘汰最少使用的项
if (Object.keys(ipGeolocationCache).length >= GEOLOCATION_CACHE_MAX_SIZE) {
evictLRUCache();
}
// 保存到缓存
ipGeolocationCache[ip] = {
location: location,
timestamp: now
};
updateCacheOrder(ip);
return location;
} catch (error) {
console.error('获取IP地理位置失败:', error);
console.error('获取 IP 地理位置失败:', error);
return "未知 未知";
}
}
@@ -880,39 +939,27 @@ async function loadLogs() {
}
// 构建缓存键,包含所有查询参数
const cacheKey = `logs_${logsPerPage}_${currentPage}_${currentFilter}_${currentSearch}_${currentSortField}_${currentSortDirection}`;
// 已禁用缓存,每次都从服务器获取最新数据
// const cacheKey = `logs_${logsPerPage}_${currentPage}_${currentFilter}_${currentSearch}_${currentSortField}_${currentSortDirection}`;
// 检查是否有有效的缓存数据
// 检查是否有有效的缓存数据(已禁用)
/*
const cachedLogs = window.pageDataCache && window.pageDataCache.getCache(cacheKey);
if (cachedLogs) {
console.log('使用缓存的日志数据');
// 确保cachedLogs是数组
const logsArray = Array.isArray(cachedLogs.logs) ? cachedLogs.logs : [];
// 确保totalLogs是有效的
const totalLogs = cachedLogs.totalLogs || logsArray.length;
// 计算总页数
totalPages = Math.ceil(totalLogs / logsPerPage);
// 更新日志表格
await updateLogsTable(logsArray);
// 绑定操作按钮事件
bindActionButtonsEvents();
// 更新分页信息
updateLogsPagination();
// 重新初始化列宽调节功能,确保新添加的行也能继承列宽设置
initResizableColumns();
// 隐藏加载状态
if (loadingEl) {
loadingEl.classList.add('hidden');
}
return;
}
*/
// 构建请求URL
let endpoint = `/logs/query?limit=${logsPerPage}&offset=${(currentPage - 1) * logsPerPage}`;
@@ -959,13 +1006,15 @@ async function loadLogs() {
// 计算总页数
totalPages = Math.ceil(totalLogs / logsPerPage);
// 存储数据到缓存
// 禁用缓存存储,每次都从服务器获取最新数据
/*
if (window.pageDataCache) {
window.pageDataCache.setCache(cacheKey, {
logs: logsArray,
totalLogs: totalLogs
});
}
*/
// 更新日志表格
await updateLogsTable(logsArray);
@@ -1148,11 +1197,12 @@ async function updateLogsTable(logs) {
</td>
<td class="py-3 px-4 text-sm">
<div class="font-medium flex items-center relative">
${log.dnssec ? '<i class="fa fa-lock text-green-500 mr-1" title="DNSSEC已启用"></i>' : ''}
${log.dnssec ? '<i class="fa fa-lock text-green-500 mr-1" title="DNSSEC 已启用"></i>' : ''}
<div class="tracker-icon-container relative">
${isTracker ? '<i class="fa fa-eye text-red-500 mr-1"></i>' : '<i class="fa fa-eye-slash text-gray-300 mr-1"></i>'}
${trackerTooltip}
</div>
<img src="images/whois.svg" alt="WHOIS" class="w-4 h-4 mr-1 inline-block cursor-pointer hover:opacity-75" onclick="event.stopPropagation(); window.location.href = window.location.pathname + '#whois'; setTimeout(() => { const input = document.getElementById('whois-domain-input'); if(input && '${log.domain}') { input.value = '${log.domain}'; if(typeof searchDomainInfo === 'function') { searchDomainInfo(); } } }, 200);" title="查看域名信息" />
${log.domain}
</div>
<div class="text-xs text-gray-500 mt-1">类型: ${log.queryType}, <span class="${statusClass}">${statusText}</span>, <span class="${cacheStatusClass}">${log.fromCache ? '缓存' : '非缓存'}</span>${log.dnssec ? ', <span class="text-green-500"><i class="fa fa-lock"></i> DNSSEC</span>' : ''}${log.edns ? ', <span class="text-blue-500"><i class="fa fa-exchange"></i> EDNS</span>' : ''}</div>
@@ -1407,8 +1457,13 @@ function setupLogsReconnect() {
}, reconnectDelay);
}
// 从WebSocket更新日志统计数据
// 从 WebSocket 更新日志统计数据 - 添加页面可见性检查
function updateLogsStatsFromWebSocket(stats) {
// 页面不可见时跳过处理,节省资源
if (document.hidden) {
return;
}
try {
// 更新统计卡片
if (stats.dns) {
@@ -1430,7 +1485,7 @@ function updateLogsStatsFromWebSocket(stats) {
document.getElementById('logs-block-rate').textContent = blockRate + '%';
}
} catch (error) {
console.error('从WebSocket更新日志统计数据失败:', error);
console.error('从 WebSocket 更新日志统计数据失败:', error);
}
}
@@ -1786,7 +1841,13 @@ async function showLogDetailModal(log) {
const domainInfoDiv = document.createElement('div');
domainInfoDiv.className = 'col-span-1 md:col-span-2 space-y-1';
domainInfoDiv.innerHTML = `
<div class="text-xs text-gray-500 dark:text-gray-400">域名信息</div>
<div class="text-xs text-gray-500 dark:text-gray-400 flex justify-between items-center">
<span>域名信息</span>
<button onclick="event.stopPropagation(); const modal = this.closest('.fixed'); if(modal && modal.parentElement === document.body) { modal.remove(); } window.location.href = window.location.pathname + '#whois'; setTimeout(() => { const input = document.getElementById('whois-domain-input'); if(input && '${domain}') { input.value = '${domain}'; if(typeof searchDomainInfo === 'function') { searchDomainInfo(); } } }, 200);" class="text-primary hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 text-xs flex items-center transition-colors" title="查看详细 WHOIS 信息">
<img src="images/whois.svg" alt="WHOIS" class="w-4 h-4 mr-1" />
查看更多
</button>
</div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100 p-3 bg-gray-50 dark:bg-gray-700 rounded-md border border-gray-200 dark:border-gray-600 w-full">
${domainInfo ? `
<div class="flex items-center mb-2">
@@ -2143,11 +2204,4 @@ function initLogDetailModal() {
});
}
// 定期更新日志统计数据(备用方案)
setInterval(() => {
// 只有在查询日志页面时才更新
if (window.location.hash === '#logs') {
loadLogsStats();
// 不自动更新日志详情,只更新统计数据
}
}, 30000); // 每30秒更新一次