status.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. // GetDetailedStatus API 实现
  2. // 该功能用于解决 Issue #850,提供类似宝塔面板的 Nginx 负载监控功能
  3. // 返回详细的 Nginx 状态信息,包括请求统计、连接数、工作进程等数据
  4. package nginx
  5. import (
  6. "errors"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/0xJacky/Nginx-UI/internal/nginx"
  11. "github.com/gin-gonic/gin"
  12. "github.com/uozi-tech/cosy"
  13. "github.com/uozi-tech/cosy/logger"
  14. )
  15. // NginxPerformanceInfo 存储 Nginx 性能相关信息
  16. type NginxPerformanceInfo struct {
  17. // 基本状态信息
  18. nginx.StubStatusData
  19. // 进程相关信息
  20. nginx.NginxProcessInfo
  21. // 配置信息
  22. nginx.NginxConfigInfo
  23. }
  24. // GetDetailStatus 获取 Nginx 详细状态信息
  25. func GetDetailStatus(c *gin.Context) {
  26. response := nginx.GetPerformanceData()
  27. c.JSON(http.StatusOK, response)
  28. }
  29. // StreamDetailStatus 使用 SSE 流式推送 Nginx 详细状态信息
  30. func StreamDetailStatus(c *gin.Context) {
  31. // 设置 SSE 的响应头
  32. c.Header("Content-Type", "text/event-stream")
  33. c.Header("Cache-Control", "no-cache")
  34. c.Header("Connection", "keep-alive")
  35. c.Header("Access-Control-Allow-Origin", "*")
  36. // 创建上下文,当客户端断开连接时取消
  37. ctx := c.Request.Context()
  38. // 为防止 goroutine 泄漏,创建一个计时器通道
  39. ticker := time.NewTicker(5 * time.Second)
  40. defer ticker.Stop()
  41. // 立即发送一次初始数据
  42. sendPerformanceData(c)
  43. // 使用 goroutine 定期发送数据
  44. for {
  45. select {
  46. case <-ticker.C:
  47. // 发送性能数据
  48. if err := sendPerformanceData(c); err != nil {
  49. logger.Warn("Error sending SSE data:", err)
  50. return
  51. }
  52. case <-ctx.Done():
  53. // 客户端断开连接或请求被取消
  54. logger.Debug("Client closed connection")
  55. return
  56. }
  57. }
  58. }
  59. // sendPerformanceData 发送一次性能数据
  60. func sendPerformanceData(c *gin.Context) error {
  61. response := nginx.GetPerformanceData()
  62. // 发送 SSE 事件
  63. c.SSEvent("message", response)
  64. // 刷新缓冲区,确保数据立即发送
  65. c.Writer.Flush()
  66. return nil
  67. }
  68. // CheckStubStatus 获取 Nginx stub_status 模块状态
  69. func CheckStubStatus(c *gin.Context) {
  70. stubStatus := nginx.GetStubStatus()
  71. c.JSON(http.StatusOK, stubStatus)
  72. }
  73. // ToggleStubStatus 启用或禁用 stub_status 模块
  74. func ToggleStubStatus(c *gin.Context) {
  75. var json struct {
  76. Enable bool `json:"enable"`
  77. }
  78. if !cosy.BindAndValid(c, &json) {
  79. return
  80. }
  81. stubStatus := nginx.GetStubStatus()
  82. // 如果当前状态与期望状态相同,则无需操作
  83. if stubStatus.Enabled == json.Enable {
  84. c.JSON(http.StatusOK, stubStatus)
  85. return
  86. }
  87. var err error
  88. if json.Enable {
  89. err = nginx.EnableStubStatus()
  90. } else {
  91. err = nginx.DisableStubStatus()
  92. }
  93. if err != nil {
  94. cosy.ErrHandler(c, err)
  95. return
  96. }
  97. // 重新加载 Nginx 配置
  98. reloadOutput := nginx.Reload()
  99. if len(reloadOutput) > 0 && (strings.Contains(strings.ToLower(reloadOutput), "error") ||
  100. strings.Contains(strings.ToLower(reloadOutput), "failed")) {
  101. cosy.ErrHandler(c, errors.New("Reload Nginx failed"))
  102. return
  103. }
  104. // 检查操作后的状态
  105. newStubStatus := nginx.GetStubStatus()
  106. c.JSON(http.StatusOK, newStubStatus)
  107. }