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

feat: 反向代理支持自定义请求头

This commit is contained in:
2026-01-26 18:30:09 +08:00
parent 197781e722
commit c4f6dd0be2
4 changed files with 106 additions and 0 deletions

View File

@@ -66,6 +66,7 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
contentStr := string(content)
proxy := &types.Proxy{
Resolver: []string{},
Headers: make(map[string]string),
Replaces: make(map[string]string),
}
@@ -114,6 +115,17 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
proxy.Replaces[sm[1]] = sm[2]
}
// 解析自定义请求头 (排除 Host)
headerPattern := regexp.MustCompile(`RequestHeader\s+set\s+(\S+)\s+"([^"]+)"`)
headerMatches := headerPattern.FindAllStringSubmatch(contentStr, -1)
for _, hm := range headerMatches {
headerName := hm[1]
headerValue := hm[2]
if headerName != "Host" {
proxy.Headers[headerName] = headerValue
}
}
return proxy, nil
}
@@ -241,6 +253,15 @@ func generateProxyConfig(proxy types.Proxy) string {
sb.WriteString(" </IfModule>\n")
}
// 自定义请求头
if len(proxy.Headers) > 0 {
sb.WriteString(" <IfModule mod_headers.c>\n")
for name, value := range proxy.Headers {
sb.WriteString(fmt.Sprintf(" RequestHeader set %s \"%s\"\n", name, value))
}
sb.WriteString(" </IfModule>\n")
}
// 响应内容替换
if len(proxy.Replaces) > 0 {
sb.WriteString(" <IfModule mod_substitute.c>\n")

View File

@@ -77,6 +77,7 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
proxy := &types.Proxy{
Location: strings.TrimSpace(matches[1]),
Resolver: []string{},
Headers: make(map[string]string),
Replaces: make(map[string]string),
}
@@ -146,6 +147,23 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
proxy.Replaces[sfm[1]] = sfm[2]
}
// 解析自定义请求头
standardHeaders := map[string]bool{
"Host": true, "X-Real-IP": true, "X-Forwarded-For": true,
"X-Forwarded-Proto": true, "Upgrade": true, "Connection": true,
"Early-Data": true, "Accept-Encoding": true,
}
headerPattern := regexp.MustCompile(`proxy_set_header\s+(\S+)\s+"?([^";]+)"?;`)
headerMatches := headerPattern.FindAllStringSubmatch(blockContent, -1)
for _, hm := range headerMatches {
headerName := strings.TrimSpace(hm[1])
headerValue := strings.TrimSpace(hm[2])
// 排除标准头
if !standardHeaders[headerName] {
proxy.Headers[headerName] = headerValue
}
}
return proxy, nil
}
@@ -269,6 +287,18 @@ func generateProxyConfig(proxy types.Proxy) string {
sb.WriteString(" proxy_cache_valid 404 1m;\n")
}
// 自定义请求头
if len(proxy.Headers) > 0 {
for name, value := range proxy.Headers {
// 变量值不加引号
if strings.HasPrefix(value, "$") {
sb.WriteString(fmt.Sprintf(" proxy_set_header %s %s;\n", name, value))
} else {
sb.WriteString(fmt.Sprintf(" proxy_set_header %s \"%s\";\n", name, value))
}
}
}
// 响应内容替换
if len(proxy.Replaces) > 0 {
sb.WriteString(" proxy_set_header Accept-Encoding \"\";\n")

View File

@@ -12,6 +12,7 @@ type Proxy struct {
Buffering bool `form:"buffering" json:"buffering"` // 是否启用缓冲
Resolver []string `form:"resolver" json:"resolver"` // 自定义 DNS 解析器配置,如: ["8.8.8.8", "ipv6=off"]
ResolverTimeout time.Duration `form:"resolver_timeout" json:"resolver_timeout"` // DNS 解析超时时间,如: 5 * time.Second
Headers map[string]string `form:"headers" json:"headers"` // 自定义请求头,如: map["X-Custom-Header"] = "value"
Replaces map[string]string `form:"replaces" json:"replaces"` // 响应内容替换,如: map["/old"] = "/new"
}

View File

@@ -303,6 +303,7 @@ const addProxy = () => {
buffering: true,
resolver: [],
resolver_timeout: 5 * 1000000000, // 5秒以纳秒为单位
headers: {},
replaces: {}
})
}
@@ -845,6 +846,59 @@ const removeCustomConfig = (index: number) => {
</n-input-group>
</n-form-item-gi>
</n-grid>
<n-divider>{{ $gettext('Custom Request Headers') }}</n-divider>
<n-flex vertical :size="8">
<n-flex
v-for="(headerValue, headerName) in proxy.headers"
:key="String(headerName)"
:size="8"
align="center"
>
<n-input
:value="String(headerName)"
:placeholder="$gettext('Header name')"
flex-1
@blur="
(e: FocusEvent) => {
const newName = (e.target as HTMLInputElement).value
const oldName = String(headerName)
if (newName && newName !== oldName) {
proxy.headers[newName] = proxy.headers[oldName]
delete proxy.headers[oldName]
}
}
"
/>
<span flex-shrink-0>:</span>
<n-input
:value="String(headerValue)"
:placeholder="$gettext('Value or variable like $host, $remote_addr')"
flex-1
@update:value="(v: string) => (proxy.headers[String(headerName)] = v)"
/>
<n-button
type="error"
secondary
size="small"
flex-shrink-0
@click="delete proxy.headers[String(headerName)]"
>
{{ $gettext('Remove') }}
</n-button>
</n-flex>
<n-button
dashed
size="small"
@click="
() => {
if (!proxy.headers) proxy.headers = {}
proxy.headers[`X-Custom-${Object.keys(proxy.headers).length}`] = ''
}
"
>
{{ $gettext('Add Request Header') }}
</n-button>
</n-flex>
<n-divider>{{ $gettext('Response Content Replacement') }}</n-divider>
<n-flex vertical :size="8">
<n-flex