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

Add redirect and advanced settings tabs to website editor (#1267)

* Initial plan

* Add redirect tab to website editor with drag-and-drop support

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Add advanced settings tab with rate limiting and basic auth

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Add htpasswd file support for basic auth (nginx and apache compatible)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Fix code review issues: prevent duplicate usernames in basic auth

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: bug

* fix: bug

* feat: 支持real ip设置

* feat: 支持real ip设置

* fix: lint

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
This commit is contained in:
Copilot
2026-01-22 06:32:13 +08:00
committed by GitHub
parent 0e958cbd78
commit a386dc3a2a
13 changed files with 821 additions and 109 deletions

View File

@@ -78,11 +78,21 @@ func parseRedirectFile(filePath string) (*types.Redirect, error) {
redirectMatchPattern := regexp.MustCompile(`RedirectMatch\s+(\d+)\s+(\S+)\s+(\S+)`)
if matches := redirectMatchPattern.FindStringSubmatch(contentStr); matches != nil {
statusCode, _ := strconv.Atoi(matches[1])
to := matches[3]
keepURI := strings.Contains(to, "$1")
if keepURI {
to = strings.TrimSuffix(to, "$1")
}
// 还原 from 为简单路径格式
from := matches[2]
from = strings.TrimPrefix(from, "^")
from = strings.TrimSuffix(from, "(.*)$")
from = strings.TrimSuffix(from, "$")
return &types.Redirect{
Type: types.RedirectTypeURL,
From: matches[2],
To: matches[3],
KeepURI: strings.Contains(matches[3], "$1"),
From: from,
To: to,
KeepURI: keepURI,
StatusCode: statusCode,
}, nil
}
@@ -94,11 +104,16 @@ func parseRedirectFile(filePath string) (*types.Redirect, error) {
if matches := hostRewritePattern.FindStringSubmatch(contentStr); matches != nil {
statusCode, _ := strconv.Atoi(matches[3])
host := strings.ReplaceAll(matches[1], `\.`, ".")
to := matches[2]
keepURI := strings.Contains(to, "$1")
if keepURI {
to = strings.TrimSuffix(to, "$1")
}
return &types.Redirect{
Type: types.RedirectTypeHost,
From: host,
To: matches[2],
KeepURI: strings.Contains(matches[2], "$1"),
To: to,
KeepURI: keepURI,
StatusCode: statusCode,
}, nil
}

View File

@@ -541,24 +541,25 @@ func (v *baseVhost) RateLimit() *types.RateLimit {
return nil
}
rateLimit := &types.RateLimit{
Zone: make(map[string]string),
}
rateLimit := &types.RateLimit{}
// 获取速率限制值
rateValue := v.vhost.GetDirectiveValue("SetEnv")
if rateValue != "" {
rateLimit.Rate = rateValue
// 获取速率限制值 (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 {
// 设置 mod_ratelimit
v.vhost.SetDirective("SetOutputFilter", "RATE_LIMIT")
if limit.Rate != "" {
v.vhost.SetDirective("SetEnv", "rate-limit", limit.Rate)
// 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
@@ -606,6 +607,58 @@ func (v *baseVhost) ClearBasicAuth() error {
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)

View File

@@ -237,12 +237,13 @@ func (s *VhostTestSuite) TestRateLimit() {
s.Nil(s.vhost.RateLimit())
limit := &types.RateLimit{
Rate: "512",
Rate: 512,
}
s.NoError(s.vhost.SetRateLimit(limit))
got := s.vhost.RateLimit()
s.NotNil(got)
s.Equal(512, got.Rate)
s.NoError(s.vhost.ClearRateLimit())
s.Nil(s.vhost.RateLimit())