mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 13:47:15 +08:00
feat: 调整更多指令到独立配置
This commit is contained in:
@@ -19,13 +19,8 @@ const DefaultVhostConf = `<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
DocumentRoot /opt/ace/sites/default/public
|
||||
DirectoryIndex index.php index.html
|
||||
|
||||
ErrorLog /opt/ace/sites/default/log/error.log
|
||||
CustomLog /opt/ace/sites/default/log/access.log combined
|
||||
|
||||
# custom configs
|
||||
IncludeOptional /opt/ace/sites/default/config/site/*.conf
|
||||
|
||||
<Directory /opt/ace/sites/default/public>
|
||||
Options -Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/acepanel/panel/pkg/webserver/types"
|
||||
@@ -281,21 +280,45 @@ func (v *baseVhost) SetIncludes(includes []types.IncludeFile) error {
|
||||
}
|
||||
|
||||
func (v *baseVhost) AccessLog() string {
|
||||
return v.vhost.GetDirectiveValue("CustomLog")
|
||||
content := v.Config("020-access-log.conf", "site")
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result string
|
||||
_, err := fmt.Sscanf(content, "CustomLog %s combined", &result)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetAccessLog(accessLog string) error {
|
||||
v.vhost.SetDirective("CustomLog", accessLog, "combined")
|
||||
return nil
|
||||
if accessLog == "" {
|
||||
return v.RemoveConfig("020-access-log.conf", "site")
|
||||
}
|
||||
return v.SetConfig("020-access-log.conf", "site", fmt.Sprintf("CustomLog %s combined\n", accessLog))
|
||||
}
|
||||
|
||||
func (v *baseVhost) ErrorLog() string {
|
||||
return v.vhost.GetDirectiveValue("ErrorLog")
|
||||
content := v.Config("020-error-log.conf", "site")
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result string
|
||||
_, err := fmt.Sscanf(content, "ErrorLog %s", &result)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetErrorLog(errorLog string) error {
|
||||
v.vhost.SetDirective("ErrorLog", errorLog)
|
||||
return nil
|
||||
if errorLog == "" {
|
||||
return v.RemoveConfig("020-error-log.conf", "site")
|
||||
}
|
||||
return v.SetConfig("020-error-log.conf", "site", fmt.Sprintf("ErrorLog %s\n", errorLog))
|
||||
}
|
||||
|
||||
func (v *baseVhost) Save() error {
|
||||
@@ -323,6 +346,31 @@ func (v *baseVhost) Reset() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) Config(name string, typ string) string {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
content, err := os.ReadFile(conf)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(content))
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetConfig(name string, typ string, content string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.WriteFile(conf, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) RemoveConfig(name string, typ string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.Remove(conf); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) SSL() bool {
|
||||
// 检查是否有 SSL 相关配置
|
||||
return v.vhost.HasDirective("SSLEngine") &&
|
||||
@@ -573,87 +621,39 @@ func (v *baseVhost) SetRedirects(redirects []types.Redirect) error {
|
||||
// ========== PHPVhost ==========
|
||||
|
||||
func (v *PHPVhost) PHP() uint {
|
||||
// Apache 通常通过 FilesMatch 块配置 PHP
|
||||
// 或者通过 SetHandler 指令
|
||||
handler := v.vhost.GetDirectiveValue("SetHandler")
|
||||
if handler != "" && strings.Contains(handler, "php") {
|
||||
// 尝试从 handler 中提取版本号
|
||||
// 例如: proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost
|
||||
if idx := strings.Index(handler, "php"); idx != -1 {
|
||||
versionStr := ""
|
||||
for i := idx + 3; i < len(handler); i++ {
|
||||
c := handler[i]
|
||||
if (c >= '0' && c <= '9') || c == '.' {
|
||||
versionStr += string(c)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if versionStr != "" {
|
||||
// 转换版本号,如 "8.4" -> 84
|
||||
parts := strings.Split(versionStr, ".")
|
||||
if len(parts) >= 2 {
|
||||
major, _ := strconv.Atoi(parts[0])
|
||||
minor, _ := strconv.Atoi(parts[1])
|
||||
return uint(major*10 + minor)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果有 PHP 处理器但无法确定版本,返回默认值
|
||||
return 1
|
||||
content := v.Config("010-php.conf", "site")
|
||||
if content == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 检查 FilesMatch 块中的 PHP 配置
|
||||
for _, dir := range v.vhost.Directives {
|
||||
if dir.Block != nil && strings.EqualFold(dir.Block.Type, "FilesMatch") {
|
||||
for _, d := range dir.Block.Directives {
|
||||
if strings.EqualFold(d.Name, "SetHandler") && len(d.Args) > 0 {
|
||||
if strings.Contains(d.Args[0], "php") {
|
||||
return 1 // 有 PHP,但版本未知
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从配置内容中提取版本号
|
||||
// 格式: proxy:unix:/tmp/php-cgi-84.sock|fcgi://localhost
|
||||
idx := strings.Index(content, "php-cgi-")
|
||||
if idx == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
var result uint
|
||||
_, err := fmt.Sscanf(content[idx:], "php-cgi-%d.sock", &result)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *PHPVhost) SetPHP(version uint) error {
|
||||
// 移除现有的 PHP 配置
|
||||
v.vhost.RemoveDirective("SetHandler")
|
||||
|
||||
// 移除 FilesMatch 块中的 PHP 配置
|
||||
for i := len(v.vhost.Directives) - 1; i >= 0; i-- {
|
||||
dir := v.vhost.Directives[i]
|
||||
if dir.Block != nil && strings.EqualFold(dir.Block.Type, "FilesMatch") {
|
||||
if len(dir.Block.Args) > 0 && strings.Contains(dir.Block.Args[0], "php") {
|
||||
v.vhost.Directives = append(v.vhost.Directives[:i], v.vhost.Directives[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if version == 0 {
|
||||
return nil // 禁用 PHP
|
||||
return v.RemoveConfig("010-php.conf", "site")
|
||||
}
|
||||
|
||||
// 添加 PHP-FPM 配置
|
||||
major := version / 10
|
||||
minor := version % 10
|
||||
socketPath := fmt.Sprintf("/run/php/php%d.%d-fpm.sock", major, minor)
|
||||
// 生成 PHP-FPM 配置
|
||||
// sock 路径格式: unix:/tmp/php-cgi-84.sock
|
||||
content := fmt.Sprintf(`<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/tmp/php-cgi-%d.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
`, version)
|
||||
|
||||
// 添加 FilesMatch 块
|
||||
block := v.vhost.AddBlock("FilesMatch", `\.php$`)
|
||||
if block.Block != nil {
|
||||
block.Block.Directives = append(block.Block.Directives,
|
||||
&Directive{
|
||||
Name: "SetHandler",
|
||||
Args: []string{fmt.Sprintf("proxy:unix:%s|fcgi://localhost", socketPath)},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
return v.SetConfig("010-php.conf", "site", content)
|
||||
}
|
||||
|
||||
// ========== ProxyVhost ==========
|
||||
|
||||
@@ -324,10 +324,11 @@ func (s *VhostTestSuite) TestDirectoryBlock() {
|
||||
func (s *VhostTestSuite) TestPHPFilesMatchBlock() {
|
||||
s.NoError(s.vhost.SetPHP(84))
|
||||
|
||||
content := s.vhost.config.Export()
|
||||
// PHP 配置现在在独立文件中
|
||||
content := s.vhost.Config("010-php.conf", "site")
|
||||
s.Contains(content, "<FilesMatch")
|
||||
s.Contains(content, "SetHandler")
|
||||
s.True(strings.Contains(content, "php8.4") || strings.Contains(content, "fcgi"))
|
||||
s.Contains(content, "php-cgi-84.sock")
|
||||
}
|
||||
|
||||
func (s *VhostTestSuite) TestDefaultVhostConfIncludesServerD() {
|
||||
|
||||
@@ -21,27 +21,8 @@ server {
|
||||
server_name localhost;
|
||||
root /opt/ace/sites/default/public;
|
||||
index index.php index.html;
|
||||
# error page
|
||||
error_page 404 /404.html;
|
||||
# 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;
|
||||
}
|
||||
# custom configs
|
||||
include /opt/ace/sites/default/config/site/*.conf;
|
||||
access_log /opt/ace/sites/default/log/access.log;
|
||||
error_log /opt/ace/sites/default/log/error.log;
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
28
pkg/webserver/nginx/testdata/http.conf
vendored
28
pkg/webserver/nginx/testdata/http.conf
vendored
@@ -1,28 +0,0 @@
|
||||
include /opt/ace/sites/default/config/shared/*.conf;
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
index index.php index.html;
|
||||
root /opt/ace/sites/default/public;
|
||||
# error page
|
||||
error_page 404 /404.html;
|
||||
# custom configs
|
||||
include /opt/ace/sites/default/config/site/*.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 /opt/ace/sites/default/log/access.log;
|
||||
error_log /opt/ace/sites/default/log/error.log;
|
||||
}
|
||||
36
pkg/webserver/nginx/testdata/https.conf
vendored
36
pkg/webserver/nginx/testdata/https.conf
vendored
@@ -1,36 +0,0 @@
|
||||
include /opt/ace/sites/default/config/shared/*.conf;
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
index index.php index.html;
|
||||
root /opt/ace/sites/default/public;
|
||||
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;
|
||||
# custom configs
|
||||
include /opt/ace/sites/default/config/site/*.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 /opt/ace/sites/default/log/access.log;
|
||||
error_log /opt/ace/sites/default/log/error.log;
|
||||
}
|
||||
@@ -284,49 +284,45 @@ func (v *baseVhost) SetIncludes(includes []types.IncludeFile) error {
|
||||
}
|
||||
|
||||
func (v *baseVhost) AccessLog() string {
|
||||
directive, err := v.parser.FindOne("server.access_log")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(v.parser.parameters2Slices(directive.GetParameters())) == 0 {
|
||||
content := v.Config("020-access-log.conf", "site")
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return directive.GetParameters()[0].GetValue()
|
||||
var result string
|
||||
_, err := fmt.Sscanf(content, "access_log %s", &result)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSuffix(result, ";")
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetAccessLog(accessLog string) error {
|
||||
_ = v.parser.Clear("server.access_log")
|
||||
|
||||
return v.parser.Set("server", []*config.Directive{
|
||||
{
|
||||
Name: "access_log",
|
||||
Parameters: []config.Parameter{{Value: accessLog}},
|
||||
},
|
||||
})
|
||||
if accessLog == "" {
|
||||
return v.RemoveConfig("020-access-log.conf", "site")
|
||||
}
|
||||
return v.SetConfig("020-access-log.conf", "site", fmt.Sprintf("access_log %s;\n", accessLog))
|
||||
}
|
||||
|
||||
func (v *baseVhost) ErrorLog() string {
|
||||
directive, err := v.parser.FindOne("server.error_log")
|
||||
content := v.Config("020-error-log.conf", "site")
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result string
|
||||
_, err := fmt.Sscanf(content, "error_log %s", &result)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(v.parser.parameters2Slices(directive.GetParameters())) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return directive.GetParameters()[0].GetValue()
|
||||
return strings.TrimSuffix(result, ";")
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetErrorLog(errorLog string) error {
|
||||
_ = v.parser.Clear("server.error_log")
|
||||
|
||||
return v.parser.Set("server", []*config.Directive{
|
||||
{
|
||||
Name: "error_log",
|
||||
Parameters: []config.Parameter{{Value: errorLog}},
|
||||
},
|
||||
})
|
||||
if errorLog == "" {
|
||||
return v.RemoveConfig("020-error-log.conf", "site")
|
||||
}
|
||||
return v.SetConfig("020-error-log.conf", "site", fmt.Sprintf("error_log %s;\n", errorLog))
|
||||
}
|
||||
|
||||
func (v *baseVhost) Save() error {
|
||||
@@ -349,6 +345,31 @@ func (v *baseVhost) Reset() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) Config(name string, typ string) string {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
content, err := os.ReadFile(conf)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(content))
|
||||
}
|
||||
|
||||
func (v *baseVhost) SetConfig(name string, typ string, content string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.WriteFile(conf, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) RemoveConfig(name string, typ string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.Remove(conf); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *baseVhost) SSL() bool {
|
||||
directive, err := v.parser.FindOne("server.ssl_certificate")
|
||||
if err != nil {
|
||||
@@ -662,33 +683,24 @@ func (v *baseVhost) SetRedirects(redirects []types.Redirect) error {
|
||||
// ========== PHPVhost ==========
|
||||
|
||||
func (v *PHPVhost) PHP() uint {
|
||||
phpConf := filepath.Join(v.configDir, "site", "010-php.conf")
|
||||
content, err := os.ReadFile(phpConf)
|
||||
if err != nil {
|
||||
content := v.Config("010-php.conf", "site")
|
||||
if content == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
var result uint
|
||||
_, err = fmt.Sscanf(strings.TrimSpace(string(content)), "include enable-php-%d.conf;", &result)
|
||||
_, err := fmt.Sscanf(content, "include enable-php-%d.conf;", &result)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *PHPVhost) SetPHP(version uint) error {
|
||||
if version == 0 {
|
||||
return os.Remove(filepath.Join(v.configDir, "site", "010-php.conf"))
|
||||
return v.RemoveConfig("010-php.conf", "site")
|
||||
}
|
||||
|
||||
phpConf := filepath.Join(v.configDir, "site", "010-php.conf")
|
||||
content := fmt.Sprintf("include enable-php-%d.conf;\n", version)
|
||||
if err := os.WriteFile(phpConf, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write php config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return v.SetConfig("010-php.conf", "site", fmt.Sprintf("include enable-php-%d.conf;\n", version))
|
||||
}
|
||||
|
||||
// ========== ProxyVhost ==========
|
||||
|
||||
@@ -75,6 +75,16 @@ type Vhost interface {
|
||||
SetBasicAuth(auth map[string]string) error
|
||||
// ClearBasicAuth 清除基本认证
|
||||
ClearBasicAuth() error
|
||||
|
||||
// Config 取指定名称的配置内容
|
||||
// type 可选值: "site", "shared"
|
||||
Config(name string, typ string) string
|
||||
// SetConfig 设置指定名称的配置内容
|
||||
// type 可选值: "site", "shared"
|
||||
SetConfig(name string, typ string, content string) error
|
||||
// RemoveConfig 清除指定名称的配置内容
|
||||
// type 可选值: "site", "shared"
|
||||
RemoveConfig(name string, typ string) error
|
||||
}
|
||||
|
||||
// StaticVhost 纯静态虚拟主机接口
|
||||
|
||||
Reference in New Issue
Block a user