فهرست منبع

feat: enhance node statistics reporting with version info and improved message handling

0xJacky 3 هفته پیش
والد
کامیت
d37a463436
2فایلهای تغییر یافته به همراه86 افزوده شده و 10 حذف شده
  1. 48 1
      api/analytic/nodes.go
  2. 38 9
      internal/analytic/node_record.go

+ 48 - 1
api/analytic/nodes.go

@@ -7,8 +7,10 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/analytic"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/kernel"
+	"github.com/0xJacky/Nginx-UI/internal/version"
 	"github.com/gin-gonic/gin"
 	"github.com/gorilla/websocket"
+	"github.com/shirou/gopsutil/v4/cpu"
 	"github.com/uozi-tech/cosy/logger"
 )
 
@@ -27,9 +29,52 @@ func GetNodeStat(c *gin.Context) {
 
 	defer ws.Close()
 
+	// Counter to track iterations for periodic full info update
+	counter := 0
+	const fullInfoInterval = 6 // Send full info every 6 iterations (every minute if interval is 10s)
+
 	for {
+		var data interface{}
+
+		// Every fullInfoInterval iterations, send complete node information including version
+		if counter%fullInfoInterval == 0 {
+			// Get complete node information including version
+			runtimeInfo, err := version.GetRuntimeInfo()
+			if err != nil {
+				logger.Error("Failed to get runtime info:", err)
+				// Fallback to stat only
+				data = analytic.GetNodeStat()
+			} else {
+				cpuInfo, _ := cpu.Info()
+				memory, _ := analytic.GetMemoryStat()
+				ver := version.GetVersionInfo()
+				diskUsage, _ := analytic.GetDiskStat()
+
+				nodeInfo := analytic.NodeInfo{
+					NodeRuntimeInfo: runtimeInfo,
+					CPUNum:          len(cpuInfo),
+					MemoryTotal:     memory.Total,
+					DiskTotal:       diskUsage.Total,
+					Version:         ver.Version,
+				}
+
+				stat := analytic.GetNodeStat()
+
+				// Send complete node information
+				data = analytic.Node{
+					NodeInfo: nodeInfo,
+					NodeStat: stat,
+				}
+
+				logger.Debugf("Sending complete node info including version: %s", ver.Version)
+			}
+		} else {
+			// Send only stat information for performance
+			data = analytic.GetNodeStat()
+		}
+
 		// write
-		err = ws.WriteJSON(analytic.GetNodeStat())
+		err = ws.WriteJSON(data)
 		if err != nil {
 			if helper.IsUnexpectedWebsocketError(err) {
 				logger.Error(err)
@@ -37,6 +82,8 @@ func GetNodeStat(c *gin.Context) {
 			break
 		}
 
+		counter++
+
 		select {
 		case <-kernel.Context.Done():
 			return

+ 38 - 9
internal/analytic/node_record.go

@@ -2,6 +2,7 @@ package analytic
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"sync"
@@ -650,11 +651,12 @@ func nodeAnalyticRecord(env *model.Environment, ctx context.Context) error {
 		_ = c.Close()
 	}()
 
-	var nodeStat NodeStat
 	messageCount := 0
 
 	for {
-		err = c.ReadJSON(&nodeStat)
+		// Use json.RawMessage to handle both NodeStat and Node types
+		var rawMsg json.RawMessage
+		err = c.ReadJSON(&rawMsg)
 		if err != nil {
 			if helper.IsUnexpectedWebsocketError(err) {
 				logger.Debugf("nodeAnalyticRecord: Unexpected WebSocket error for environment ID: %d, error: %v", env.ID, err)
@@ -667,15 +669,42 @@ func nodeAnalyticRecord(env *model.Environment, ctx context.Context) error {
 		messageCount++
 		logger.Debugf("nodeAnalyticRecord: Received message #%d from environment ID: %d", messageCount, env.ID)
 
-		// set online
-		nodeStat.Status = true
-		nodeStat.ResponseAt = time.Now()
-
 		mutex.Lock()
 		if NodeMap[env.ID] != nil {
-			NodeMap[env.ID].NodeStat = nodeStat
-			logger.Debugf("nodeAnalyticRecord: Updated NodeStat for environment ID: %d, Status: %t, ResponseAt: %v",
-				env.ID, nodeStat.Status, nodeStat.ResponseAt)
+			// Try to unmarshal as complete Node first (contains both NodeInfo and NodeStat)
+			var fullNode Node
+			if err := json.Unmarshal(rawMsg, &fullNode); err == nil && fullNode.Version != "" {
+				// Check if version has changed
+				oldVersion := NodeMap[env.ID].Version
+				if oldVersion != "" && oldVersion != fullNode.Version {
+					logger.Infof("nodeAnalyticRecord: Version updated for environment ID: %d, from %s to %s",
+						env.ID, oldVersion, fullNode.Version)
+				}
+
+				// This is a complete Node with version info - update everything
+				NodeMap[env.ID].NodeInfo = fullNode.NodeInfo
+				NodeMap[env.ID].NodeStat = fullNode.NodeStat
+				// Ensure status and response time are set
+				NodeMap[env.ID].NodeStat.Status = true
+				NodeMap[env.ID].NodeStat.ResponseAt = time.Now()
+
+				logger.Debugf("nodeAnalyticRecord: Updated complete Node info for environment ID: %d, Version: %s, Status: %t, ResponseAt: %v",
+					env.ID, fullNode.Version, NodeMap[env.ID].NodeStat.Status, NodeMap[env.ID].NodeStat.ResponseAt)
+			} else {
+				// Fall back to NodeStat only
+				var nodeStat NodeStat
+				if err := json.Unmarshal(rawMsg, &nodeStat); err == nil {
+					// set online
+					nodeStat.Status = true
+					nodeStat.ResponseAt = time.Now()
+
+					NodeMap[env.ID].NodeStat = nodeStat
+					logger.Debugf("nodeAnalyticRecord: Updated NodeStat for environment ID: %d, Status: %t, ResponseAt: %v",
+						env.ID, nodeStat.Status, nodeStat.ResponseAt)
+				} else {
+					logger.Debugf("nodeAnalyticRecord: Failed to unmarshal message for environment ID: %d, error: %v", env.ID, err)
+				}
+			}
 		} else {
 			logger.Debugf("nodeAnalyticRecord: Warning - Node not found in NodeMap for environment ID: %d", env.ID)
 		}