2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 11:27:17 +08:00

feat: 支持apache

This commit is contained in:
2026-01-13 02:55:28 +08:00
parent a236ca9db8
commit b341eddd04
10 changed files with 373 additions and 87 deletions

View File

@@ -86,10 +86,10 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
proxy.Host = matches[1]
}
// 解析 SSLProxyEngine 和 ProxySSL* (SNI)
// 解析 SSLProxyEngine 和 SNI
if regexp.MustCompile(`SSLProxyEngine\s+On`).MatchString(contentStr) {
// 尝试获取 SNI
sniPattern := regexp.MustCompile(`ProxyPassMatch.*ssl:([^/\s]+)`)
// 尝试从 # SNI: xxx 注释中获取 SNI
sniPattern := regexp.MustCompile(`#\s*SNI:\s*(\S+)`)
if sm := sniPattern.FindStringSubmatch(contentStr); sm != nil {
proxy.SNI = sm[1]
}
@@ -106,9 +106,11 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
}
// 解析 Substitute (响应内容替换)
subPattern := regexp.MustCompile(`Substitute\s+"s/([^/]+)/([^/]*)/[gin]*"`)
subMatches := subPattern.FindAllStringSubmatch(contentStr, -1)
for _, sm := range subMatches {
// 格式: Substitute "s|from|to|n" 或 Substitute "s/from/to/n"
// 使用 | 作为分隔符以支持包含 / 的内容
subPatternPipe := regexp.MustCompile(`Substitute\s+"s\|([^|]*)\|([^|]*)\|[gin]*"`)
subMatchesPipe := subPatternPipe.FindAllStringSubmatch(contentStr, -1)
for _, sm := range subMatchesPipe {
proxy.Replaces[sm[1]] = sm[2]
}
@@ -178,9 +180,6 @@ func generateProxyConfig(proxy types.Proxy) string {
var sb strings.Builder
location := proxy.Location
if location == "" {
location = "/"
}
// 将 Nginx 风格的 location 转换为 Apache 格式
// ^~ / -> /
// ~ ^/api -> /api (正则匹配需要使用 ProxyPassMatch)
@@ -194,6 +193,12 @@ func generateProxyConfig(proxy types.Proxy) string {
location = "/" + location
}
// Pass 不以 / 结尾时,添加 /
// 垃圾 Apache 需要这样才不报错
if !strings.HasSuffix(proxy.Pass, "/") {
proxy.Pass += "/"
}
sb.WriteString(fmt.Sprintf("# Reverse proxy: %s -> %s\n", location, proxy.Pass))
// 启用代理模块
@@ -210,17 +215,16 @@ func generateProxyConfig(proxy types.Proxy) string {
sb.WriteString(" ProxyPreserveHost On\n")
}
// 标准代理头
sb.WriteString(" RequestHeader set X-Real-IP \"%{REMOTE_ADDR}e\"\n")
sb.WriteString(" RequestHeader set X-Forwarded-For \"%{X-Forwarded-For}e\"\n")
sb.WriteString(" RequestHeader set X-Forwarded-Proto \"%{REQUEST_SCHEME}e\"\n")
// SSL/SNI 配置
if proxy.SNI != "" || strings.HasPrefix(proxy.Pass, "https://") {
sb.WriteString(" SSLProxyEngine On\n")
sb.WriteString(" SSLProxyVerify none\n")
sb.WriteString(" SSLProxyCheckPeerCN off\n")
sb.WriteString(" SSLProxyCheckPeerName off\n")
if proxy.SNI != "" {
// 垃圾 Apache 不支持自定义 SNI写这里只是备注一下
sb.WriteString(fmt.Sprintf(" # SNI: %s\n", proxy.SNI))
}
}
// Buffering 配置
@@ -241,7 +245,8 @@ func generateProxyConfig(proxy types.Proxy) string {
sb.WriteString(" <IfModule mod_substitute.c>\n")
sb.WriteString(" AddOutputFilterByType SUBSTITUTE text/html text/plain text/xml\n")
for from, to := range proxy.Replaces {
sb.WriteString(fmt.Sprintf(" Substitute \"s/%s/%s/n\"\n", from, to))
// 使用 | 作为分隔符以支持包含 / 的内容
sb.WriteString(fmt.Sprintf(" Substitute \"s|%s|%s|n\"\n", from, to))
}
sb.WriteString(" </IfModule>\n")
}
@@ -307,29 +312,25 @@ func parseBalancerFile(filePath string, name string) (*types.Upstream, error) {
// </Proxy>
// 解析 BalancerMember
memberPattern := regexp.MustCompile(`BalancerMember\s+(\S+)(?:\s+(.+))?`)
memberMatches := memberPattern.FindAllStringSubmatch(contentStr, -1)
for _, mm := range memberMatches {
addr := mm[1]
options := ""
if len(mm) > 2 {
options = strings.TrimSpace(mm[2])
memberPattern := regexp.MustCompile(`^\s*BalancerMember\s+(\S+)(?:\s+(.*))?$`)
lines := strings.Split(contentStr, "\n")
for _, line := range lines {
if mm := memberPattern.FindStringSubmatch(line); mm != nil {
addr := mm[1]
options := ""
if len(mm) > 2 {
options = strings.TrimSpace(mm[2])
}
upstream.Servers[addr] = options
}
upstream.Servers[addr] = options
}
// 解析负载均衡方法
lbMethodPattern := regexp.MustCompile(`lbmethod=(\S+)`)
if lm := lbMethodPattern.FindStringSubmatch(contentStr); lm != nil {
switch lm[1] {
case "byrequests":
upstream.Algo = ""
case "bytraffic":
upstream.Algo = "bytraffic"
case "bybusyness":
upstream.Algo = "least_conn"
case "heartbeat":
upstream.Algo = "heartbeat"
// byrequests 是默认值,不需要存储
if lm[1] != "byrequests" {
upstream.Algo = lm[1]
}
}
@@ -409,13 +410,8 @@ func generateBalancerConfig(upstream types.Upstream) string {
// 负载均衡方法
lbMethod := "byrequests" // 默认轮询
switch upstream.Algo {
case "least_conn":
lbMethod = "bybusyness"
case "bytraffic":
lbMethod = "bytraffic"
case "heartbeat":
lbMethod = "heartbeat"
if upstream.Algo != "" {
lbMethod = upstream.Algo
}
sb.WriteString(fmt.Sprintf(" ProxySet lbmethod=%s\n", lbMethod))