feat(wip): self check

This commit is contained in:
Jacky 2025-01-22 16:21:33 +08:00
parent 5911462f90
commit ded74bbe0a
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
40 changed files with 1418 additions and 69 deletions

View file

@ -39,6 +39,23 @@ func GetConfPath(dir ...string) (confPath string) {
return joined
}
func GetConfEntryPath() (path string) {
if settings.NginxSettings.ConfigPath == "" {
out := getNginxV()
r, _ := regexp.Compile("--conf-path=(.*.conf)")
match := r.FindStringSubmatch(out)
if len(match) < 1 {
logger.Error("nginx.GetConfEntryPath len(match) < 1")
return ""
}
path = match[1]
} else {
path = settings.NginxSettings.ConfigPath
}
return
}
func GetPIDPath() (path string) {
if settings.NginxSettings.PIDPath == "" {
out := getNginxV()
@ -53,7 +70,7 @@ func GetPIDPath() (path string) {
path = settings.NginxSettings.PIDPath
}
return path
return
}
func GetSbinPath() (path string) {
@ -66,7 +83,7 @@ func GetSbinPath() (path string) {
}
path = match[1]
return path
return
}
func GetAccessLogPath() (path string) {
@ -83,7 +100,7 @@ func GetAccessLogPath() (path string) {
path = settings.NginxSettings.AccessLogPath
}
return path
return
}
func GetErrorLogPath() string {

View file

@ -0,0 +1,67 @@
package self_check
import (
"os"
"github.com/0xJacky/Nginx-UI/internal/nginx"
)
// CheckSitesDirectory checks if sites-available/sites-enabled directory exists
func CheckSitesDirectory() error {
// check sites-available directory
if _, err := os.Stat(nginx.GetConfPath("sites-available")); os.IsNotExist(err) {
return ErrSitesAvailableNotExist
}
// check sites-enabled directory
if _, err := os.Stat(nginx.GetConfPath("sites-enabled")); os.IsNotExist(err) {
return ErrSitesEnabledNotExist
}
return nil
}
// CheckStreamDirectory checks if stream-available/stream-enabled directory exists
func CheckStreamDirectory() error {
// check stream-available directory
if _, err := os.Stat(nginx.GetConfPath("streams-available")); os.IsNotExist(err) {
return ErrStreamAvailableNotExist
}
// check stream-enabled directory
if _, err := os.Stat(nginx.GetConfPath("streams-enabled")); os.IsNotExist(err) {
return ErrStreamEnabledNotExist
}
return nil
}
// FixSitesDirectory creates sites-available/sites-enabled directory
func FixSitesDirectory() error {
// create sites-available directory
if err := os.MkdirAll(nginx.GetConfPath("sites-available"), 0755); err != nil {
return err
}
// create sites-enabled directory
if err := os.MkdirAll(nginx.GetConfPath("sites-enabled"), 0755); err != nil {
return err
}
return nil
}
// FixStreamDirectory creates stream-available/stream-enabled directory
func FixStreamDirectory() error {
// create stream-available directory
if err := os.MkdirAll(nginx.GetConfPath("streams-available"), 0755); err != nil {
return err
}
// create stream-enabled directory
if err := os.MkdirAll(nginx.GetConfPath("streams-enabled"), 0755); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,19 @@
package self_check
import "github.com/uozi-tech/cosy"
var (
e = cosy.NewErrorScope("self_check")
ErrTaskNotFound = e.New(4040, "Task not found")
ErrFailedToReadNginxConf = e.New(4041, "Failed to read nginx.conf")
ErrParseNginxConf = e.New(5001, "Failed to parse nginx.conf")
ErrNginxConfNoHttpBlock = e.New(4042, "Nginx conf no http block")
ErrNginxConfNotIncludeSitesEnabled = e.New(4043, "Nginx conf not include sites-enabled")
ErrorNginxConfNoStreamBlock = e.New(4044, "Nginx conf no stream block")
ErrNginxConfNotIncludeStreamEnabled = e.New(4045, "Nginx conf not include stream-enabled")
ErrFailedToCreateBackup = e.New(5001, "Failed to create backup")
ErrSitesAvailableNotExist = e.New(4046, "Sites-available directory not exist")
ErrSitesEnabledNotExist = e.New(4047, "Sites-enabled directory not exist")
ErrStreamAvailableNotExist = e.New(4048, "Stream-available directory not exist")
ErrStreamEnabledNotExist = e.New(4049, "Stream-enabled directory not exist")
)

View file

@ -0,0 +1,168 @@
package self_check
import (
"fmt"
"os"
"time"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/spf13/cast"
"github.com/tufanbarisyildirim/gonginx/config"
"github.com/tufanbarisyildirim/gonginx/dumper"
"github.com/tufanbarisyildirim/gonginx/parser"
)
// CheckNginxConfIncludeSites checks if nginx.conf include sites-enabled
func CheckNginxConfIncludeSites() error {
path := nginx.GetConfEntryPath()
content, err := os.ReadFile(path)
if err != nil {
return ErrFailedToReadNginxConf
}
// parse nginx.conf
p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
c, err := p.Parse()
if err != nil {
return ErrParseNginxConf
}
// find http block
for _, v := range c.Block.Directives {
if v.GetName() == "http" {
// find include sites-enabled
for _, directive := range v.GetBlock().GetDirectives() {
if directive.GetName() == "include" && len(directive.GetParameters()) > 0 &&
directive.GetParameters()[0] == nginx.GetConfPath("sites-enabled/*") {
return nil
}
}
return ErrNginxConfNotIncludeSitesEnabled
}
}
return ErrNginxConfNoHttpBlock
}
// CheckNginxConfIncludeStreams checks if nginx.conf include streams-enabled
func CheckNginxConfIncludeStreams() error {
path := nginx.GetConfEntryPath()
content, err := os.ReadFile(path)
if err != nil {
return ErrFailedToReadNginxConf
}
// parse nginx.conf
p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
c, err := p.Parse()
if err != nil {
return ErrParseNginxConf
}
// find http block
for _, v := range c.Block.Directives {
if v.GetName() == "stream" {
// find include sites-enabled
for _, directive := range v.GetBlock().GetDirectives() {
if directive.GetName() == "include" && len(directive.GetParameters()) > 0 &&
directive.GetParameters()[0] == nginx.GetConfPath("streams-enabled/*") {
return nil
}
}
return ErrNginxConfNotIncludeStreamEnabled
}
}
return ErrorNginxConfNoStreamBlock
}
// FixNginxConfIncludeSites attempts to fix nginx.conf include sites-enabled
func FixNginxConfIncludeSites() error {
path := nginx.GetConfEntryPath()
content, err := os.ReadFile(path)
if err != nil {
return ErrFailedToReadNginxConf
}
// create a backup file (+.bak.timestamp)
backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
err = os.WriteFile(backupPath, content, 0644)
if err != nil {
return ErrFailedToCreateBackup
}
// parse nginx.conf
p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
c, err := p.Parse()
if err != nil {
return ErrParseNginxConf
}
// find http block
for _, v := range c.Block.Directives {
if v.GetName() == "http" {
// add include sites-enabled/* to http block
includeDirective := &config.Directive{
Name: "include",
Parameters: []string{nginx.GetConfPath("sites-enabled/*")},
}
realBlock := v.GetBlock().(*config.HTTP)
realBlock.Directives = append(realBlock.Directives, includeDirective)
// write to file
return os.WriteFile(path, []byte(dumper.DumpBlock(c.Block, dumper.IndentedStyle)), 0644)
}
}
// if no http block, append http block with include sites-enabled/*
content = append(content, []byte(fmt.Sprintf("\nhttp {\n\tinclude %s;\n}\n", nginx.GetConfPath("sites-enabled/*")))...)
return os.WriteFile(path, content, 0644)
}
// FixNginxConfIncludeStreams attempts to fix nginx.conf include streams-enabled
func FixNginxConfIncludeStreams() error {
path := nginx.GetConfEntryPath()
content, err := os.ReadFile(path)
if err != nil {
return ErrFailedToReadNginxConf
}
// create a backup file (+.bak.timestamp)
backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
err = os.WriteFile(backupPath, content, 0644)
if err != nil {
return ErrFailedToCreateBackup
}
// parse nginx.conf
p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
c, err := p.Parse()
if err != nil {
return ErrParseNginxConf
}
// find stream block
for _, v := range c.Block.Directives {
if v.GetName() == "stream" {
// add include streams-enabled/* to stream block
includeDirective := &config.Directive{
Name: "include",
Parameters: []string{nginx.GetConfPath("streams-enabled/*")},
}
realBlock := v.GetBlock().(*config.Block)
realBlock.Directives = append(realBlock.Directives, includeDirective)
// write to file
return os.WriteFile(path, []byte(dumper.DumpBlock(c.Block, dumper.IndentedStyle)), 0644)
}
}
// if no stream block, append stream block with include streams-enabled/*
content = append(content, []byte(fmt.Sprintf("\nstream {\n\tinclude %s;\n}\n", nginx.GetConfPath("streams-enabled/*")))...)
return os.WriteFile(path, content, 0644)
}

View file

@ -0,0 +1,123 @@
package self_check
import (
"errors"
"os"
"strings"
"testing"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/stretchr/testify/assert"
"github.com/uozi-tech/cosy"
"github.com/uozi-tech/cosy/logger"
)
func TestCheckNginxConfIncludeSites(t *testing.T) {
// test ok
logger.Init("debug")
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/ok.conf"
var result *cosy.Error
errors.As(CheckNginxConfIncludeSites(), &result)
assert.Nil(t, result)
// test 4041 nginx.conf not found
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/4041.conf"
errors.As(CheckNginxConfIncludeSites(), &result)
assert.Equal(t, int32(4041), result.Code)
// test 5001 nginx.conf parse error
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/5001.conf"
errors.As(CheckNginxConfIncludeSites(), &result)
assert.Equal(t, int32(5001), result.Code)
// test 4042 nginx.conf no http block
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/no-http-block.conf"
errors.As(CheckNginxConfIncludeSites(), &result)
assert.Equal(t, int32(4042), result.Code)
// test 4043 nginx.conf not include sites-enabled
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled.conf"
errors.As(CheckNginxConfIncludeSites(), &result)
assert.Equal(t, int32(4043), result.Code)
}
func TestCheckNginxConfIncludeStreams(t *testing.T) {
// test ok
logger.Init("debug")
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/ok.conf"
var result *cosy.Error
errors.As(CheckNginxConfIncludeStreams(), &result)
assert.Nil(t, result)
// test 4041 nginx.conf not found
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/4041.conf"
errors.As(CheckNginxConfIncludeStreams(), &result)
assert.Equal(t, int32(4041), result.Code)
// test 5001 nginx.conf parse error
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/5001.conf"
errors.As(CheckNginxConfIncludeStreams(), &result)
assert.Equal(t, int32(5001), result.Code)
// test 4044 nginx.conf no stream block
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/no-http-block.conf"
errors.As(CheckNginxConfIncludeStreams(), &result)
assert.Equal(t, int32(4044), result.Code)
// test 4045 nginx.conf not include stream-enabled
settings.NginxSettings.ConfigDir = "/etc/nginx"
settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled.conf"
errors.As(CheckNginxConfIncludeStreams(), &result)
assert.Equal(t, int32(4045), result.Code)
}
func TestFixNginxConfIncludeSites(t *testing.T) {
logger.Init("debug")
settings.NginxSettings.ConfigDir = "/etc/nginx"
// copy file
content, err := os.ReadFile("./test_cases/no-http-block.conf")
assert.Nil(t, err)
err = os.WriteFile("./test_cases/no-http-block-fixed.conf", content, 0644)
assert.Nil(t, err)
settings.NginxSettings.ConfigPath = "./test_cases/no-http-block-fixed.conf"
var result *cosy.Error
errors.As(FixNginxConfIncludeSites(), &result)
assert.Nil(t, result)
// copy file
content, err = os.ReadFile("./test_cases/no-http-sites-enabled.conf")
assert.Nil(t, err)
err = os.WriteFile("./test_cases/no-http-sites-enabled-fixed.conf", content, 0644)
assert.Nil(t, err)
settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled-fixed.conf"
errors.As(FixNginxConfIncludeSites(), &result)
assert.Nil(t, result)
settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled-fixed.conf"
errors.As(FixNginxConfIncludeStreams(), &result)
assert.Nil(t, result)
// remove backup files (./test_cases/*.bak.*)
files, err := os.ReadDir("./test_cases")
assert.Nil(t, err)
for _, file := range files {
if strings.Contains(file.Name(), ".bak.") {
err = os.Remove("./test_cases/" + file.Name())
assert.Nil(t, err)
}
}
}

View file

@ -0,0 +1,74 @@
package self_check
import (
"errors"
"github.com/uozi-tech/cosy"
)
type Task struct {
Name string
CheckFunc func() error
FixFunc func() error
}
type Report struct {
Name string `json:"name"`
Err *cosy.Error `json:"err,omitempty"`
}
type Reports []*Report
var selfCheckTasks = []*Task{
{
Name: "Directory-Sites",
CheckFunc: CheckSitesDirectory,
FixFunc: FixSitesDirectory,
},
{
Name: "Directory-Streams",
CheckFunc: CheckStreamDirectory,
FixFunc: FixStreamDirectory,
},
{
Name: "NginxConf-Sites-Enabled",
CheckFunc: CheckNginxConfIncludeSites,
FixFunc: FixNginxConfIncludeSites,
},
{
Name: "NginxConf-Streams-Enabled",
CheckFunc: CheckNginxConfIncludeStreams,
FixFunc: FixNginxConfIncludeStreams,
},
}
var selfCheckTaskMap = make(map[string]*Task)
func init() {
for _, task := range selfCheckTasks {
selfCheckTaskMap[task.Name] = task
}
}
func Run() (reports Reports) {
reports = make(Reports, 0)
for _, task := range selfCheckTasks {
var cErr *cosy.Error
if err := task.CheckFunc(); err != nil {
errors.As(err, &cErr)
}
reports = append(reports, &Report{
Name: task.Name,
Err: cErr,
})
}
return
}
func AttemptFix(taskName string) (err error) {
task, ok := selfCheckTaskMap[taskName]
if !ok {
return ErrTaskNotFound
}
return task.FixFunc()
}

View file

@ -0,0 +1 @@
5001.conf

View file

@ -0,0 +1,14 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.local.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/sites-enabled/*;
}

View file

@ -0,0 +1,10 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.local.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}

View file

@ -0,0 +1,23 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.local.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
include /etc/nginx/streams-enabled/*;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

View file

@ -0,0 +1,34 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.local.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
}

View file

@ -0,0 +1,35 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.local.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
include /etc/nginx/streams-enabled/*;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

View file

@ -0,0 +1 @@
package self_check