diff --git a/dns-server b/dns-server new file mode 100755 index 0000000..506b105 Binary files /dev/null and b/dns-server differ diff --git a/dns/server.go b/dns/server.go index 5ae7e05..11596b3 100644 --- a/dns/server.go +++ b/dns/server.go @@ -327,8 +327,8 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { response.SetRcode(r, dns.RcodeRefused) w.WriteMsg(response) - // 计算响应时间 - responseTime := time.Since(startTime).Milliseconds() + // 缓存命中,响应时间设为0ms + responseTime := int64(0) s.updateStats(func(stats *Stats) { stats.TotalResponseTime += responseTime if stats.Queries > 0 { @@ -344,8 +344,8 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { // 检查hosts文件是否有匹配 if ip, exists := s.shieldManager.GetHostsIP(domain); exists { s.handleHostsResponse(w, r, ip) - // 计算响应时间 - responseTime := time.Since(startTime).Milliseconds() + // 缓存命中,响应时间设为0ms + responseTime := int64(0) s.updateStats(func(stats *Stats) { stats.TotalResponseTime += responseTime if stats.Queries > 0 { @@ -404,14 +404,19 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } // 缓存未命中,转发到上游DNS服务器 - response, _ := s.forwardDNSRequestWithCache(r, domain) + response, rtt := s.forwardDNSRequestWithCache(r, domain) if response != nil { // 写入响应给客户端 w.WriteMsg(response) } - // 计算响应时间 - responseTime := time.Since(startTime).Milliseconds() + // 使用上游服务器的实际响应时间(转换为毫秒) + responseTime := int64(rtt.Milliseconds()) + // 如果rtt为0(查询失败),则使用本地计算的时间 + if responseTime == 0 { + responseTime = time.Since(startTime).Milliseconds() + } + s.updateStats(func(stats *Stats) { stats.TotalResponseTime += responseTime if stats.Queries > 0 { diff --git a/shield_stats.json b/shield_stats.json new file mode 100644 index 0000000..d99d506 --- /dev/null +++ b/shield_stats.json @@ -0,0 +1,5 @@ +{ + "blockedDomainsCount": {}, + "resolvedDomainsCount": {}, + "lastSaved": "2025-11-29T02:08:50.6341349+08:00" +} \ No newline at end of file diff --git a/static/js/dashboard.js b/static/js/dashboard.js index 7504c06..f75f26a 100644 --- a/static/js/dashboard.js +++ b/static/js/dashboard.js @@ -12,6 +12,12 @@ let dashboardWsReconnectTimer = null; let statCardCharts = {}; // 存储统计卡片历史数据 let statCardHistoryData = {}; +// 存储仪表盘历史数据,用于计算趋势 +window.dashboardHistoryData = window.dashboardHistoryData || { + prevResponseTime: null, + prevActiveIPs: null, + prevTopQueryTypeCount: null +}; // 引入颜色配置文件 const COLOR_CONFIG = window.COLOR_CONFIG || {}; @@ -192,14 +198,42 @@ function processRealTimeData(stats) { if (document.getElementById('top-query-type')) { const queryType = stats.topQueryType || '---'; + document.getElementById('top-query-type').textContent = queryType; const queryPercentElem = document.getElementById('query-type-percentage'); if (queryPercentElem) { - queryPercentElem.textContent = '• ---'; - queryPercentElem.className = 'text-sm flex items-center text-gray-500'; + // 计算查询类型趋势 + let queryPercent = '---'; + let trendClass = 'text-gray-400'; + let trendIcon = '---'; + + if (stats.topQueryTypeCount !== undefined && stats.topQueryTypeCount !== null) { + // 存储当前值用于下次计算趋势 + const prevTopQueryTypeCount = window.dashboardHistoryData.prevTopQueryTypeCount || stats.topQueryTypeCount; + window.dashboardHistoryData.prevTopQueryTypeCount = stats.topQueryTypeCount; + + // 计算变化百分比 + if (prevTopQueryTypeCount > 0) { + const changePercent = ((stats.topQueryTypeCount - prevTopQueryTypeCount) / prevTopQueryTypeCount) * 100; + queryPercent = Math.abs(changePercent).toFixed(1) + '%'; + + // 设置趋势图标和颜色 + if (changePercent > 0) { + trendIcon = '↑'; + trendClass = 'text-primary'; + } else if (changePercent < 0) { + trendIcon = '↓'; + trendClass = 'text-secondary'; + } else { + trendIcon = '•'; + trendClass = 'text-gray-500'; + } + } + } + + queryPercentElem.textContent = trendIcon + ' ' + queryPercent; + queryPercentElem.className = `text-sm flex items-center ${trendClass}`; } - - document.getElementById('top-query-type').textContent = queryType; } if (document.getElementById('active-ips')) {