mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 13:47:15 +08:00
feat: 反向代理支持自定义请求头
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user