mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
739 lines
18 KiB
Go
739 lines
18 KiB
Go
package apache
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/acepanel/panel/pkg/webserver/types"
|
||
"github.com/samber/lo"
|
||
)
|
||
|
||
// StaticVhost 纯静态虚拟主机
|
||
type StaticVhost struct {
|
||
*baseVhost
|
||
}
|
||
|
||
// PHPVhost PHP 虚拟主机
|
||
type PHPVhost struct {
|
||
*baseVhost
|
||
}
|
||
|
||
// ProxyVhost 反向代理虚拟主机
|
||
type ProxyVhost struct {
|
||
*baseVhost
|
||
}
|
||
|
||
// baseVhost Apache 虚拟主机基础实现
|
||
type baseVhost struct {
|
||
config *Config
|
||
vhost *VirtualHost
|
||
configDir string // 配置目录
|
||
siteName string // 网站名
|
||
}
|
||
|
||
// newBaseVhost 创建基础虚拟主机实例
|
||
func newBaseVhost(configDir string) (*baseVhost, error) {
|
||
if configDir == "" {
|
||
return nil, fmt.Errorf("config directory is required")
|
||
}
|
||
|
||
v := &baseVhost{
|
||
configDir: configDir,
|
||
siteName: filepath.Base(filepath.Dir(configDir)),
|
||
}
|
||
|
||
// 加载配置
|
||
var config *Config
|
||
var err error
|
||
|
||
// 从配置目录加载主配置文件
|
||
configFile := filepath.Join(v.configDir, "apache.conf")
|
||
if _, statErr := os.Stat(configFile); statErr == nil {
|
||
config, err = ParseFile(configFile)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to parse apache config: %w", err)
|
||
}
|
||
}
|
||
|
||
// 如果没有配置文件,使用默认配置
|
||
if config == nil {
|
||
defaultConf := strings.ReplaceAll(DefaultVhostConf, "/opt/ace/sites/default", fmt.Sprintf("/opt/ace/sites/%s", v.siteName))
|
||
config, err = ParseString(defaultConf)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to parse default config: %w", err)
|
||
}
|
||
}
|
||
|
||
v.config = config
|
||
|
||
// 获取第一个虚拟主机
|
||
if len(config.VirtualHosts) > 0 {
|
||
v.vhost = config.VirtualHosts[0]
|
||
} else {
|
||
// 创建默认虚拟主机
|
||
v.vhost = config.AddVirtualHost("*:80")
|
||
}
|
||
|
||
return v, nil
|
||
}
|
||
|
||
// NewStaticVhost 创建纯静态虚拟主机实例
|
||
func NewStaticVhost(configDir string) (*StaticVhost, error) {
|
||
base, err := newBaseVhost(configDir)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &StaticVhost{baseVhost: base}, nil
|
||
}
|
||
|
||
// NewPHPVhost 创建 PHP 虚拟主机实例
|
||
func NewPHPVhost(configDir string) (*PHPVhost, error) {
|
||
base, err := newBaseVhost(configDir)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &PHPVhost{baseVhost: base}, nil
|
||
}
|
||
|
||
// NewProxyVhost 创建反向代理虚拟主机实例
|
||
func NewProxyVhost(configDir string) (*ProxyVhost, error) {
|
||
base, err := newBaseVhost(configDir)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &ProxyVhost{baseVhost: base}, nil
|
||
}
|
||
|
||
func (v *baseVhost) Enable() bool {
|
||
// 检查是否存在禁用配置文件
|
||
disableConf := filepath.Join(v.configDir, "site", "00-disable.conf")
|
||
_, err := os.Stat(disableConf)
|
||
return os.IsNotExist(err)
|
||
}
|
||
|
||
func (v *baseVhost) SetEnable(enable bool) error {
|
||
disableConf := filepath.Join(v.configDir, "site", "00-disable.conf")
|
||
|
||
if enable {
|
||
// 启用:删除禁用配置文件
|
||
if err := os.Remove(disableConf); err != nil && !os.IsNotExist(err) {
|
||
return fmt.Errorf("failed to remove disable config: %w", err)
|
||
}
|
||
} else {
|
||
// 禁用:所有请求重写到禁用页面
|
||
content := fmt.Sprintf(`# Auto-generated by AcePanel. DO NOT EDIT MANUALLY!
|
||
RewriteEngine On
|
||
RewriteRule ^ %s [L]
|
||
`, DisablePage)
|
||
if err := os.WriteFile(disableConf, []byte(content), 0600); err != nil {
|
||
return fmt.Errorf("failed to write disable config: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Listen() []types.Listen {
|
||
var result []types.Listen
|
||
|
||
// Apache 的监听配置通常在 VirtualHost 的参数中
|
||
// 例如: <VirtualHost *:80> 或 <VirtualHost 192.168.1.1:443>
|
||
for _, arg := range v.vhost.Args {
|
||
listen := types.Listen{Address: arg, Args: []string{}}
|
||
result = append(result, listen)
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
func (v *baseVhost) SetListen(listens []types.Listen) error {
|
||
var args []string
|
||
for _, l := range listens {
|
||
addr := l.Address
|
||
// 如果只是端口号,添加 *: 前缀
|
||
if !strings.Contains(addr, ":") {
|
||
addr = "*:" + addr
|
||
}
|
||
args = append(args, addr)
|
||
}
|
||
v.vhost.Args = args
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) ServerName() []string {
|
||
names := make([]string, 0)
|
||
|
||
// 获取 ServerName
|
||
serverName := v.vhost.GetDirectiveValue("ServerName")
|
||
if serverName != "" {
|
||
names = append(names, serverName)
|
||
}
|
||
|
||
// 获取 ServerAlias(可能有多个值)
|
||
aliases := v.vhost.GetDirectives("ServerAlias")
|
||
for _, alias := range aliases {
|
||
names = append(names, alias.Args...)
|
||
}
|
||
|
||
return names
|
||
}
|
||
|
||
func (v *baseVhost) SetServerName(serverName []string) error {
|
||
if len(serverName) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 设置主域名
|
||
v.vhost.SetDirective("ServerName", serverName[0])
|
||
|
||
// 删除现有的 ServerAlias
|
||
v.vhost.RemoveDirectives("ServerAlias")
|
||
|
||
// 设置别名
|
||
if len(serverName) > 1 {
|
||
v.vhost.AddDirective("ServerAlias", serverName[1:]...)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Index() []string {
|
||
values := v.vhost.GetDirectiveValues("DirectoryIndex")
|
||
if values != nil {
|
||
return values
|
||
}
|
||
return []string{}
|
||
}
|
||
|
||
func (v *baseVhost) SetIndex(index []string) error {
|
||
if len(index) == 0 {
|
||
v.vhost.RemoveDirective("DirectoryIndex")
|
||
return nil
|
||
}
|
||
v.vhost.SetDirective("DirectoryIndex", index...)
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Root() string {
|
||
return v.vhost.GetDirectiveValue("DocumentRoot")
|
||
}
|
||
|
||
func (v *baseVhost) SetRoot(root string) error {
|
||
v.vhost.SetDirective("DocumentRoot", root)
|
||
|
||
// 同时更新 Directory 块
|
||
dirBlock := v.vhost.GetBlock("Directory")
|
||
if dirBlock != nil {
|
||
// 更新现有的 Directory 块路径
|
||
dirBlock.Args = []string{root}
|
||
} else {
|
||
// 添加新的 Directory 块
|
||
block := v.vhost.AddBlock("Directory", root)
|
||
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"}},
|
||
)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Includes() []types.IncludeFile {
|
||
var result []types.IncludeFile
|
||
|
||
// 获取所有 Include 和 IncludeOptional 指令
|
||
for _, dir := range v.vhost.GetDirectives("Include") {
|
||
if len(dir.Args) > 0 {
|
||
result = append(result, types.IncludeFile{
|
||
Path: dir.Args[0],
|
||
})
|
||
}
|
||
}
|
||
for _, dir := range v.vhost.GetDirectives("IncludeOptional") {
|
||
if len(dir.Args) > 0 {
|
||
result = append(result, types.IncludeFile{
|
||
Path: dir.Args[0],
|
||
})
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
func (v *baseVhost) SetIncludes(includes []types.IncludeFile) error {
|
||
// 删除现有的 Include 指令
|
||
v.vhost.RemoveDirectives("Include")
|
||
v.vhost.RemoveDirectives("IncludeOptional")
|
||
|
||
// 添加新的 Include 指令
|
||
for _, inc := range includes {
|
||
v.vhost.AddDirective("Include", inc.Path)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) AccessLog() string {
|
||
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 {
|
||
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 {
|
||
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 {
|
||
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 {
|
||
configFile := filepath.Join(v.configDir, "apache.conf")
|
||
content := v.config.Export()
|
||
if err := os.WriteFile(configFile, []byte(content), 0600); err != nil {
|
||
return fmt.Errorf("failed to save config file: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Reset() error {
|
||
// 重置配置为默认值
|
||
defaultConf := strings.ReplaceAll(DefaultVhostConf, "/opt/ace/sites/default", fmt.Sprintf("/opt/ace/sites/%s", v.siteName))
|
||
config, err := ParseString(defaultConf)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to reset config: %w", err)
|
||
}
|
||
|
||
v.config = config
|
||
if len(config.VirtualHosts) > 0 {
|
||
v.vhost = config.VirtualHosts[0]
|
||
}
|
||
|
||
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), 0600); 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") &&
|
||
strings.EqualFold(v.vhost.GetDirectiveValue("SSLEngine"), "on")
|
||
}
|
||
|
||
func (v *baseVhost) SSLConfig() *types.SSLConfig {
|
||
if !v.SSL() {
|
||
return nil
|
||
}
|
||
|
||
config := &types.SSLConfig{
|
||
Cert: v.vhost.GetDirectiveValue("SSLCertificateFile"),
|
||
Key: v.vhost.GetDirectiveValue("SSLCertificateKeyFile"),
|
||
}
|
||
|
||
// 获取协议
|
||
protocols := v.vhost.GetDirectiveValues("SSLProtocol")
|
||
if protocols != nil {
|
||
config.Protocols = protocols
|
||
}
|
||
|
||
// 获取加密套件
|
||
config.Ciphers = v.vhost.GetDirectiveValue("SSLCipherSuite")
|
||
|
||
// 检查 HSTS
|
||
headers := v.vhost.GetDirectives("Header")
|
||
for _, h := range headers {
|
||
if len(h.Args) >= 3 && strings.Contains(strings.Join(h.Args, " "), "Strict-Transport-Security") {
|
||
config.HSTS = true
|
||
break
|
||
}
|
||
}
|
||
|
||
// 检查 OCSP
|
||
config.OCSP = strings.EqualFold(v.vhost.GetDirectiveValue("SSLUseStapling"), "on")
|
||
|
||
// 检查 HTTP 重定向(通常在 HTTP 虚拟主机中配置)
|
||
redirects := v.vhost.GetDirectives("RewriteRule")
|
||
for _, r := range redirects {
|
||
if len(r.Args) >= 2 && strings.Contains(strings.Join(r.Args, " "), "https://") {
|
||
config.HTTPRedirect = true
|
||
break
|
||
}
|
||
}
|
||
|
||
return config
|
||
}
|
||
|
||
func (v *baseVhost) SetSSLConfig(cfg *types.SSLConfig) error {
|
||
if cfg == nil {
|
||
return fmt.Errorf("SSL config cannot be nil")
|
||
}
|
||
|
||
// 启用 SSL
|
||
v.vhost.SetDirective("SSLEngine", "on")
|
||
|
||
// 设置证书
|
||
if cfg.Cert != "" {
|
||
v.vhost.SetDirective("SSLCertificateFile", cfg.Cert)
|
||
}
|
||
if cfg.Key != "" {
|
||
v.vhost.SetDirective("SSLCertificateKeyFile", cfg.Key)
|
||
}
|
||
|
||
// 设置协议
|
||
if len(cfg.Protocols) > 0 {
|
||
v.vhost.SetDirective("SSLProtocol", cfg.Protocols...)
|
||
} else {
|
||
v.vhost.SetDirective("SSLProtocol", "all", "-SSLv2", "-SSLv3", "-TLSv1", "-TLSv1.1")
|
||
}
|
||
|
||
// 设置加密套件
|
||
if cfg.Ciphers != "" {
|
||
v.vhost.SetDirective("SSLCipherSuite", cfg.Ciphers)
|
||
} else {
|
||
v.vhost.SetDirective("SSLCipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384")
|
||
}
|
||
|
||
// 设置 HSTS
|
||
if cfg.HSTS {
|
||
// 只移除现有的 HSTS Header,保留其他 Header
|
||
newDirectives := make([]*Directive, 0, len(v.vhost.Directives))
|
||
for _, dir := range v.vhost.Directives {
|
||
if strings.EqualFold(dir.Name, "Header") {
|
||
if len(dir.Args) >= 3 && strings.Contains(strings.Join(dir.Args, " "), "Strict-Transport-Security") {
|
||
continue
|
||
}
|
||
}
|
||
newDirectives = append(newDirectives, dir)
|
||
}
|
||
v.vhost.Directives = newDirectives
|
||
v.vhost.AddDirective("Header", "always", "set", "Strict-Transport-Security", `"max-age=31536000"`)
|
||
}
|
||
|
||
// 设置 OCSP
|
||
if cfg.OCSP {
|
||
v.vhost.SetDirective("SSLUseStapling", "on")
|
||
}
|
||
|
||
// 设置 HTTP 重定向(需要 mod_rewrite)
|
||
if cfg.HTTPRedirect {
|
||
v.vhost.SetDirective("RewriteEngine", "on")
|
||
v.vhost.AddDirective("RewriteCond", "%{HTTPS}", "off")
|
||
v.vhost.AddDirective("RewriteRule", "^(.*)$", "https://%{HTTP_HOST}%{REQUEST_URI}", "[R=301,L]")
|
||
}
|
||
|
||
// 更新监听端口为 443
|
||
hasSSLPort := false
|
||
for _, arg := range v.vhost.Args {
|
||
if strings.Contains(arg, ":443") {
|
||
hasSSLPort = true
|
||
break
|
||
}
|
||
}
|
||
if !hasSSLPort {
|
||
v.vhost.Args = append(v.vhost.Args, "*:443")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) ClearSSL() error {
|
||
// 移除 SSL 相关指令
|
||
v.vhost.RemoveDirective("SSLEngine")
|
||
v.vhost.RemoveDirective("SSLCertificateFile")
|
||
v.vhost.RemoveDirective("SSLCertificateKeyFile")
|
||
v.vhost.RemoveDirective("SSLProtocol")
|
||
v.vhost.RemoveDirective("SSLCipherSuite")
|
||
v.vhost.RemoveDirective("SSLUseStapling")
|
||
|
||
// 只移除 HSTS 相关的 Header 指令
|
||
newDirectives := make([]*Directive, 0, len(v.vhost.Directives))
|
||
for _, dir := range v.vhost.Directives {
|
||
if strings.EqualFold(dir.Name, "Header") {
|
||
// 检查是否是 HSTS Header
|
||
if len(dir.Args) >= 3 && strings.Contains(strings.Join(dir.Args, " "), "Strict-Transport-Security") {
|
||
continue // 跳过 HSTS Header
|
||
}
|
||
}
|
||
newDirectives = append(newDirectives, dir)
|
||
}
|
||
v.vhost.Directives = newDirectives
|
||
|
||
// 移除重定向规则
|
||
v.vhost.RemoveDirective("RewriteEngine")
|
||
v.vhost.RemoveDirectives("RewriteCond")
|
||
v.vhost.RemoveDirectives("RewriteRule")
|
||
|
||
// 更新监听端口,移除 443
|
||
var newArgs []string
|
||
for _, arg := range v.vhost.Args {
|
||
if !strings.Contains(arg, ":443") {
|
||
newArgs = append(newArgs, arg)
|
||
}
|
||
}
|
||
if len(newArgs) == 0 {
|
||
newArgs = []string{"*:80"}
|
||
}
|
||
v.vhost.Args = newArgs
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) RateLimit() *types.RateLimit {
|
||
// Apache 使用 mod_ratelimit
|
||
rate := v.vhost.GetDirectiveValue("SetOutputFilter")
|
||
if rate != "RATE_LIMIT" {
|
||
return nil
|
||
}
|
||
|
||
rateLimit := &types.RateLimit{}
|
||
|
||
// 获取速率限制值 (SetEnv rate-limit 512)
|
||
args := v.vhost.GetDirectiveValues("SetEnv")
|
||
if len(args) >= 2 && args[0] == "rate-limit" {
|
||
_, _ = fmt.Sscanf(args[1], "%d", &rateLimit.Rate)
|
||
}
|
||
|
||
return rateLimit
|
||
}
|
||
|
||
func (v *baseVhost) SetRateLimit(limit *types.RateLimit) error {
|
||
// Apache mod_ratelimit 只支持流量限制,不支持并发连接限制
|
||
if limit.Rate > 0 {
|
||
v.vhost.SetDirective("SetOutputFilter", "RATE_LIMIT")
|
||
v.vhost.SetDirective("SetEnv", "rate-limit", fmt.Sprintf("%d", limit.Rate))
|
||
} else {
|
||
v.vhost.RemoveDirective("SetOutputFilter")
|
||
v.vhost.RemoveDirectives("SetEnv")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) ClearRateLimit() error {
|
||
v.vhost.RemoveDirective("SetOutputFilter")
|
||
v.vhost.RemoveDirectives("SetEnv")
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) BasicAuth() map[string]string {
|
||
authType := v.vhost.GetDirectiveValue("AuthType")
|
||
if authType == "" || !strings.EqualFold(authType, "Basic") {
|
||
return nil
|
||
}
|
||
|
||
return map[string]string{
|
||
"realm": v.vhost.GetDirectiveValue("AuthName"),
|
||
"user_file": v.vhost.GetDirectiveValue("AuthUserFile"),
|
||
}
|
||
}
|
||
|
||
func (v *baseVhost) SetBasicAuth(auth map[string]string) error {
|
||
v.vhost.SetDirective("AuthType", "Basic")
|
||
v.vhost.SetDirective("AuthName", fmt.Sprintf(`"%s"`, lo.If(auth["realm"] != "", auth["realm"]).Else("Restricted")))
|
||
v.vhost.SetDirective("AuthUserFile", auth["user_file"])
|
||
v.vhost.SetDirective("Require", "valid-user")
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) ClearBasicAuth() error {
|
||
v.vhost.RemoveDirective("AuthType")
|
||
v.vhost.RemoveDirective("AuthName")
|
||
v.vhost.RemoveDirective("AuthUserFile")
|
||
v.vhost.RemoveDirective("Require")
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) RealIP() *types.RealIP {
|
||
// Apache 使用 mod_remoteip
|
||
// RemoteIPHeader X-Forwarded-For
|
||
// RemoteIPTrustedProxy 127.0.0.1
|
||
header := v.vhost.GetDirectiveValue("RemoteIPHeader")
|
||
if header == "" {
|
||
return nil
|
||
}
|
||
|
||
var from []string
|
||
for _, dir := range v.vhost.GetDirectives("RemoteIPTrustedProxy") {
|
||
if len(dir.Args) > 0 {
|
||
from = append(from, dir.Args[0])
|
||
}
|
||
}
|
||
|
||
return &types.RealIP{
|
||
From: from,
|
||
Header: header,
|
||
}
|
||
}
|
||
|
||
func (v *baseVhost) SetRealIP(realIP *types.RealIP) error {
|
||
// 清除现有配置
|
||
v.vhost.RemoveDirective("RemoteIPHeader")
|
||
v.vhost.RemoveDirectives("RemoteIPTrustedProxy")
|
||
|
||
if realIP == nil || (len(realIP.From) == 0 && realIP.Header == "") {
|
||
return nil
|
||
}
|
||
|
||
// 设置 RemoteIPHeader
|
||
if realIP.Header != "" {
|
||
v.vhost.SetDirective("RemoteIPHeader", realIP.Header)
|
||
}
|
||
|
||
// 设置 RemoteIPTrustedProxy
|
||
for _, ip := range realIP.From {
|
||
if ip != "" {
|
||
v.vhost.AddDirective("RemoteIPTrustedProxy", ip)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) ClearRealIP() error {
|
||
v.vhost.RemoveDirective("RemoteIPHeader")
|
||
v.vhost.RemoveDirectives("RemoteIPTrustedProxy")
|
||
return nil
|
||
}
|
||
|
||
func (v *baseVhost) Redirects() []types.Redirect {
|
||
siteDir := filepath.Join(v.configDir, "site")
|
||
redirects, _ := parseRedirectFiles(siteDir)
|
||
return redirects
|
||
}
|
||
|
||
func (v *baseVhost) SetRedirects(redirects []types.Redirect) error {
|
||
siteDir := filepath.Join(v.configDir, "site")
|
||
return writeRedirectFiles(siteDir, redirects)
|
||
}
|
||
|
||
// ========== PHPVhost ==========
|
||
|
||
func (v *PHPVhost) PHP() uint {
|
||
content := v.Config("010-php.conf", "site")
|
||
if content == "" {
|
||
return 0
|
||
}
|
||
|
||
// 从配置内容中提取版本号
|
||
// 格式: proxy:unix:/tmp/php-cgi-84.sock|fcgi://localhost/
|
||
idx := strings.Index(content, "php-cgi-")
|
||
if idx == -1 {
|
||
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 {
|
||
if version == 0 {
|
||
return v.RemoveConfig("010-php.conf", "site")
|
||
}
|
||
|
||
// 生成 PHP-FPM 配置
|
||
// sock 路径格式: unix:/tmp/php-cgi-84.sock
|
||
content := fmt.Sprintf(`# Auto-generated by AcePanel. DO NOT EDIT MANUALLY!
|
||
<FilesMatch \.php$>
|
||
SetHandler "proxy:unix:/tmp/php-cgi-%d.sock|fcgi://localhost/"
|
||
</FilesMatch>
|
||
`, version)
|
||
|
||
return v.SetConfig("010-php.conf", "site", content)
|
||
}
|
||
|
||
// ========== ProxyVhost ==========
|
||
|
||
func (v *ProxyVhost) Proxies() []types.Proxy {
|
||
siteDir := filepath.Join(v.configDir, "site")
|
||
proxies, _ := parseProxyFiles(siteDir)
|
||
return proxies
|
||
}
|
||
|
||
func (v *ProxyVhost) SetProxies(proxies []types.Proxy) error {
|
||
siteDir := filepath.Join(v.configDir, "site")
|
||
return writeProxyFiles(siteDir, proxies)
|
||
}
|
||
|
||
func (v *ProxyVhost) ClearProxies() error {
|
||
siteDir := filepath.Join(v.configDir, "site")
|
||
return clearProxyFiles(siteDir)
|
||
}
|
||
|
||
func (v *ProxyVhost) Upstreams() []types.Upstream {
|
||
sharedDir := filepath.Join(v.configDir, "shared")
|
||
upstreams, _ := parseBalancerFiles(sharedDir)
|
||
return upstreams
|
||
}
|
||
|
||
func (v *ProxyVhost) SetUpstreams(upstreams []types.Upstream) error {
|
||
sharedDir := filepath.Join(v.configDir, "shared")
|
||
return writeBalancerFiles(sharedDir, upstreams)
|
||
}
|
||
|
||
func (v *ProxyVhost) ClearUpstreams() error {
|
||
sharedDir := filepath.Join(v.configDir, "shared")
|
||
return clearBalancerFiles(sharedDir)
|
||
}
|