设置界面更新

This commit is contained in:
Alex Yang
2025-11-27 01:37:53 +08:00
parent 6fc1283519
commit acf0ff6d96
16 changed files with 153434 additions and 126 deletions

View File

@@ -10,11 +10,12 @@ import (
"sync"
"time"
"github.com/gorilla/websocket"
"dns-server/config"
"dns-server/dns"
"dns-server/logger"
"dns-server/shield"
"github.com/gorilla/websocket"
)
// Server HTTP控制台服务器
@@ -24,7 +25,7 @@ type Server struct {
dnsServer *dns.Server
shieldManager *shield.ShieldManager
server *http.Server
// WebSocket相关字段
upgrader websocket.Upgrader
clients map[*websocket.Conn]bool
@@ -50,10 +51,10 @@ func NewServer(globalConfig *config.Config, dnsServer *dns.Server, shieldManager
clients: make(map[*websocket.Conn]bool),
broadcastChan: make(chan []byte, 100),
}
// 启动广播协程
go server.startBroadcastLoop()
return server
}
@@ -73,6 +74,8 @@ func (s *Server) Start() error {
// 添加统计相关接口
mux.HandleFunc("/api/top-blocked", s.handleTopBlockedDomains)
mux.HandleFunc("/api/top-resolved", s.handleTopResolvedDomains)
mux.HandleFunc("/api/top-clients", s.handleTopClients)
mux.HandleFunc("/api/top-domains", s.handleTopDomains)
mux.HandleFunc("/api/recent-blocked", s.handleRecentBlockedDomains)
mux.HandleFunc("/api/hourly-stats", s.handleHourlyStats)
mux.HandleFunc("/api/daily-stats", s.handleDailyStats)
@@ -131,27 +134,27 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
// 格式化平均响应时间为两位小数
formattedResponseTime := float64(int(dnsStats.AvgResponseTime*100)) / 100
// 构建响应数据,确保所有字段都反映服务器的真实状态
stats := map[string]interface{}{
"dns": map[string]interface{}{
"Queries": dnsStats.Queries,
"Blocked": dnsStats.Blocked,
"Allowed": dnsStats.Allowed,
"Errors": dnsStats.Errors,
"LastQuery": dnsStats.LastQuery,
"AvgResponseTime": formattedResponseTime,
"Queries": dnsStats.Queries,
"Blocked": dnsStats.Blocked,
"Allowed": dnsStats.Allowed,
"Errors": dnsStats.Errors,
"LastQuery": dnsStats.LastQuery,
"AvgResponseTime": formattedResponseTime,
"TotalResponseTime": dnsStats.TotalResponseTime,
"QueryTypes": dnsStats.QueryTypes,
"SourceIPs": dnsStats.SourceIPs,
"CpuUsage": dnsStats.CpuUsage,
"QueryTypes": dnsStats.QueryTypes,
"SourceIPs": dnsStats.SourceIPs,
"CpuUsage": dnsStats.CpuUsage,
},
"shield": shieldStats,
"topQueryType": topQueryType,
"activeIPs": activeIPCount,
"shield": shieldStats,
"topQueryType": topQueryType,
"activeIPs": activeIPCount,
"avgResponseTime": formattedResponseTime,
"cpuUsage": dnsStats.CpuUsage,
"time": time.Now(),
"cpuUsage": dnsStats.CpuUsage,
"time": time.Now(),
}
w.Header().Set("Content-Type", "application/json")
@@ -197,25 +200,25 @@ func (s *Server) handleWebSocketStats(w http.ResponseWriter, r *http.Request) {
case <-ticker.C:
// 获取最新统计数据
currentStats := s.buildStatsData()
// 检查数据是否有变化
if !s.areStatsEqual(lastStats, currentStats) {
// 数据有变化,发送更新
data, err := json.Marshal(map[string]interface{}{
"type": "stats_update",
"data": currentStats,
"time": time.Now(),
"type": "stats_update",
"data": currentStats,
"time": time.Now(),
})
if err != nil {
logger.Error(fmt.Sprintf("序列化统计数据失败: %v", err))
continue
}
if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
logger.Error(fmt.Sprintf("发送WebSocket消息失败: %v", err))
return
}
// 更新最后发送的数据
lastStats = currentStats
}
@@ -235,9 +238,9 @@ func (s *Server) handleWebSocketStats(w http.ResponseWriter, r *http.Request) {
func (s *Server) sendInitialStats(conn *websocket.Conn) error {
stats := s.buildStatsData()
data, err := json.Marshal(map[string]interface{}{
"type": "initial_data",
"data": stats,
"time": time.Now(),
"type": "initial_data",
"data": stats,
"time": time.Now(),
})
if err != nil {
return err
@@ -267,25 +270,25 @@ func (s *Server) buildStatsData() map[string]interface{} {
// 格式化平均响应时间
formattedResponseTime := float64(int(dnsStats.AvgResponseTime*100)) / 100
return map[string]interface{}{
"dns": map[string]interface{}{
"Queries": dnsStats.Queries,
"Blocked": dnsStats.Blocked,
"Allowed": dnsStats.Allowed,
"Errors": dnsStats.Errors,
"LastQuery": dnsStats.LastQuery,
"AvgResponseTime": formattedResponseTime,
"Queries": dnsStats.Queries,
"Blocked": dnsStats.Blocked,
"Allowed": dnsStats.Allowed,
"Errors": dnsStats.Errors,
"LastQuery": dnsStats.LastQuery,
"AvgResponseTime": formattedResponseTime,
"TotalResponseTime": dnsStats.TotalResponseTime,
"QueryTypes": dnsStats.QueryTypes,
"SourceIPs": dnsStats.SourceIPs,
"CpuUsage": dnsStats.CpuUsage,
"QueryTypes": dnsStats.QueryTypes,
"SourceIPs": dnsStats.SourceIPs,
"CpuUsage": dnsStats.CpuUsage,
},
"shield": shieldStats,
"topQueryType": topQueryType,
"activeIPs": activeIPCount,
"shield": shieldStats,
"topQueryType": topQueryType,
"activeIPs": activeIPCount,
"avgResponseTime": formattedResponseTime,
"cpuUsage": dnsStats.CpuUsage,
"cpuUsage": dnsStats.CpuUsage,
}
}
@@ -294,20 +297,20 @@ func (s *Server) areStatsEqual(stats1, stats2 map[string]interface{}) bool {
if stats1 == nil || stats2 == nil {
return false
}
// 只比较关键数值,避免频繁更新
if dns1, ok1 := stats1["dns"].(map[string]interface{}); ok1 {
if dns2, ok2 := stats2["dns"].(map[string]interface{}); ok2 {
// 检查主要计数器
if dns1["Queries"] != dns2["Queries"] ||
dns1["Blocked"] != dns2["Blocked"] ||
dns1["Allowed"] != dns2["Allowed"] ||
dns1["Errors"] != dns2["Errors"] {
dns1["Blocked"] != dns2["Blocked"] ||
dns1["Allowed"] != dns2["Allowed"] ||
dns1["Errors"] != dns2["Errors"] {
return false
}
}
}
return true
}
@@ -493,7 +496,7 @@ func (s *Server) handleQueryTypeStats(w http.ResponseWriter, r *http.Request) {
// 获取DNS统计数据
dnsStats := s.dnsServer.GetStats()
// 转换为前端需要的格式
result := make([]map[string]interface{}, 0, len(dnsStats.QueryTypes))
for queryType, count := range dnsStats.QueryTypes {
@@ -512,6 +515,74 @@ func (s *Server) handleQueryTypeStats(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(result)
}
// handleTopClients 处理TOP客户端请求
func (s *Server) handleTopClients(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取TOP客户端列表
clients := s.dnsServer.GetTopClients(10)
// 转换为前端需要的格式
result := make([]map[string]interface{}, len(clients))
for i, client := range clients {
result[i] = map[string]interface{}{
"ip": client.IP,
"count": client.Count,
"lastSeen": client.LastSeen,
}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}
// handleTopDomains 处理TOP域名请求
func (s *Server) handleTopDomains(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取TOP被屏蔽域名
blockedDomains := s.dnsServer.GetTopBlockedDomains(10)
// 获取TOP已解析域名
resolvedDomains := s.dnsServer.GetTopResolvedDomains(10)
// 合并并去重域名统计
domainMap := make(map[string]int64)
for _, domain := range blockedDomains {
domainMap[domain.Domain] += domain.Count
}
for _, domain := range resolvedDomains {
domainMap[domain.Domain] += domain.Count
}
// 转换为切片并排序
domainList := make([]map[string]interface{}, 0, len(domainMap))
for domain, count := range domainMap {
domainList = append(domainList, map[string]interface{}{
"domain": domain,
"count": count,
})
}
// 按计数降序排序
sort.Slice(domainList, func(i, j int) bool {
return domainList[i]["count"].(int64) > domainList[j]["count"].(int64)
})
// 返回限制数量
if len(domainList) > 10 {
domainList = domainList[:10]
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(domainList)
}
// handleShield 处理屏蔽规则管理请求
func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@@ -820,25 +891,25 @@ func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
}
stats := s.dnsServer.GetStats()
// 使用服务器的实际启动时间计算准确的运行时间
serverStartTime := s.dnsServer.GetStartTime()
uptime := time.Since(serverStartTime)
// 构建包含所有真实服务器统计数据的响应
status := map[string]interface{}{
"status": "running",
"queries": stats.Queries,
"blocked": stats.Blocked,
"allowed": stats.Allowed,
"errors": stats.Errors,
"lastQuery": stats.LastQuery,
"status": "running",
"queries": stats.Queries,
"blocked": stats.Blocked,
"allowed": stats.Allowed,
"errors": stats.Errors,
"lastQuery": stats.LastQuery,
"avgResponseTime": stats.AvgResponseTime,
"activeIPs": len(stats.SourceIPs),
"startTime": serverStartTime,
"uptime": uptime.Milliseconds(), // 转换为毫秒数,方便前端处理
"cpuUsage": stats.CpuUsage,
"timestamp": time.Now(),
"activeIPs": len(stats.SourceIPs),
"startTime": serverStartTime,
"uptime": uptime.Milliseconds(), // 转换为毫秒数,方便前端处理
"cpuUsage": stats.CpuUsage,
"timestamp": time.Now(),
}
w.Header().Set("Content-Type", "application/json")