mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
feat: 支持apache
This commit is contained in:
@@ -107,7 +107,6 @@ func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
|
||||
}
|
||||
defer func(out client.ImagePullResponse) { _ = out.Close() }(out)
|
||||
|
||||
// TODO 实现流式显示拉取进度
|
||||
if err = out.Wait(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ func (r *containerImageRepo) Pull(req *request.ContainerImagePull) error {
|
||||
}
|
||||
defer func(out client.ImagePullResponse) { _ = out.Close() }(out)
|
||||
|
||||
// TODO 实现流式显示拉取进度
|
||||
return out.Wait(context.Background())
|
||||
}
|
||||
|
||||
|
||||
@@ -81,21 +81,35 @@ func (r *websiteRepo) GetRewrites() (map[string]string, error) {
|
||||
}
|
||||
|
||||
func (r *websiteRepo) UpdateDefaultConfig(req *request.WebsiteDefaultConfig) error {
|
||||
if err := io.Write(filepath.Join(app.Root, "server/nginx/html/index.html"), req.Index, 0644); err != nil {
|
||||
webServer, err := r.setting.Get(biz.SettingKeyWebserver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := io.Write(filepath.Join(app.Root, "server/nginx/html/stop.html"), req.Stop, 0644); err != nil {
|
||||
var htmlPath string
|
||||
switch webServer {
|
||||
case "nginx":
|
||||
htmlPath = filepath.Join(app.Root, "server/nginx/html")
|
||||
case "apache":
|
||||
htmlPath = filepath.Join(app.Root, "server/apache/htdocs")
|
||||
default:
|
||||
htmlPath = filepath.Join(app.Root, "server/nginx/html")
|
||||
}
|
||||
|
||||
if err = io.Write(filepath.Join(htmlPath, "index.html"), req.Index, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(filepath.Join(htmlPath, "stop.html"), req.Stop, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.NotFound != "" {
|
||||
if err := io.Write(filepath.Join(app.Root, "server/nginx/html/404.html"), req.NotFound, 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(htmlPath, "404.html"), req.NotFound, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.setting.SetSlice(biz.SettingKeyWebsiteTLSVersions, req.TLSVersions); err != nil {
|
||||
if err = r.setting.SetSlice(biz.SettingKeyWebsiteTLSVersions, req.TLSVersions); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.setting.Set(biz.SettingKeyWebsiteCipherSuites, req.CipherSuites); err != nil {
|
||||
if err = r.setting.Set(biz.SettingKeyWebsiteCipherSuites, req.CipherSuites); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -245,6 +259,11 @@ func (r *websiteRepo) Create(ctx context.Context, req *request.WebsiteCreate) (*
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
webServer, err := r.setting.Get(biz.SettingKeyWebserver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vhost, err := r.getVhost(w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -290,8 +309,14 @@ func (r *websiteRepo) Create(ctx context.Context, req *request.WebsiteCreate) (*
|
||||
return nil, err
|
||||
}
|
||||
// 404 页面
|
||||
// TODO 需要兼容 Apache
|
||||
if err = vhost.SetConfig("010-error-404.conf", "site", `error_page 404 /404.html;`); err != nil {
|
||||
var errorPageConfig string
|
||||
switch webServer {
|
||||
case "nginx":
|
||||
errorPageConfig = `error_page 404 /404.html;`
|
||||
case "apache":
|
||||
errorPageConfig = `ErrorDocument 404 /404.html`
|
||||
}
|
||||
if err = vhost.SetConfig("010-error-404.conf", "site", errorPageConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -315,8 +340,10 @@ func (r *websiteRepo) Create(ctx context.Context, req *request.WebsiteCreate) (*
|
||||
if err = phpVhost.SetConfig("010-rewrite.conf", "site", ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO 需要兼容 Apache
|
||||
if err = phpVhost.SetConfig("010-cache.conf", "site", `# browser cache
|
||||
var cacheConfig string
|
||||
switch webServer {
|
||||
case "nginx":
|
||||
cacheConfig = `# browser cache
|
||||
location ~ .*\.(bmp|jpg|jpeg|png|gif|svg|ico|tiff|webp|avif|heif|heic|jxl)$ {
|
||||
expires 30d;
|
||||
access_log /dev/null;
|
||||
@@ -331,7 +358,38 @@ location ~ .*\.(js|css|ttf|otf|woff|woff2|eot)$ {
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return 404;
|
||||
}
|
||||
`); err != nil {
|
||||
`
|
||||
case "apache":
|
||||
cacheConfig = `# browser cache
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/bmp "access plus 30 days"
|
||||
ExpiresByType image/jpeg "access plus 30 days"
|
||||
ExpiresByType image/png "access plus 30 days"
|
||||
ExpiresByType image/gif "access plus 30 days"
|
||||
ExpiresByType image/svg+xml "access plus 30 days"
|
||||
ExpiresByType image/x-icon "access plus 30 days"
|
||||
ExpiresByType image/tiff "access plus 30 days"
|
||||
ExpiresByType image/webp "access plus 30 days"
|
||||
ExpiresByType image/avif "access plus 30 days"
|
||||
ExpiresByType image/heif "access plus 30 days"
|
||||
ExpiresByType image/heic "access plus 30 days"
|
||||
ExpiresByType image/jxl "access plus 30 days"
|
||||
ExpiresByType text/css "access plus 6 hours"
|
||||
ExpiresByType application/javascript "access plus 6 hours"
|
||||
ExpiresByType font/ttf "access plus 6 hours"
|
||||
ExpiresByType font/otf "access plus 6 hours"
|
||||
ExpiresByType font/woff "access plus 6 hours"
|
||||
ExpiresByType font/woff2 "access plus 6 hours"
|
||||
ExpiresByType application/vnd.ms-fontobject "access plus 6 hours"
|
||||
</IfModule>
|
||||
# deny sensitive files
|
||||
<FilesMatch "^(\.user\.ini|\.htaccess|\.git|\.svn|\.env)">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
`
|
||||
}
|
||||
if err = phpVhost.SetConfig("010-cache.conf", "site", cacheConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -358,9 +416,15 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
var notFound []byte
|
||||
|
||||
// 如果存在自定义 404 页面,则使用自定义的
|
||||
// TODO 需要兼容 Apache
|
||||
if io.Exists(filepath.Join(app.Root, "server/nginx/html/404.html")) {
|
||||
notFound, _ = os.ReadFile(filepath.Join(app.Root, "server/nginx/html/404.html"))
|
||||
var custom404Path string
|
||||
switch webServer {
|
||||
case "nginx":
|
||||
custom404Path = filepath.Join(app.Root, "server/nginx/html/404.html")
|
||||
case "apache":
|
||||
custom404Path = filepath.Join(app.Root, "server/apache/htdocs/404.html")
|
||||
}
|
||||
if io.Exists(custom404Path) {
|
||||
notFound, _ = os.ReadFile(custom404Path)
|
||||
} else {
|
||||
switch app.Locale {
|
||||
case "zh_CN":
|
||||
|
||||
@@ -140,7 +140,6 @@ func (s *CliService) Fix(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
|
||||
func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
|
||||
// TODO 未来加权限设置之后这里需要优化
|
||||
user := new(biz.User)
|
||||
if err := s.db.First(user).Error; err != nil {
|
||||
return errors.New(s.t.Get("Failed to get user info: %v", err))
|
||||
|
||||
@@ -35,9 +35,20 @@ func (s *WebsiteService) GetRewrites(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *WebsiteService) GetDefaultConfig(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/index.html"))
|
||||
stop, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/stop.html"))
|
||||
notFound, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/404.html"))
|
||||
webServer, _ := s.settingRepo.Get(biz.SettingKeyWebserver)
|
||||
var htmlPath string
|
||||
switch webServer {
|
||||
case "nginx":
|
||||
htmlPath = filepath.Join(app.Root, "server/nginx/html")
|
||||
case "apache":
|
||||
htmlPath = filepath.Join(app.Root, "server/apache/htdocs")
|
||||
default:
|
||||
htmlPath = filepath.Join(app.Root, "server/nginx/html")
|
||||
}
|
||||
|
||||
index, _ := io.Read(filepath.Join(htmlPath, "index.html"))
|
||||
stop, _ := io.Read(filepath.Join(htmlPath, "stop.html"))
|
||||
notFound, _ := io.Read(filepath.Join(htmlPath, "404.html"))
|
||||
tlsVersions, _ := s.settingRepo.GetSlice(biz.SettingKeyWebsiteTLSVersions)
|
||||
cipherSuites, _ := s.settingRepo.Get(biz.SettingKeyWebsiteCipherSuites)
|
||||
|
||||
|
||||
@@ -165,8 +165,8 @@ var order = map[string]int{
|
||||
"Proxy": 1311,
|
||||
"ProxyMatch": 1312,
|
||||
|
||||
"Include": 1400,
|
||||
"IncludeOptional": 1401,
|
||||
"Include": 1290,
|
||||
"IncludeOptional": 1291,
|
||||
|
||||
"ErrorLog": 1500,
|
||||
"CustomLog": 1501,
|
||||
|
||||
@@ -205,11 +205,11 @@ func (p *Parser) parseVirtualHost(token Token) (*VirtualHost, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否是Include类指令
|
||||
// 检查是否是 Include 类指令
|
||||
if p.options.ProcessIncludes && (strings.EqualFold(directive.Name, "Include") || strings.EqualFold(directive.Name, "IncludeOptional")) {
|
||||
includeConfig, err := p.processInclude(directive)
|
||||
if err != nil {
|
||||
// 对于IncludeOptional,如果文件不存在则忽略错误
|
||||
// 对于 IncludeOptional,如果文件不存在则忽略错误
|
||||
if strings.EqualFold(directive.Name, "IncludeOptional") && os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
@@ -228,6 +228,21 @@ func (p *Parser) parseVirtualHost(token Token) (*VirtualHost, error) {
|
||||
} else {
|
||||
vhost.Directives = append(vhost.Directives, directive)
|
||||
}
|
||||
} else if nextToken.Type == BLOCKDIRECTIVE {
|
||||
// 处理虚拟主机内的块指令(如 Directory, Location 等)
|
||||
block, err := p.parseBlockDirective(nextToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 将块指令作为带 Block 的 Directive 添加到虚拟主机
|
||||
directive := &Directive{
|
||||
Name: block.Type,
|
||||
Args: block.Args,
|
||||
Line: block.Line,
|
||||
Column: block.Column,
|
||||
Block: block,
|
||||
}
|
||||
vhost.Directives = append(vhost.Directives, directive)
|
||||
} else if nextToken.Type == COMMENT {
|
||||
// 收集虚拟主机内的注释
|
||||
comment := &Comment{
|
||||
|
||||
@@ -28,7 +28,7 @@ func parseProxyFiles(siteDir string) ([]types.Proxy, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var proxies []types.Proxy
|
||||
proxies := make([]types.Proxy, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
@@ -66,6 +66,7 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
|
||||
|
||||
contentStr := string(content)
|
||||
proxy := &types.Proxy{
|
||||
Resolver: []string{},
|
||||
Replaces: make(map[string]string),
|
||||
}
|
||||
|
||||
@@ -188,6 +189,18 @@ func generateProxyConfig(proxy types.Proxy) string {
|
||||
if location == "" {
|
||||
location = "/"
|
||||
}
|
||||
// 将 Nginx 风格的 location 转换为 Apache 格式
|
||||
// ^~ / -> /
|
||||
// ~ ^/api -> /api (正则匹配需要使用 ProxyPassMatch)
|
||||
location = strings.TrimPrefix(location, "^~ ")
|
||||
location = strings.TrimPrefix(location, "~ ")
|
||||
location = strings.TrimPrefix(location, "= ")
|
||||
// 如果 location 以 ^ 开头(正则),去掉它
|
||||
location = strings.TrimPrefix(location, "^")
|
||||
// 确保 location 以 / 开头
|
||||
if !strings.HasPrefix(location, "/") {
|
||||
location = "/" + location
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("# Reverse proxy: %s -> %s\n", location, proxy.Pass))
|
||||
|
||||
@@ -261,7 +274,7 @@ func parseBalancerFiles(sharedDir string) ([]types.Upstream, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var upstreams []types.Upstream
|
||||
upstreams := make([]types.Upstream, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
@@ -294,8 +307,9 @@ func parseBalancerFile(filePath string, name string) (*types.Upstream, error) {
|
||||
|
||||
contentStr := string(content)
|
||||
upstream := &types.Upstream{
|
||||
Name: name,
|
||||
Servers: make(map[string]string),
|
||||
Name: name,
|
||||
Servers: make(map[string]string),
|
||||
Resolver: []string{},
|
||||
}
|
||||
|
||||
// 解析 <Proxy balancer://name> 块
|
||||
|
||||
@@ -641,14 +641,14 @@ func (v *PHPVhost) PHP() uint {
|
||||
}
|
||||
|
||||
// 从配置内容中提取版本号
|
||||
// 格式: proxy:unix:/tmp/php-cgi-84.sock|fcgi://localhost
|
||||
idx := strings.Index(content, "php-cgi-")
|
||||
// 格式: Include /opt/ace/server/apache/conf/extra/enable-php-85.conf
|
||||
idx := strings.Index(content, "enable-php-")
|
||||
if idx == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var result uint
|
||||
_, err := fmt.Sscanf(content[idx:], "php-cgi-%d.sock", &result)
|
||||
_, err := fmt.Sscanf(content[idx:], "enable-php-%d.conf", &result)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
@@ -660,12 +660,9 @@ func (v *PHPVhost) SetPHP(version uint) error {
|
||||
return v.RemoveConfig("010-php.conf", "site")
|
||||
}
|
||||
|
||||
// 生成 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)
|
||||
// 生成 PHP-FPM 配置,直接 Include Apache 的 PHP-FPM 配置文件
|
||||
// 配置文件路径: /opt/ace/server/apache/conf/extra/enable-php-85.conf
|
||||
content := fmt.Sprintf("Include conf/extra/enable-php-%d.conf\n", version)
|
||||
|
||||
return v.SetConfig("010-php.conf", "site", content)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user