mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
132 lines
3.1 KiB
Go
132 lines
3.1 KiB
Go
// GetDetailedStatus API 实现
|
||
// 该功能用于解决 Issue #850,提供类似宝塔面板的 Nginx 负载监控功能
|
||
// 返回详细的 Nginx 状态信息,包括请求统计、连接数、工作进程等数据
|
||
package nginx
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/uozi-tech/cosy"
|
||
"github.com/uozi-tech/cosy/logger"
|
||
)
|
||
|
||
// NginxPerformanceInfo 存储 Nginx 性能相关信息
|
||
type NginxPerformanceInfo struct {
|
||
// 基本状态信息
|
||
nginx.StubStatusData
|
||
|
||
// 进程相关信息
|
||
nginx.NginxProcessInfo
|
||
|
||
// 配置信息
|
||
nginx.NginxConfigInfo
|
||
}
|
||
|
||
// GetDetailStatus 获取 Nginx 详细状态信息
|
||
func GetDetailStatus(c *gin.Context) {
|
||
response := nginx.GetPerformanceData()
|
||
c.JSON(http.StatusOK, response)
|
||
}
|
||
|
||
// StreamDetailStatus 使用 SSE 流式推送 Nginx 详细状态信息
|
||
func StreamDetailStatus(c *gin.Context) {
|
||
// 设置 SSE 的响应头
|
||
c.Header("Content-Type", "text/event-stream")
|
||
c.Header("Cache-Control", "no-cache")
|
||
c.Header("Connection", "keep-alive")
|
||
c.Header("Access-Control-Allow-Origin", "*")
|
||
|
||
// 创建上下文,当客户端断开连接时取消
|
||
ctx := c.Request.Context()
|
||
|
||
// 为防止 goroutine 泄漏,创建一个计时器通道
|
||
ticker := time.NewTicker(5 * time.Second)
|
||
defer ticker.Stop()
|
||
|
||
// 立即发送一次初始数据
|
||
sendPerformanceData(c)
|
||
|
||
// 使用 goroutine 定期发送数据
|
||
for {
|
||
select {
|
||
case <-ticker.C:
|
||
// 发送性能数据
|
||
if err := sendPerformanceData(c); err != nil {
|
||
logger.Warn("Error sending SSE data:", err)
|
||
return
|
||
}
|
||
case <-ctx.Done():
|
||
// 客户端断开连接或请求被取消
|
||
logger.Debug("Client closed connection")
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// sendPerformanceData 发送一次性能数据
|
||
func sendPerformanceData(c *gin.Context) error {
|
||
response := nginx.GetPerformanceData()
|
||
|
||
// 发送 SSE 事件
|
||
c.SSEvent("message", response)
|
||
|
||
// 刷新缓冲区,确保数据立即发送
|
||
c.Writer.Flush()
|
||
return nil
|
||
}
|
||
|
||
// CheckStubStatus 获取 Nginx stub_status 模块状态
|
||
func CheckStubStatus(c *gin.Context) {
|
||
stubStatus := nginx.GetStubStatus()
|
||
|
||
c.JSON(http.StatusOK, stubStatus)
|
||
}
|
||
|
||
// ToggleStubStatus 启用或禁用 stub_status 模块
|
||
func ToggleStubStatus(c *gin.Context) {
|
||
var json struct {
|
||
Enable bool `json:"enable"`
|
||
}
|
||
|
||
if !cosy.BindAndValid(c, &json) {
|
||
return
|
||
}
|
||
|
||
stubStatus := nginx.GetStubStatus()
|
||
|
||
// 如果当前状态与期望状态相同,则无需操作
|
||
if stubStatus.Enabled == json.Enable {
|
||
c.JSON(http.StatusOK, stubStatus)
|
||
return
|
||
}
|
||
|
||
var err error
|
||
if json.Enable {
|
||
err = nginx.EnableStubStatus()
|
||
} else {
|
||
err = nginx.DisableStubStatus()
|
||
}
|
||
|
||
if err != nil {
|
||
cosy.ErrHandler(c, err)
|
||
return
|
||
}
|
||
|
||
// 重新加载 Nginx 配置
|
||
reloadOutput := nginx.Reload()
|
||
if len(reloadOutput) > 0 && (strings.Contains(strings.ToLower(reloadOutput), "error") ||
|
||
strings.Contains(strings.ToLower(reloadOutput), "failed")) {
|
||
cosy.ErrHandler(c, errors.New("Reload Nginx failed"))
|
||
return
|
||
}
|
||
|
||
// 检查操作后的状态
|
||
newStubStatus := nginx.GetStubStatus()
|
||
|
||
c.JSON(http.StatusOK, newStubStatus)
|
||
}
|