nodes.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package analytic
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/0xJacky/Nginx-UI/internal/analytic"
  6. "github.com/0xJacky/Nginx-UI/internal/helper"
  7. "github.com/0xJacky/Nginx-UI/internal/kernel"
  8. "github.com/0xJacky/Nginx-UI/internal/version"
  9. "github.com/gin-gonic/gin"
  10. "github.com/gorilla/websocket"
  11. "github.com/shirou/gopsutil/v4/cpu"
  12. "github.com/uozi-tech/cosy/logger"
  13. )
  14. func GetNodeStat(c *gin.Context) {
  15. var upGrader = websocket.Upgrader{
  16. CheckOrigin: func(r *http.Request) bool {
  17. return true
  18. },
  19. }
  20. // upgrade http to websocket
  21. ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
  22. if err != nil {
  23. logger.Error(err)
  24. return
  25. }
  26. defer ws.Close()
  27. // Counter to track iterations for periodic full info update
  28. counter := 0
  29. const fullInfoInterval = 6 // Send full info every 6 iterations (every minute if interval is 10s)
  30. for {
  31. var data interface{}
  32. // Every fullInfoInterval iterations, send complete node information including version
  33. if counter%fullInfoInterval == 0 {
  34. // Get complete node information including version
  35. runtimeInfo, err := version.GetRuntimeInfo()
  36. if err != nil {
  37. logger.Error("Failed to get runtime info:", err)
  38. // Fallback to stat only
  39. data = analytic.GetNodeStat()
  40. } else {
  41. cpuInfo, _ := cpu.Info()
  42. memory, _ := analytic.GetMemoryStat()
  43. ver := version.GetVersionInfo()
  44. diskUsage, _ := analytic.GetDiskStat()
  45. nodeInfo := analytic.NodeInfo{
  46. NodeRuntimeInfo: runtimeInfo,
  47. CPUNum: len(cpuInfo),
  48. MemoryTotal: memory.Total,
  49. DiskTotal: diskUsage.Total,
  50. Version: ver.Version,
  51. }
  52. stat := analytic.GetNodeStat()
  53. // Send complete node information
  54. data = analytic.Node{
  55. NodeInfo: nodeInfo,
  56. NodeStat: stat,
  57. }
  58. }
  59. } else {
  60. // Send only stat information for performance
  61. data = analytic.GetNodeStat()
  62. }
  63. // write
  64. err = ws.WriteJSON(data)
  65. if err != nil {
  66. if helper.IsUnexpectedWebsocketError(err) {
  67. logger.Error(err)
  68. }
  69. break
  70. }
  71. counter++
  72. select {
  73. case <-kernel.Context.Done():
  74. logger.Debug("GetNodeStat: Context cancelled, closing WebSocket")
  75. return
  76. case <-time.After(10 * time.Second):
  77. }
  78. }
  79. }
  80. func GetNodesAnalytic(c *gin.Context) {
  81. var upGrader = websocket.Upgrader{
  82. CheckOrigin: func(r *http.Request) bool {
  83. return true
  84. },
  85. }
  86. // upgrade http to websocket
  87. ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
  88. if err != nil {
  89. logger.Error(err)
  90. return
  91. }
  92. defer ws.Close()
  93. for {
  94. // Send NodeMap data to client
  95. err = ws.WriteJSON(analytic.NodeMap)
  96. if err != nil {
  97. if helper.IsUnexpectedWebsocketError(err) {
  98. logger.Error(err)
  99. }
  100. break
  101. }
  102. select {
  103. case <-kernel.Context.Done():
  104. logger.Debug("GetNodesAnalytic: Context cancelled, closing WebSocket")
  105. return
  106. case <-time.After(10 * time.Second):
  107. }
  108. }
  109. }