mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
234 lines
6.1 KiB
Go
234 lines
6.1 KiB
Go
package nginx
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/0xJacky/Nginx-UI/settings"
|
|
"github.com/pkg/errors"
|
|
"github.com/uozi-tech/cosy/logger"
|
|
)
|
|
|
|
// StubStatusInfo Store the stub_status module status
|
|
type StubStatusInfo struct {
|
|
Enabled bool `json:"stub_status_enabled"` // stub_status module is enabled
|
|
URL string `json:"stub_status_url"` // stub_status access address
|
|
}
|
|
|
|
type StubStatusData struct {
|
|
Active int `json:"active"`
|
|
Accepts int `json:"accepts"`
|
|
Handled int `json:"handled"`
|
|
Requests int `json:"requests"`
|
|
Reading int `json:"reading"`
|
|
Writing int `json:"writing"`
|
|
Waiting int `json:"waiting"`
|
|
}
|
|
|
|
const (
|
|
StubStatusPath = "/stub_status"
|
|
StubStatusHost = "127.0.0.1"
|
|
StubStatusProtocol = "http"
|
|
StubStatusAllow = "127.0.0.1"
|
|
StubStatusDeny = "all"
|
|
StubStatusConfigName = "stub_status_nginx-ui.conf"
|
|
)
|
|
|
|
// GetStubStatusData Get the stub_status module data
|
|
func GetStubStatusData() (bool, *StubStatusData, error) {
|
|
result := &StubStatusData{
|
|
Active: 0,
|
|
Accepts: 0,
|
|
Handled: 0,
|
|
Requests: 0,
|
|
Reading: 0,
|
|
Writing: 0,
|
|
Waiting: 0,
|
|
}
|
|
|
|
// Get the stub_status status information
|
|
enabled, statusURL := IsStubStatusEnabled()
|
|
logger.Debug("GetStubStatusData", "enabled", enabled, "statusURL", statusURL)
|
|
if !enabled {
|
|
return false, result, fmt.Errorf("stub_status is not enabled")
|
|
}
|
|
|
|
// Create an HTTP client
|
|
client := &http.Client{
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
|
|
// Send a request to get the stub_status data
|
|
resp, err := client.Get(statusURL)
|
|
if err != nil {
|
|
return enabled, result, fmt.Errorf("failed to get stub status: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read the response content
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return enabled, result, fmt.Errorf("failed to read response body: %v", err)
|
|
}
|
|
|
|
// Parse the response content
|
|
statusContent := string(body)
|
|
|
|
// Match the active connection number
|
|
activeRe := regexp.MustCompile(`Active connections:\s+(\d+)`)
|
|
if matches := activeRe.FindStringSubmatch(statusContent); len(matches) > 1 {
|
|
result.Active, _ = strconv.Atoi(matches[1])
|
|
}
|
|
|
|
// Match the request statistics information
|
|
serverRe := regexp.MustCompile(`(\d+)\s+(\d+)\s+(\d+)`)
|
|
if matches := serverRe.FindStringSubmatch(statusContent); len(matches) > 3 {
|
|
result.Accepts, _ = strconv.Atoi(matches[1])
|
|
result.Handled, _ = strconv.Atoi(matches[2])
|
|
result.Requests, _ = strconv.Atoi(matches[3])
|
|
}
|
|
|
|
// Match the read and write waiting numbers
|
|
connRe := regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`)
|
|
if matches := connRe.FindStringSubmatch(statusContent); len(matches) > 3 {
|
|
result.Reading, _ = strconv.Atoi(matches[1])
|
|
result.Writing, _ = strconv.Atoi(matches[2])
|
|
result.Waiting, _ = strconv.Atoi(matches[3])
|
|
}
|
|
|
|
return enabled, result, nil
|
|
}
|
|
|
|
// GetStubStatus Get the stub_status module status
|
|
func GetStubStatus() *StubStatusInfo {
|
|
enabled, statusURL := IsStubStatusEnabled()
|
|
return &StubStatusInfo{
|
|
Enabled: enabled,
|
|
URL: statusURL,
|
|
}
|
|
}
|
|
|
|
// IsStubStatusEnabled Check if the stub_status module is enabled and return the access address
|
|
// Only check the stub_status_nginx-ui.conf configuration file
|
|
func IsStubStatusEnabled() (bool, string) {
|
|
stubStatusConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
if _, err := os.Stat(stubStatusConfPath); os.IsNotExist(err) {
|
|
return false, ""
|
|
}
|
|
|
|
ngxConfig, err := ParseNgxConfig(stubStatusConfPath)
|
|
if err != nil {
|
|
return false, ""
|
|
}
|
|
|
|
// Find the stub_status configuration
|
|
for _, server := range ngxConfig.Servers {
|
|
protocol := StubStatusProtocol
|
|
host := StubStatusHost
|
|
port := settings.NginxSettings.GetStubStatusPort()
|
|
|
|
for _, location := range server.Locations {
|
|
// Check if the location content contains stub_status
|
|
if strings.Contains(location.Content, "stub_status") {
|
|
stubStatusURL := fmt.Sprintf("%s://%s:%d%s", protocol, host, port, StubStatusPath)
|
|
return true, stubStatusURL
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, ""
|
|
}
|
|
|
|
// EnableStubStatus Enable stub_status module
|
|
func EnableStubStatus() error {
|
|
enabled, _ := IsStubStatusEnabled()
|
|
if enabled {
|
|
return nil
|
|
}
|
|
|
|
return CreateStubStatusConfig()
|
|
}
|
|
|
|
// DisableStubStatus Disable stub_status module
|
|
func DisableStubStatus() error {
|
|
stubStatusConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
if _, err := os.Stat(stubStatusConfPath); os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
|
|
return os.Remove(stubStatusConfPath)
|
|
}
|
|
|
|
// CreateStubStatusConfig Create a new stub_status configuration file
|
|
func CreateStubStatusConfig() error {
|
|
httpConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
|
|
const stubStatusTemplate = `
|
|
# DO NOT EDIT THIS FILE, IT IS AUTO GENERATED BY NGINX-UI
|
|
# Nginx stub_status configuration for Nginx-UI
|
|
# Modified at {{.ModifiedTime}}
|
|
|
|
server {
|
|
listen {{.Port}}; # Use non-standard port to avoid conflicts
|
|
server_name {{.ServerName}};
|
|
|
|
# Status monitoring interface
|
|
location {{.StatusPath}} {
|
|
stub_status;
|
|
allow {{.AllowIP}}; # Only allow local access
|
|
deny {{.DenyAccess}};
|
|
}
|
|
}
|
|
`
|
|
|
|
type StubStatusTemplateData struct {
|
|
ModifiedTime string
|
|
Port uint
|
|
ServerName string
|
|
StatusPath string
|
|
AllowIP string
|
|
DenyAccess string
|
|
}
|
|
|
|
data := StubStatusTemplateData{
|
|
ModifiedTime: time.Now().Format(time.DateTime),
|
|
Port: settings.NginxSettings.GetStubStatusPort(),
|
|
ServerName: "localhost",
|
|
StatusPath: StubStatusPath,
|
|
AllowIP: StubStatusAllow,
|
|
DenyAccess: StubStatusDeny,
|
|
}
|
|
|
|
tmpl, err := template.New("stub_status").Parse(stubStatusTemplate)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to parse template")
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmpl.Execute(&buf, data); err != nil {
|
|
return errors.Wrap(err, "failed to execute template")
|
|
}
|
|
|
|
stubStatusConfig := buf.String()
|
|
|
|
ngxConfig, err := ParseNgxConfigByContent(stubStatusConfig)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to parse new nginx config")
|
|
}
|
|
|
|
ngxConfig.FileName = httpConfPath
|
|
configText, err := ngxConfig.BuildConfig()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to build nginx config")
|
|
}
|
|
|
|
return os.WriteFile(httpConfPath, []byte(configText), 0644)
|
|
}
|