From 62bd8927e34abe05ca676a1931b3cf3bcf2a2695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Wed, 3 Dec 2025 22:00:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=BD=91=E7=AB=99?= =?UTF-8?q?=E5=90=AF=E5=81=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/nginx/data.go | 33 -- pkg/nginx/getter.go | 223 ------------- pkg/nginx/parser.go | 213 ------------- pkg/nginx/parser_test.go | 234 -------------- pkg/nginx/setter.go | 492 ----------------------------- pkg/nginx/testdata/http.conf | 26 -- pkg/nginx/testdata/https.conf | 34 -- pkg/webserver/apache/vhost.go | 39 +-- pkg/webserver/apache/vhost_test.go | 2 +- pkg/webserver/nginx/vhost.go | 29 +- pkg/webserver/nginx/vhost_test.go | 2 +- pkg/webserver/types/vhost.go | 4 +- 12 files changed, 37 insertions(+), 1294 deletions(-) delete mode 100644 pkg/nginx/data.go delete mode 100644 pkg/nginx/getter.go delete mode 100644 pkg/nginx/parser.go delete mode 100644 pkg/nginx/parser_test.go delete mode 100644 pkg/nginx/setter.go delete mode 100644 pkg/nginx/testdata/http.conf delete mode 100644 pkg/nginx/testdata/https.conf diff --git a/pkg/nginx/data.go b/pkg/nginx/data.go deleted file mode 100644 index 816430cb..00000000 --- a/pkg/nginx/data.go +++ /dev/null @@ -1,33 +0,0 @@ -package nginx - -var order = []string{"listen", "server_name", "index", "root", - "ssl_certificate", "ssl_certificate_key", "ssl_session_timeout", "ssl_session_cache", "ssl_protocols", "ssl_ciphers", "ssl_prefer_server_ciphers", "ssl_early_data", "ssl_stapling", "ssl_stapling_verify", "ssl_trusted_certificate", - "resolver", "error_page", "include", "if", "location", "add_header", "access_log", "error_log"} - -const DefaultConf = `server { - listen 80; - server_name localhost; - index index.php index.html; - root /www/wwwroot/default; - # Error page - error_page 404 /404.html; - include enable-php-0.conf; - # Browser cache - location ~ .*\.(bmp|jpg|jpeg|png|gif|svg|ico|tiff|webp|avif|heif|heic|jxl)$ { - expires 30d; - access_log /dev/null; - error_log /dev/null; - } - location ~ .*\.(js|css|ttf|otf|woff|woff2|eot)$ { - expires 6h; - access_log /dev/null; - error_log /dev/null; - } - # Deny sensitive files - location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) { - return 404; - } - access_log /www/wwwlogs/default.log; - error_log /www/wwwlogs/default.log; -} -` diff --git a/pkg/nginx/getter.go b/pkg/nginx/getter.go deleted file mode 100644 index 2c0bac28..00000000 --- a/pkg/nginx/getter.go +++ /dev/null @@ -1,223 +0,0 @@ -package nginx - -import ( - "fmt" - "slices" - "strings" -) - -func (p *Parser) GetListen() ([][]string, error) { - directives, err := p.Find("server.listen") - if err != nil { - return nil, err - } - - var result [][]string - for _, dir := range directives { - result = append(result, p.parameters2Slices(dir.GetParameters())) - } - - return result, nil -} - -func (p *Parser) GetServerName() ([]string, error) { - directive, err := p.FindOne("server.server_name") - if err != nil { - return nil, err - } - - return p.parameters2Slices(directive.GetParameters()), nil -} - -func (p *Parser) GetIndex() ([]string, error) { - directive, err := p.FindOne("server.index") - if err != nil { - return nil, err - } - - return p.parameters2Slices(directive.GetParameters()), nil -} - -func (p *Parser) GetIndexWithComment() ([]string, []string, error) { - directive, err := p.FindOne("server.index") - if err != nil { - return nil, nil, err - } - - return p.parameters2Slices(directive.GetParameters()), directive.GetComment(), nil -} - -func (p *Parser) GetRoot() (string, error) { - directive, err := p.FindOne("server.root") - if err != nil { - return "", err - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return "", nil - } - - return directive.GetParameters()[0].GetValue(), nil -} - -func (p *Parser) GetRootWithComment() (string, []string, error) { - directive, err := p.FindOne("server.root") - if err != nil { - return "", nil, err - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return "", directive.GetComment(), nil - } - - return directive.GetParameters()[0].GetValue(), directive.GetComment(), nil -} - -func (p *Parser) GetIncludes() (includes []string, comments [][]string, err error) { - directives, err := p.Find("server.include") - if err != nil { - return nil, nil, err - } - - for _, dir := range directives { - if len(dir.GetParameters()) != 1 { - return nil, nil, fmt.Errorf("invalid include directive, expected 1 parameter but got %d", len(dir.GetParameters())) - } - includes = append(includes, dir.GetParameters()[0].GetValue()) - comments = append(comments, dir.GetComment()) - } - - return includes, comments, nil -} - -func (p *Parser) GetPHP() int { - directives, err := p.Find("server.include") - if err != nil { - return 0 - } - - var result int - for _, dir := range directives { - if slices.ContainsFunc(p.parameters2Slices(dir.GetParameters()), func(s string) bool { - return strings.HasPrefix(s, "enable-php-") && strings.HasSuffix(s, ".conf") - }) { - _, _ = fmt.Sscanf(dir.GetParameters()[0].GetValue(), "enable-php-%d.conf", &result) - } - } - - return result -} - -func (p *Parser) GetHTTPS() bool { - directive, err := p.FindOne("server.ssl_certificate") - if err != nil { - return false - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return false - } - - return true -} - -func (p *Parser) GetHTTPSProtocols() []string { - directive, err := p.FindOne("server.ssl_protocols") - if err != nil { - return nil - } - - return p.parameters2Slices(directive.GetParameters()) -} - -func (p *Parser) GetHTTPSCiphers() string { - directive, err := p.FindOne("server.ssl_ciphers") - if err != nil { - return "" - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return "" - } - - return directive.GetParameters()[0].GetValue() -} - -func (p *Parser) GetOCSP() bool { - directive, err := p.FindOne("server.ssl_stapling") - if err != nil { - return false - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return false - } - - return directive.GetParameters()[0].GetValue() == "on" -} - -func (p *Parser) GetHSTS() bool { - directives, err := p.Find("server.add_header") - if err != nil { - return false - } - - for _, dir := range directives { - if slices.Contains(p.parameters2Slices(dir.GetParameters()), "Strict-Transport-Security") { - return true - } - } - - return false -} - -func (p *Parser) GetHTTPSRedirect() bool { - directives, err := p.Find("server.if") - if err != nil { - return false - } - - for _, dir := range directives { - for _, dir2 := range dir.GetBlock().GetDirectives() { - if dir2.GetName() == "return" && slices.Contains(p.parameters2Slices(dir2.GetParameters()), "https://$host$request_uri") { - return true - } - } - } - - return false -} - -func (p *Parser) GetAltSvc() string { - directive, err := p.FindOne("server.add_header") - if err != nil { - return "" - } - - for i, param := range p.parameters2Slices(directive.GetParameters()) { - if strings.HasPrefix(param, "Alt-Svc") && i+1 < len(p.parameters2Slices(directive.GetParameters())) { - return p.parameters2Slices(directive.GetParameters())[i+1] - } - } - - return "" -} - -func (p *Parser) GetAccessLog() (string, error) { - directive, err := p.FindOne("server.access_log") - if err != nil { - return "", err - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return "", nil - } - - return directive.GetParameters()[0].GetValue(), nil -} - -func (p *Parser) GetErrorLog() (string, error) { - directive, err := p.FindOne("server.error_log") - if err != nil { - return "", err - } - if len(p.parameters2Slices(directive.GetParameters())) == 0 { - return "", nil - } - - return directive.GetParameters()[0].GetValue(), nil -} diff --git a/pkg/nginx/parser.go b/pkg/nginx/parser.go deleted file mode 100644 index 04b94cf7..00000000 --- a/pkg/nginx/parser.go +++ /dev/null @@ -1,213 +0,0 @@ -package nginx - -import ( - "errors" - "fmt" - "slices" - "strings" - - "github.com/tufanbarisyildirim/gonginx/config" - "github.com/tufanbarisyildirim/gonginx/dumper" - "github.com/tufanbarisyildirim/gonginx/parser" -) - -// Parser Nginx vhost 配置解析器 -type Parser struct { - c *config.Config - orderIndex map[string]int -} - -func NewParser(str ...string) (*Parser, error) { - if len(str) == 0 { - str = append(str, DefaultConf) - } - p := parser.NewStringParser(str[0], parser.WithSkipIncludeParsingErr(), parser.WithSkipValidDirectivesErr()) - c, err := p.Parse() - if err != nil { - return nil, err - } - - orderIndex := make(map[string]int) - for i, name := range order { - orderIndex[name] = i - } - - return &Parser{c: c, orderIndex: orderIndex}, nil -} - -func (p *Parser) Config() *config.Config { - return p.c -} - -// Find 通过表达式查找配置 -// e.g. Find("server.listen") -func (p *Parser) Find(key string) ([]config.IDirective, error) { - parts := strings.Split(key, ".") - var block *config.Block - var ok bool - block = p.c.Block - for i := 0; i < len(parts)-1; i++ { - key = parts[i] - directives := block.FindDirectives(key) - if len(directives) == 0 { - return nil, fmt.Errorf("given key %s not found", key) - } - if len(directives) > 1 { - return nil, errors.New("multiple directives found") - } - block, ok = directives[0].GetBlock().(*config.Block) - if !ok { - return nil, errors.New("block is not *config.Block") - } - } - - var result []config.IDirective - for _, dir := range block.GetDirectives() { - if dir.GetName() == parts[len(parts)-1] { - result = append(result, dir) - } - } - - return result, nil -} - -// FindOne 通过表达式查找一个配置 -// e.g. FindOne("server.server_name") -func (p *Parser) FindOne(key string) (config.IDirective, error) { - directives, err := p.Find(key) - if err != nil { - return nil, err - } - if len(directives) == 0 { - return nil, fmt.Errorf("given key %s not found", key) - } - - return directives[0], nil -} - -// Clear 通过表达式移除配置 -// e.g. Clear("server.server_name") -func (p *Parser) Clear(key string) error { - parts := strings.Split(key, ".") - last := parts[len(parts)-1] - parts = parts[:len(parts)-1] - - var block *config.Block - var ok bool - block = p.c.Block - for i := 0; i < len(parts); i++ { - directives := block.FindDirectives(parts[i]) - if len(directives) == 0 { - return fmt.Errorf("given key %s not found", parts[i]) - } - if len(directives) > 1 { - return fmt.Errorf("multiple directives found for %s", parts[i]) - } - block, ok = directives[0].GetBlock().(*config.Block) - if !ok { - return errors.New("block is not *config.Block") - } - } - - var newDirectives []config.IDirective - for _, directive := range block.GetDirectives() { - if directive.GetName() != last { - newDirectives = append(newDirectives, directive) - } - } - block.Directives = newDirectives - - return nil -} - -// Set 通过表达式设置配置 -// e.g. Set("server.server_name", []directive) -func (p *Parser) Set(key string, directives []*config.Directive, after ...string) error { - parts := strings.Split(key, ".") - - var block *config.Block - var blockDirective config.IDirective - var ok bool - block = p.c.Block - for i := 0; i < len(parts); i++ { - sub := block.FindDirectives(parts[i]) - if len(sub) == 0 { - return fmt.Errorf("given key %s not found", parts[i]) - } - if len(sub) > 1 { - return fmt.Errorf("multiple directives found for %s", parts[i]) - } - block, ok = sub[0].GetBlock().(*config.Block) - if !ok { - return errors.New("block is not *config.Block") - } - blockDirective = sub[0] - } - - iDirectives := make([]config.IDirective, 0, len(directives)) - for _, directive := range directives { - directive.SetParent(blockDirective) - iDirectives = append(iDirectives, directive) - } - - if len(after) == 0 { - block.Directives = append(block.Directives, iDirectives...) - } else { - insertIndex := -1 - for i, d := range block.Directives { - if d.GetName() == after[0] { - insertIndex = i + 1 - break - } - } - if insertIndex == -1 { - return fmt.Errorf("after directive %s not found", after[0]) - } - - block.Directives = append( - block.Directives[:insertIndex], - append(iDirectives, block.Directives[insertIndex:]...)..., - ) - } - - return nil -} - -func (p *Parser) Sort() { - p.sortDirectives(p.c.Directives, p.orderIndex) -} - -func (p *Parser) Dump() string { - return dumper.DumpConfig(p.c, dumper.IndentedStyle) -} - -func (p *Parser) sortDirectives(directives []config.IDirective, orderIndex map[string]int) { - slices.SortFunc(directives, func(a config.IDirective, b config.IDirective) int { - if orderIndex[a.GetName()] != orderIndex[b.GetName()] { - return orderIndex[a.GetName()] - orderIndex[b.GetName()] - } - return slices.Compare(p.parameters2Slices(a.GetParameters()), p.parameters2Slices(b.GetParameters())) - }) - - for _, directive := range directives { - if block, ok := directive.GetBlock().(*config.Block); ok { - p.sortDirectives(block.Directives, orderIndex) - } - } -} - -func (p *Parser) slices2Parameters(slices []string) []config.Parameter { - var parameters []config.Parameter - for _, slice := range slices { - parameters = append(parameters, config.Parameter{Value: slice}) - } - return parameters -} - -func (p *Parser) parameters2Slices(parameters []config.Parameter) []string { - var s []string - for _, parameter := range parameters { - s = append(s, parameter.Value) - } - return s -} diff --git a/pkg/nginx/parser_test.go b/pkg/nginx/parser_test.go deleted file mode 100644 index 805d72d2..00000000 --- a/pkg/nginx/parser_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package nginx - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/acepanel/panel/pkg/io" -) - -type NginxTestSuite struct { - suite.Suite -} - -func TestNginxTestSuite(t *testing.T) { - suite.Run(t, &NginxTestSuite{}) -} - -func (s *NginxTestSuite) TestListen() { - parser, err := NewParser() - s.NoError(err) - listen, err := parser.GetListen() - s.NoError(err) - s.Equal([][]string{{"80"}}, listen) - s.NoError(parser.SetListen([][]string{{"80"}, {"443"}})) - listen, err = parser.GetListen() - s.NoError(err) - s.Equal([][]string{{"80"}, {"443"}}, listen) -} - -func (s *NginxTestSuite) TestServerName() { - parser, err := NewParser() - s.NoError(err) - serverName, err := parser.GetServerName() - s.NoError(err) - s.Equal([]string{"localhost"}, serverName) - s.NoError(parser.SetServerName([]string{"example.com"})) - serverName, err = parser.GetServerName() - s.NoError(err) - s.Equal([]string{"example.com"}, serverName) -} - -func (s *NginxTestSuite) TestIndex() { - parser, err := NewParser() - s.NoError(err) - index, err := parser.GetIndex() - s.NoError(err) - s.Equal([]string{"index.php", "index.html"}, index) - s.NoError(parser.SetIndex([]string{"index.html", "index.php"})) - index, err = parser.GetIndex() - s.NoError(err) - s.Equal([]string{"index.html", "index.php"}, index) -} - -func (s *NginxTestSuite) TestIndexWithComment() { - parser, err := NewParser() - s.NoError(err) - index, comment, err := parser.GetIndexWithComment() - s.NoError(err) - s.Equal([]string{"index.php", "index.html"}, index) - s.Equal([]string(nil), comment) - s.NoError(parser.SetIndexWithComment([]string{"index.html", "index.php"}, []string{"# 测试"})) - index, comment, err = parser.GetIndexWithComment() - s.NoError(err) - s.Equal([]string{"index.html", "index.php"}, index) - s.Equal([]string{"# 测试"}, comment) -} - -func (s *NginxTestSuite) TestRoot() { - parser, err := NewParser() - s.NoError(err) - root, err := parser.GetRoot() - s.NoError(err) - s.Equal("/www/wwwroot/default", root) - s.NoError(parser.SetRoot("/www/wwwroot/test")) - root, err = parser.GetRoot() - s.NoError(err) - s.Equal("/www/wwwroot/test", root) -} - -func (s *NginxTestSuite) TestRootWithComment() { - parser, err := NewParser() - s.NoError(err) - root, comment, err := parser.GetRootWithComment() - s.NoError(err) - s.Equal("/www/wwwroot/default", root) - s.Equal([]string(nil), comment) - s.NoError(parser.SetRootWithComment("/www/wwwroot/test", []string{"# 测试"})) - root, comment, err = parser.GetRootWithComment() - s.NoError(err) - s.Equal("/www/wwwroot/test", root) - s.Equal([]string{"# 测试"}, comment) -} - -func (s *NginxTestSuite) TestIncludes() { - parser, err := NewParser() - s.NoError(err) - includes, comments, err := parser.GetIncludes() - s.NoError(err) - s.Equal([]string{"enable-php-0.conf"}, includes) - s.Equal([][]string{[]string(nil)}, comments) - s.NoError(parser.SetIncludes([]string{"/www/server/vhost/rewrite/default.conf"}, nil)) - includes, comments, err = parser.GetIncludes() - s.NoError(err) - s.Equal([]string{"/www/server/vhost/rewrite/default.conf"}, includes) - s.Equal([][]string{[]string(nil)}, comments) - s.NoError(parser.SetIncludes([]string{"/www/server/vhost/rewrite/test.conf"}, [][]string{{"# 伪静态规则测试"}})) - includes, comments, err = parser.GetIncludes() - s.NoError(err) - s.Equal([]string{"/www/server/vhost/rewrite/test.conf"}, includes) - s.Equal([][]string{{"# 伪静态规则测试"}}, comments) -} - -func (s *NginxTestSuite) TestPHP() { - parser, err := NewParser() - s.NoError(err) - s.Equal(0, parser.GetPHP()) - s.NoError(parser.SetPHP(80)) - s.Equal(80, parser.GetPHP()) - s.NoError(parser.SetPHP(0)) - s.Equal(0, parser.GetPHP()) -} - -func (s *NginxTestSuite) TestHTTP() { - parser, err := NewParser() - s.NoError(err) - expect, err := io.Read("testdata/http.conf") - s.NoError(err) - s.Equal(expect, parser.Dump()) -} - -func (s *NginxTestSuite) TestHTTPS() { - parser, err := NewParser() - s.NoError(err) - s.False(parser.GetHTTPS()) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.True(parser.GetHTTPS()) - expect, err := io.Read("testdata/https.conf") - s.NoError(err) - s.Equal(expect, parser.Dump()) -} - -func (s *NginxTestSuite) TestHTTPSProtocols() { - parser, err := NewParser() - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.Equal([]string{"TLSv1.2", "TLSv1.3"}, parser.GetHTTPSProtocols()) - s.NoError(parser.SetHTTPSProtocols([]string{"TLSv1.3"})) - s.Equal([]string{"TLSv1.3"}, parser.GetHTTPSProtocols()) -} - -func (s *NginxTestSuite) TestHTTPSCiphers() { - parser, err := NewParser() - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.Equal("ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305", parser.GetHTTPSCiphers()) - s.NoError(parser.SetHTTPSCiphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384")) - s.Equal("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384", parser.GetHTTPSCiphers()) -} - -func (s *NginxTestSuite) TestOCSP() { - parser, err := NewParser() - s.NoError(err) - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.False(parser.GetOCSP()) - s.NoError(parser.SetOCSP(false)) - s.False(parser.GetOCSP()) - s.NoError(parser.SetOCSP(true)) - s.True(parser.GetOCSP()) - s.NoError(parser.SetOCSP(false)) - s.False(parser.GetOCSP()) -} - -func (s *NginxTestSuite) TestHSTS() { - parser, err := NewParser() - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.False(parser.GetHSTS()) - s.NoError(parser.SetHSTS(false)) - s.False(parser.GetHSTS()) - s.NoError(parser.SetHSTS(true)) - s.True(parser.GetHSTS()) - s.NoError(parser.SetHSTS(false)) - s.False(parser.GetHSTS()) -} - -func (s *NginxTestSuite) TestHTTPSRedirect() { - parser, err := NewParser() - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.False(parser.GetHTTPSRedirect()) - s.NoError(parser.SetHTTPRedirect(false)) - s.False(parser.GetHTTPSRedirect()) - s.NoError(parser.SetHTTPRedirect(true)) - s.True(parser.GetHTTPSRedirect()) - s.NoError(parser.SetHTTPRedirect(false)) - s.False(parser.GetHTTPSRedirect()) -} - -func (s *NginxTestSuite) TestAltSvc() { - parser, err := NewParser() - s.NoError(err) - s.NoError(parser.SetHTTPS("/www/server/vhost/cert/default.pem", "/www/server/vhost/cert/default.key")) - s.Equal("", parser.GetAltSvc()) - s.NoError(parser.SetAltSvc(`'h3=":$server_port"; ma=2592000'`)) - s.Equal(`'h3=":$server_port"; ma=2592000'`, parser.GetAltSvc()) - s.NoError(parser.SetAltSvc("")) - s.Equal("", parser.GetAltSvc()) -} - -func (s *NginxTestSuite) TestAccessLog() { - parser, err := NewParser() - s.NoError(err) - log, err := parser.GetAccessLog() - s.NoError(err) - s.Equal("/www/wwwlogs/default.log", log) - s.NoError(parser.SetAccessLog("/www/wwwlogs/access.log")) - log, err = parser.GetAccessLog() - s.NoError(err) - s.Equal("/www/wwwlogs/access.log", log) -} - -func (s *NginxTestSuite) TestErrorLog() { - parser, err := NewParser() - s.NoError(err) - log, err := parser.GetErrorLog() - s.NoError(err) - s.Equal("/www/wwwlogs/default.log", log) - s.NoError(parser.SetErrorLog("/www/wwwlogs/error.log")) - log, err = parser.GetErrorLog() - s.NoError(err) - s.Equal("/www/wwwlogs/error.log", log) -} diff --git a/pkg/nginx/setter.go b/pkg/nginx/setter.go deleted file mode 100644 index 7a9dafb2..00000000 --- a/pkg/nginx/setter.go +++ /dev/null @@ -1,492 +0,0 @@ -package nginx - -import ( - "fmt" - "slices" - "strings" - - "github.com/tufanbarisyildirim/gonginx/config" -) - -func (p *Parser) SetListen(listen [][]string) error { - var directives []*config.Directive - for _, l := range listen { - directives = append(directives, &config.Directive{ - Name: "listen", - Parameters: p.slices2Parameters(l), - }) - } - - if err := p.Clear("server.listen"); err != nil { - return err - } - - return p.Set("server", directives) -} - -func (p *Parser) SetServerName(serverName []string) error { - if err := p.Clear("server.server_name"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "server_name", - Parameters: p.slices2Parameters(serverName), - }, - }) -} - -func (p *Parser) SetIndex(index []string) error { - if err := p.Clear("server.index"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "index", - Parameters: p.slices2Parameters(index), - }, - }) -} - -func (p *Parser) SetIndexWithComment(index []string, comment []string) error { - if err := p.Clear("server.index"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "index", - Parameters: p.slices2Parameters(index), - Comment: comment, - }, - }) -} - -func (p *Parser) SetRoot(root string) error { - if err := p.Clear("server.root"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "root", - Parameters: []config.Parameter{{Value: root}}, - }, - }) -} - -func (p *Parser) SetRootWithComment(root string, comment []string) error { - if err := p.Clear("server.root"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "root", - Parameters: []config.Parameter{{Value: root}}, - Comment: comment, - }, - }) -} - -func (p *Parser) SetIncludes(includes []string, comments [][]string) error { - if err := p.Clear("server.include"); err != nil { - return err - } - - var directives []*config.Directive - for i, item := range includes { - var comment []string - if i < len(comments) { - comment = comments[i] - } - directives = append(directives, &config.Directive{ - Name: "include", - Parameters: []config.Parameter{{Value: item}}, - Comment: comment, - }) - } - - return p.Set("server", directives) -} - -func (p *Parser) SetPHP(php int) error { - old, err := p.Find("server.include") - if err != nil { - return err - } - if err = p.Clear("server.include"); err != nil { - return err - } - - var directives []*config.Directive - var foundFlag bool - for _, item := range old { - // 查找enable-php的配置 - if slices.ContainsFunc(p.parameters2Slices(item.GetParameters()), func(s string) bool { - return strings.HasPrefix(s, "enable-php-") && strings.HasSuffix(s, ".conf") - }) { - foundFlag = true - directives = append(directives, &config.Directive{ - Name: item.GetName(), - Parameters: []config.Parameter{{Value: fmt.Sprintf("enable-php-%d.conf", php)}}, - Comment: item.GetComment(), - }) - } else { - // 其余的原样保留 - directives = append(directives, &config.Directive{ - Name: item.GetName(), - Parameters: item.GetParameters(), - Comment: item.GetComment(), - }) - } - } - - // 如果没有找到enable-php的配置,直接添加一个 - if !foundFlag { - directives = append(directives, &config.Directive{ - Name: "include", - Parameters: []config.Parameter{{Value: fmt.Sprintf("enable-php-%d.conf", php)}}, - }) - } - - return p.Set("server", directives) -} - -func (p *Parser) ClearSetHTTPS() error { - if err := p.Clear("server.ssl_certificate"); err != nil { - return err - } - if err := p.Clear("server.ssl_certificate_key"); err != nil { - return err - } - if err := p.Clear("server.ssl_session_timeout"); err != nil { - return err - } - if err := p.Clear("server.ssl_session_cache"); err != nil { - return err - } - if err := p.Clear("server.ssl_protocols"); err != nil { - return err - } - if err := p.Clear("server.ssl_ciphers"); err != nil { - return err - } - if err := p.Clear("server.ssl_prefer_server_ciphers"); err != nil { - return err - } - if err := p.Clear("server.ssl_early_data"); err != nil { - return err - } - - return nil -} - -func (p *Parser) SetHTTPS(cert, key string) error { - if err := p.ClearSetHTTPS(); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "ssl_certificate", - Parameters: []config.Parameter{{Value: cert}}, - }, - { - Name: "ssl_certificate_key", - Parameters: []config.Parameter{{Value: key}}, - }, - { - Name: "ssl_session_timeout", - Parameters: []config.Parameter{{Value: "1d"}}, - }, - { - Name: "ssl_session_cache", - Parameters: []config.Parameter{{Value: "shared:SSL:10m"}}, - }, - { - Name: "ssl_protocols", - Parameters: []config.Parameter{{Value: "TLSv1.2"}, {Value: "TLSv1.3"}}, - }, - { - Name: "ssl_ciphers", - Parameters: []config.Parameter{{Value: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"}}, - }, - { - Name: "ssl_prefer_server_ciphers", - Parameters: []config.Parameter{{Value: "off"}}, - }, - { - Name: "ssl_early_data", - Parameters: []config.Parameter{{Value: "on"}}, - }, - }, "root") -} - -func (p *Parser) SetHTTPSProtocols(protocols []string) error { - if err := p.Clear("server.ssl_protocols"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "ssl_protocols", - Parameters: p.slices2Parameters(protocols), - }, - }) -} - -func (p *Parser) SetHTTPSCiphers(ciphers string) error { - if err := p.Clear("server.ssl_ciphers"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "ssl_ciphers", - Parameters: []config.Parameter{{Value: ciphers}}, - }, - }) -} - -func (p *Parser) SetOCSP(ocsp bool) error { - if err := p.Clear("server.ssl_stapling"); err != nil { - return err - } - if err := p.Clear("server.ssl_stapling_verify"); err != nil { - return err - } - - if ocsp { - return p.Set("server", []*config.Directive{ - { - Name: "ssl_stapling", - Parameters: []config.Parameter{{Value: "on"}}, - }, - { - Name: "ssl_stapling_verify", - Parameters: []config.Parameter{{Value: "on"}}, - }, - }) - } - - return nil -} - -func (p *Parser) SetHSTS(hsts bool) error { - old, err := p.Find("server.add_header") - if err != nil { - return err - } - if err = p.Clear("server.add_header"); err != nil { - return err - } - - var directives []*config.Directive - var foundFlag bool - for _, dir := range old { - if slices.Contains(p.parameters2Slices(dir.GetParameters()), "Strict-Transport-Security") { - foundFlag = true - if hsts { - directives = append(directives, &config.Directive{ - Name: dir.GetName(), - Parameters: []config.Parameter{{Value: "Strict-Transport-Security"}, {Value: "max-age=31536000"}}, - Comment: dir.GetComment(), - }) - } - } else { - directives = append(directives, &config.Directive{ - Name: dir.GetName(), - Parameters: dir.GetParameters(), - Comment: dir.GetComment(), - }) - } - } - - if !foundFlag && hsts { - directives = append(directives, &config.Directive{ - Name: "add_header", - Parameters: []config.Parameter{{Value: "Strict-Transport-Security"}, {Value: "max-age=31536000"}}, - }) - } - - return p.Set("server", directives) -} - -func (p *Parser) SetHTTPRedirect(httpRedirect bool) error { - // if 重定向 - ifs, err := p.Find("server.if") - if err != nil { - return err - } - if err = p.Clear("server.if"); err != nil { - return err - } - - var directives []*config.Directive - var foundFlag bool - for _, dir := range ifs { // 所有 if - if !httpRedirect { - if len(dir.GetParameters()) == 3 && dir.GetParameters()[0].GetValue() == "($scheme" && dir.GetParameters()[1].GetValue() == "=" && dir.GetParameters()[2].GetValue() == "http)" { - continue - } - } - var ifDirectives []config.IDirective - for _, dir2 := range dir.GetBlock().GetDirectives() { // 每个 if 中所有指令 - if !httpRedirect { - // 不启用http重定向,则判断并移除特定的return指令 - if dir2.GetName() != "return" && !slices.Contains(p.parameters2Slices(dir2.GetParameters()), "https://$host$request_uri") { - ifDirectives = append(ifDirectives, dir2) - } - } else { - // 启用http重定向,需要检查防止重复添加 - if dir2.GetName() == "return" && slices.Contains(p.parameters2Slices(dir2.GetParameters()), "https://$host$request_uri") { - foundFlag = true - } - ifDirectives = append(ifDirectives, dir2) - } - } - // 写回 if 指令 - if block, ok := dir.GetBlock().(*config.Block); ok { - block.Directives = ifDirectives - } - directives = append(directives, &config.Directive{ - Block: dir.GetBlock(), - Name: dir.GetName(), - Parameters: dir.GetParameters(), - Comment: dir.GetComment(), - }) - } - - if !foundFlag && httpRedirect { - ifDir := &config.Directive{ - Name: "if", - Block: &config.Block{}, - Parameters: []config.Parameter{{Value: "($scheme"}, {Value: "="}, {Value: "http)"}}, - } - redirectDir := &config.Directive{ - Name: "return", - Parameters: []config.Parameter{{Value: "308"}, {Value: "https://$host$request_uri"}}, - } - redirectDir.SetParent(ifDir.GetParent()) - ifBlock := ifDir.GetBlock().(*config.Block) - ifBlock.Directives = append(ifBlock.Directives, redirectDir) - directives = append(directives, ifDir) - } - - if err = p.Set("server", directives); err != nil { - return err - } - - // error_page 497 重定向 - directives = nil - errorPages, err := p.Find("server.error_page") - if err != nil { - return err - } - if err = p.Clear("server.error_page"); err != nil { - return err - } - var found497 bool - for _, dir := range errorPages { - if !httpRedirect { - // 不启用https重定向,则判断并移除特定的return指令 - if !slices.Contains(p.parameters2Slices(dir.GetParameters()), "497") && !slices.Contains(p.parameters2Slices(dir.GetParameters()), "https://$host:$server_port$request_uri") { - directives = append(directives, &config.Directive{ - Block: dir.GetBlock(), - Name: dir.GetName(), - Parameters: dir.GetParameters(), - Comment: dir.GetComment(), - }) - } - } else { - // 启用https重定向,需要检查防止重复添加 - if slices.Contains(p.parameters2Slices(dir.GetParameters()), "497") && slices.Contains(p.parameters2Slices(dir.GetParameters()), "https://$host:$server_port$request_uri") { - found497 = true - } - directives = append(directives, &config.Directive{ - Block: dir.GetBlock(), - Name: dir.GetName(), - Parameters: dir.GetParameters(), - Comment: dir.GetComment(), - }) - } - } - - if !found497 && httpRedirect { - directives = append(directives, &config.Directive{ - Name: "error_page", - Parameters: []config.Parameter{{Value: "497"}, {Value: "=308"}, {Value: "https://$host:$server_port$request_uri"}}, - }) - } - - return p.Set("server", directives) -} - -func (p *Parser) SetAltSvc(altSvc string) error { - old, err := p.Find("server.add_header") - if err != nil { - return err - } - if err = p.Clear("server.add_header"); err != nil { - return err - } - - var directives []*config.Directive - var foundFlag bool - for _, dir := range old { - if slices.Contains(p.parameters2Slices(dir.GetParameters()), "Alt-Svc") { - foundFlag = true - if altSvc != "" { // 为空表示要删除 - directives = append(directives, &config.Directive{ - Name: dir.GetName(), - Parameters: []config.Parameter{{Value: "Alt-Svc"}, {Value: altSvc}}, - Comment: dir.GetComment(), - }) - } - } else { - directives = append(directives, &config.Directive{ - Name: dir.GetName(), - Parameters: dir.GetParameters(), - Comment: dir.GetComment(), - }) - } - } - - if !foundFlag && altSvc != "" { - directives = append(directives, &config.Directive{ - Name: "add_header", - Parameters: []config.Parameter{{Value: "Alt-Svc"}, {Value: altSvc}}, - }) - } - - return p.Set("server", directives) -} - -func (p *Parser) SetAccessLog(accessLog string) error { - if err := p.Clear("server.access_log"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "access_log", - Parameters: []config.Parameter{{Value: accessLog}}, - }, - }) -} - -func (p *Parser) SetErrorLog(errorLog string) error { - if err := p.Clear("server.error_log"); err != nil { - return err - } - - return p.Set("server", []*config.Directive{ - { - Name: "error_log", - Parameters: []config.Parameter{{Value: errorLog}}, - }, - }) -} diff --git a/pkg/nginx/testdata/http.conf b/pkg/nginx/testdata/http.conf deleted file mode 100644 index 02126989..00000000 --- a/pkg/nginx/testdata/http.conf +++ /dev/null @@ -1,26 +0,0 @@ -server { - listen 80; - server_name localhost; - index index.php index.html; - root /www/wwwroot/default; - # Error page - error_page 404 /404.html; - include enable-php-0.conf; - # Browser cache - location ~ .*\.(bmp|jpg|jpeg|png|gif|svg|ico|tiff|webp|avif|heif|heic|jxl)$ { - expires 30d; - access_log /dev/null; - error_log /dev/null; - } - location ~ .*\.(js|css|ttf|otf|woff|woff2|eot)$ { - expires 6h; - access_log /dev/null; - error_log /dev/null; - } - # Deny sensitive files - location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) { - return 404; - } - access_log /www/wwwlogs/default.log; - error_log /www/wwwlogs/default.log; -} \ No newline at end of file diff --git a/pkg/nginx/testdata/https.conf b/pkg/nginx/testdata/https.conf deleted file mode 100644 index 7ee96096..00000000 --- a/pkg/nginx/testdata/https.conf +++ /dev/null @@ -1,34 +0,0 @@ -server { - listen 80; - server_name localhost; - index index.php index.html; - root /www/wwwroot/default; - ssl_certificate /www/server/vhost/cert/default.pem; - ssl_certificate_key /www/server/vhost/cert/default.key; - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:10m; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; - ssl_prefer_server_ciphers off; - ssl_early_data on; - # Error page - error_page 404 /404.html; - include enable-php-0.conf; - # Browser cache - location ~ .*\.(bmp|jpg|jpeg|png|gif|svg|ico|tiff|webp|avif|heif|heic|jxl)$ { - expires 30d; - access_log /dev/null; - error_log /dev/null; - } - location ~ .*\.(js|css|ttf|otf|woff|woff2|eot)$ { - expires 6h; - access_log /dev/null; - error_log /dev/null; - } - # Deny sensitive files - location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) { - return 404; - } - access_log /www/wwwlogs/default.log; - error_log /www/wwwlogs/default.log; -} \ No newline at end of file diff --git a/pkg/webserver/apache/vhost.go b/pkg/webserver/apache/vhost.go index cf73bf38..9766d6c7 100644 --- a/pkg/webserver/apache/vhost.go +++ b/pkg/webserver/apache/vhost.go @@ -108,40 +108,35 @@ func (v *baseVhost) Enable() bool { return root != DisablePagePath } -func (v *baseVhost) SetEnable(enable bool, siteConfig ...string) error { - name := "" +func (v *baseVhost) SetEnable(enable bool) error { path := DisablePagePath if enable { - if len(siteConfig) != 2 { - return fmt.Errorf("site config is required to enable the vhost") + // 尝试获取保存的根目录 + if root, err := os.ReadFile(filepath.Join(v.configDir, "root.saved")); err != nil { + path = filepath.Join(SitesPath, filepath.Dir(v.configDir), "public") // 默认根目录 + } else { + path = strings.TrimSpace(string(root)) + } + } else { + // 禁用时,保存当前根目录 + currentRoot := v.Root() + if currentRoot != "" && currentRoot != DisablePagePath { + if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0644); err != nil { + return fmt.Errorf("failed to save current root: %w", err) + } } - name = siteConfig[0] - path = siteConfig[1] } // 设置根目录 - v.vhost.SetDirective("DocumentRoot", path) - - // 更新 Directory 块 - dirBlock := v.vhost.GetBlock("Directory") - if dirBlock != nil { - dirBlock.Args = []string{path} - } else { - block := v.vhost.AddBlock("Directory", path) - if block.Block != nil { - block.Block.Directives = append(block.Block.Directives, - &Directive{Name: "Options", Args: []string{"-Indexes", "+FollowSymLinks"}}, - &Directive{Name: "AllowOverride", Args: []string{"All"}}, - &Directive{Name: "Require", Args: []string{"all", "granted"}}, - ) - } + if err := v.SetRoot(path); err != nil { + return err } // 设置 Include 配置 v.vhost.RemoveDirectives("IncludeOptional") if enable { - v.vhost.AddDirective("IncludeOptional", fmt.Sprintf("%s/%s/config/site/*.conf", SitesPath, name)) + v.vhost.AddDirective("IncludeOptional", fmt.Sprintf("%s/site/*.conf", v.configDir)) } return nil diff --git a/pkg/webserver/apache/vhost_test.go b/pkg/webserver/apache/vhost_test.go index 625cb294..73a4cc8b 100644 --- a/pkg/webserver/apache/vhost_test.go +++ b/pkg/webserver/apache/vhost_test.go @@ -59,7 +59,7 @@ func (s *VhostTestSuite) TestEnable() { s.False(s.vhost.Enable()) // 重新启用 - s.NoError(s.vhost.SetEnable(true, "testsite", "/var/www/test")) + s.NoError(s.vhost.SetEnable(true)) s.True(s.vhost.Enable()) } diff --git a/pkg/webserver/nginx/vhost.go b/pkg/webserver/nginx/vhost.go index de6da82b..edf1af31 100644 --- a/pkg/webserver/nginx/vhost.go +++ b/pkg/webserver/nginx/vhost.go @@ -108,26 +108,29 @@ func (v *baseVhost) Enable() bool { return directive.GetParameters()[0].GetValue() != DisablePagePath } -func (v *baseVhost) SetEnable(enable bool, siteConfig ...string) error { - name := "" +func (v *baseVhost) SetEnable(enable bool) error { path := DisablePagePath if enable { - if len(siteConfig) != 2 { - return fmt.Errorf("site config is required to enable the vhost") + // 尝试获取保存的根目录 + if root, err := os.ReadFile(filepath.Join(v.configDir, "root.saved")); err != nil { + path = filepath.Join(SitesPath, filepath.Dir(v.configDir), "public") // 默认根目录 + } else { + path = strings.TrimSpace(string(root)) + } + } else { + // 禁用时,保存当前根目录 + currentRoot := v.Root() + if currentRoot != "" && currentRoot != DisablePagePath { + if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0644); err != nil { + return fmt.Errorf("failed to save current root: %w", err) + } } - name = siteConfig[0] - path = siteConfig[1] } // 设置根目录 _ = v.parser.Clear("server.root") - if err := v.parser.Set("server", []*config.Directive{ - { - Name: "root", - Parameters: v.parser.slices2Parameters([]string{path}), - }, - }); err != nil { + if err := v.SetRoot(path); err != nil { return err } @@ -137,7 +140,7 @@ func (v *baseVhost) SetEnable(enable bool, siteConfig ...string) error { return v.parser.Set("server", []*config.Directive{ { Name: "include", - Parameters: v.parser.slices2Parameters([]string{fmt.Sprintf("%s/%s/config/site/*.conf", SitesPath, name)}), + Parameters: v.parser.slices2Parameters([]string{fmt.Sprintf("%s/site/*.conf", v.configDir)}), }, }) } diff --git a/pkg/webserver/nginx/vhost_test.go b/pkg/webserver/nginx/vhost_test.go index 2f2e8928..c19a88d5 100644 --- a/pkg/webserver/nginx/vhost_test.go +++ b/pkg/webserver/nginx/vhost_test.go @@ -58,7 +58,7 @@ func (s *VhostTestSuite) TestEnable() { s.False(s.vhost.Enable()) // 重新启用 - s.NoError(s.vhost.SetEnable(true, "testsite", "/var/www/test")) + s.NoError(s.vhost.SetEnable(true)) s.True(s.vhost.Enable()) } diff --git a/pkg/webserver/types/vhost.go b/pkg/webserver/types/vhost.go index 0090572b..1aa08a47 100644 --- a/pkg/webserver/types/vhost.go +++ b/pkg/webserver/types/vhost.go @@ -6,8 +6,8 @@ type Vhost interface { // Enable 取启用状态 Enable() bool - // SetEnable 设置启用状态及网站名称和根目录 - SetEnable(enable bool, siteConfig ...string) error + // SetEnable 设置启用状态 + SetEnable(enable bool) error // Listen 取监听配置 Listen() []Listen