mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 15:06:37 +08:00
[功能] 添加OpenRestyResolvers支持,优化DNS解析器配置和验证逻辑
This commit is contained in:
@@ -81,6 +81,7 @@ var OpenRestyProxyBusyBuffersSize = "64k"
|
||||
var OpenRestyGzipEnabled = true
|
||||
var OpenRestyGzipMinLength = 1024
|
||||
var OpenRestyGzipCompLevel = 5
|
||||
var OpenRestyResolvers = ""
|
||||
var OpenRestyCacheEnabled = false
|
||||
var OpenRestyCachePath = ""
|
||||
var OpenRestyCacheLevels = "1:2"
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net"
|
||||
"net/http"
|
||||
"openflare/common"
|
||||
"openflare/model"
|
||||
@@ -113,6 +114,16 @@ func validateOpenRestyOption(key string, value string) error {
|
||||
default:
|
||||
return fmt.Errorf("%s 仅支持 epoll、kqueue、poll、select、rtsig、/dev/poll、eventport 或留空", key)
|
||||
}
|
||||
case "OpenRestyResolvers":
|
||||
if trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
for _, token := range splitOpenRestyResolvers(trimmed) {
|
||||
if net.ParseIP(token) == nil {
|
||||
return fmt.Errorf("%s only supports IP resolver entries, invalid value %q", key, token)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case "OpenRestyEventsMultiAcceptEnabled",
|
||||
"OpenRestyWebsocketEnabled",
|
||||
"OpenRestyProxyRequestBufferingEnabled",
|
||||
@@ -176,6 +187,12 @@ func validateOpenRestyOption(key string, value string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func splitOpenRestyResolvers(value string) []string {
|
||||
return strings.FieldsFunc(value, func(r rune) bool {
|
||||
return r == ',' || r == '\n' || r == '\r' || r == '\t' || r == ' '
|
||||
})
|
||||
}
|
||||
|
||||
// GetOptions godoc
|
||||
// @Summary List editable options
|
||||
// @Tags Options
|
||||
|
||||
@@ -32,6 +32,9 @@ func TestValidateOpenRestyOption(t *testing.T) {
|
||||
{name: "cache use stale invalid", key: "OpenRestyCacheUseStale", value: "error whatever", wantErr: true},
|
||||
{name: "gzip level valid", key: "OpenRestyGzipCompLevel", value: "9"},
|
||||
{name: "gzip level invalid", key: "OpenRestyGzipCompLevel", value: "10", wantErr: true},
|
||||
{name: "resolvers empty", key: "OpenRestyResolvers", value: ""},
|
||||
{name: "resolvers valid", key: "OpenRestyResolvers", value: "1.1.1.1, 8.8.8.8"},
|
||||
{name: "resolvers invalid", key: "OpenRestyResolvers", value: "dns.internal", wantErr: true},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
@@ -80,6 +80,7 @@ func InitOptionMap() {
|
||||
common.OptionMap["OpenRestyGzipEnabled"] = strconv.FormatBool(common.OpenRestyGzipEnabled)
|
||||
common.OptionMap["OpenRestyGzipMinLength"] = strconv.Itoa(common.OpenRestyGzipMinLength)
|
||||
common.OptionMap["OpenRestyGzipCompLevel"] = strconv.Itoa(common.OpenRestyGzipCompLevel)
|
||||
common.OptionMap["OpenRestyResolvers"] = common.OpenRestyResolvers
|
||||
common.OptionMap["OpenRestyCacheEnabled"] = strconv.FormatBool(common.OpenRestyCacheEnabled)
|
||||
common.OptionMap["OpenRestyCachePath"] = common.OpenRestyCachePath
|
||||
common.OptionMap["OpenRestyCacheLevels"] = common.OpenRestyCacheLevels
|
||||
@@ -298,6 +299,8 @@ func updateOptionMap(key string, value string) {
|
||||
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
||||
common.OpenRestyGzipCompLevel = v
|
||||
}
|
||||
case "OpenRestyResolvers":
|
||||
common.OpenRestyResolvers = strings.TrimSpace(value)
|
||||
case "OpenRestyCacheEnabled":
|
||||
common.OpenRestyCacheEnabled = value == "true"
|
||||
case "OpenRestyCachePath":
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"openflare/common"
|
||||
"openflare/model"
|
||||
@@ -89,6 +90,7 @@ type openRestyConfigSnapshot struct {
|
||||
GzipEnabled bool `json:"gzip_enabled"`
|
||||
GzipMinLength int `json:"gzip_min_length"`
|
||||
GzipCompLevel int `json:"gzip_comp_level"`
|
||||
Resolvers string `json:"resolvers,omitempty"`
|
||||
CacheEnabled bool `json:"cache_enabled"`
|
||||
CachePath string `json:"cache_path,omitempty"`
|
||||
CacheLevels string `json:"cache_levels"`
|
||||
@@ -124,7 +126,6 @@ const (
|
||||
nginxLuaDirPlaceholder = "__OPENFLARE_LUA_DIR__"
|
||||
nginxObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__"
|
||||
nginxObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
||||
nginxResolverDirectivePlaceholder = "__OPENFLARE_RESOLVER_DIRECTIVE__"
|
||||
)
|
||||
|
||||
var requiredMainConfigTemplatePlaceholders = []string{
|
||||
@@ -355,7 +356,7 @@ func buildCurrentConfigBundle(requireRoutes bool) (*configBundle, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routeConfig, supportFiles, err := renderRouteConfig(routes)
|
||||
routeConfig, supportFiles, err := renderRouteConfig(routes, openRestyConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -469,6 +470,7 @@ func buildOpenRestyConfigSnapshot() openRestyConfigSnapshot {
|
||||
GzipEnabled: common.OpenRestyGzipEnabled,
|
||||
GzipMinLength: common.OpenRestyGzipMinLength,
|
||||
GzipCompLevel: common.OpenRestyGzipCompLevel,
|
||||
Resolvers: common.OpenRestyResolvers,
|
||||
CacheEnabled: common.OpenRestyCacheEnabled,
|
||||
CachePath: common.OpenRestyCachePath,
|
||||
CacheLevels: common.OpenRestyCacheLevels,
|
||||
@@ -530,6 +532,7 @@ func diffOpenRestyOptionDetails(left openRestyConfigSnapshot, right openRestyCon
|
||||
appendIfChanged("OpenRestyGzipEnabled", fmt.Sprintf("%t", left.GzipEnabled), fmt.Sprintf("%t", right.GzipEnabled))
|
||||
appendIfChanged("OpenRestyGzipMinLength", fmt.Sprintf("%d", left.GzipMinLength), fmt.Sprintf("%d", right.GzipMinLength))
|
||||
appendIfChanged("OpenRestyGzipCompLevel", fmt.Sprintf("%d", left.GzipCompLevel), fmt.Sprintf("%d", right.GzipCompLevel))
|
||||
appendIfChanged("OpenRestyResolvers", left.Resolvers, right.Resolvers)
|
||||
appendIfChanged("OpenRestyCacheEnabled", fmt.Sprintf("%t", left.CacheEnabled), fmt.Sprintf("%t", right.CacheEnabled))
|
||||
appendIfChanged("OpenRestyCachePath", left.CachePath, right.CachePath)
|
||||
appendIfChanged("OpenRestyCacheLevels", left.CacheLevels, right.CacheLevels)
|
||||
@@ -576,6 +579,7 @@ func openRestyOptionKeys() []string {
|
||||
"OpenRestyGzipEnabled",
|
||||
"OpenRestyGzipMinLength",
|
||||
"OpenRestyGzipCompLevel",
|
||||
"OpenRestyResolvers",
|
||||
"OpenRestyCacheEnabled",
|
||||
"OpenRestyCachePath",
|
||||
"OpenRestyCacheLevels",
|
||||
@@ -588,7 +592,7 @@ func openRestyOptionKeys() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func renderRouteConfig(routes []*model.ProxyRoute) (string, []SupportFile, error) {
|
||||
func renderRouteConfig(routes []*model.ProxyRoute, cfg openRestyConfigSnapshot) (string, []SupportFile, error) {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("# This file is generated by OpenFlare. Do not edit manually.\n")
|
||||
supportFiles := make([]SupportFile, 0)
|
||||
@@ -598,7 +602,7 @@ func renderRouteConfig(routes []*model.ProxyRoute) (string, []SupportFile, error
|
||||
return "", nil, fmt.Errorf("路由 %s 自定义请求头无效", route.Domain)
|
||||
}
|
||||
if !route.EnableHTTPS {
|
||||
builder.WriteString(renderHTTPProxyServer(route.Domain, route.OriginURL, route.OriginHost, customHeaders))
|
||||
builder.WriteString(renderHTTPProxyServer(route.Domain, route.OriginURL, route.OriginHost, customHeaders, cfg))
|
||||
continue
|
||||
}
|
||||
if route.CertID == nil || *route.CertID == 0 {
|
||||
@@ -615,9 +619,9 @@ func renderRouteConfig(routes []*model.ProxyRoute) (string, []SupportFile, error
|
||||
if route.RedirectHTTP {
|
||||
builder.WriteString(renderHTTPRedirectServer(route.Domain))
|
||||
} else {
|
||||
builder.WriteString(renderHTTPProxyServer(route.Domain, route.OriginURL, route.OriginHost, customHeaders))
|
||||
builder.WriteString(renderHTTPProxyServer(route.Domain, route.OriginURL, route.OriginHost, customHeaders, cfg))
|
||||
}
|
||||
builder.WriteString(renderHTTPSServer(route.Domain, route.OriginURL, route.OriginHost, certificate.ID, customHeaders))
|
||||
builder.WriteString(renderHTTPSServer(route.Domain, route.OriginURL, route.OriginHost, certificate.ID, customHeaders, cfg))
|
||||
}
|
||||
return builder.String(), dedupeSupportFiles(supportFiles), nil
|
||||
}
|
||||
@@ -673,7 +677,7 @@ func renderMainConfigTemplate(templateText string, cfg openRestyConfigSnapshot)
|
||||
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
|
||||
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength),
|
||||
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel),
|
||||
"{{OpenRestyResolverDirective}}", nginxResolverDirectivePlaceholder,
|
||||
"{{OpenRestyResolverDirective}}", renderResolverDirective(cfg.Resolvers),
|
||||
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg),
|
||||
"{{OpenRestyRouteConfigInclude}}", nginxRouteConfigPlaceholder,
|
||||
)
|
||||
@@ -752,18 +756,18 @@ func nextVersionNumber(now time.Time) (string, error) {
|
||||
return fmt.Sprintf("%s-%03d", prefix, count+1), nil
|
||||
}
|
||||
|
||||
func renderHTTPProxyServer(domain string, originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput) string {
|
||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL))
|
||||
func renderHTTPProxyServer(domain string, originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput, cfg openRestyConfigSnapshot) string {
|
||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg))
|
||||
}
|
||||
|
||||
func renderHTTPRedirectServer(domain string) string {
|
||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n return 301 https://$host$request_uri;\n}\n\n", domain, renderExactHostGuard(domain))
|
||||
}
|
||||
|
||||
func renderHTTPSServer(domain string, originURL string, originHost string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput) string {
|
||||
func renderHTTPSServer(domain string, originURL string, originHost string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput, cfg openRestyConfigSnapshot) string {
|
||||
certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID))
|
||||
keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID))
|
||||
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, certPath, keyPath, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL))
|
||||
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, certPath, keyPath, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg))
|
||||
}
|
||||
|
||||
func renderExactHostGuard(domain string) string {
|
||||
@@ -798,11 +802,14 @@ func renderProxyHeaderBlock(originURL string, originHost string, customHeaders [
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func renderProxyPassBlock(originURL string) string {
|
||||
func renderProxyPassBlock(originURL string, cfg openRestyConfigSnapshot) string {
|
||||
parsed, err := url.Parse(originURL)
|
||||
if err != nil || parsed.Host == "" || parsed.Scheme == "" {
|
||||
return fmt.Sprintf(" proxy_pass %s;\n", originURL)
|
||||
}
|
||||
if !shouldUseRuntimeResolver(originURL, cfg.Resolvers) {
|
||||
return fmt.Sprintf(" proxy_pass %s;\n", originURL)
|
||||
}
|
||||
upstreamURL := fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host)
|
||||
basePath := strings.TrimRight(parsed.EscapedPath(), "/")
|
||||
if basePath == "" || basePath == "." {
|
||||
@@ -825,6 +832,35 @@ func renderProxyPassBlock(originURL string) string {
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func shouldUseRuntimeResolver(originURL string, resolvers string) bool {
|
||||
if strings.TrimSpace(resolvers) == "" {
|
||||
return false
|
||||
}
|
||||
return requiresRuntimeResolver(originURL)
|
||||
}
|
||||
|
||||
func requiresRuntimeResolver(originURL string) bool {
|
||||
parsed, err := url.Parse(strings.TrimSpace(originURL))
|
||||
if err != nil || parsed.Hostname() == "" {
|
||||
return false
|
||||
}
|
||||
return net.ParseIP(parsed.Hostname()) == nil
|
||||
}
|
||||
|
||||
func renderResolverDirective(value string) string {
|
||||
resolvers := splitResolverList(value)
|
||||
if len(resolvers) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(" resolver %s valid=30s ipv6=off;\n resolver_timeout 5s;\n", strings.Join(resolvers, " "))
|
||||
}
|
||||
|
||||
func splitResolverList(value string) []string {
|
||||
return strings.FieldsFunc(strings.TrimSpace(value), func(r rune) bool {
|
||||
return r == ',' || r == '\n' || r == '\r' || r == '\t' || r == ' '
|
||||
})
|
||||
}
|
||||
|
||||
func resolveUpstreamServerName(originURL string, originHost string) string {
|
||||
parsed, err := url.Parse(originURL)
|
||||
if err != nil || !strings.EqualFold(parsed.Scheme, "https") {
|
||||
|
||||
@@ -63,8 +63,8 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
|
||||
if !strings.Contains(result.Version.MainConfig, "listen __OPENFLARE_OBSERVABILITY_LISTEN__;") {
|
||||
t.Fatal("expected main config to include managed openresty observability listen placeholder")
|
||||
}
|
||||
if !strings.Contains(result.Version.MainConfig, "__OPENFLARE_RESOLVER_DIRECTIVE__") {
|
||||
t.Fatal("expected main config to include managed resolver directive placeholder")
|
||||
if strings.Contains(result.Version.MainConfig, "resolver ") {
|
||||
t.Fatal("expected main config to omit resolver directive when no resolvers are configured")
|
||||
}
|
||||
if strings.Contains(result.Version.MainConfig, "allow 127.0.0.1;") {
|
||||
t.Fatal("expected main config to avoid hard-coded allow rules on observability server")
|
||||
@@ -141,11 +141,11 @@ func TestPublishConfigVersionRendersCustomHeaders(t *testing.T) {
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Connection $http_connection;") {
|
||||
t.Fatal("expected rendered config to forward websocket connection header")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "https://origin.internal";`) {
|
||||
t.Fatal("expected rendered config to defer upstream resolution via variable proxy_pass")
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://origin.internal;") {
|
||||
t.Fatal("expected rendered config to keep direct proxy_pass when no resolvers are configured")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass $openflare_upstream$request_uri;") {
|
||||
t.Fatal("expected rendered config to proxy via runtime-resolved upstream variable")
|
||||
if strings.Contains(result.Version.RenderedConfig, "proxy_pass $openflare_upstream$request_uri;") {
|
||||
t.Fatal("expected rendered config to avoid runtime-resolved proxy_pass when no resolvers are configured")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,14 +175,68 @@ func TestPublishConfigVersionOverridesOriginHostHeader(t *testing.T) {
|
||||
if !strings.Contains(result.Version.RenderedConfig, `proxy_ssl_name "git.arctel.net";`) {
|
||||
t.Fatal("expected rendered config to set proxy ssl name from origin host override")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "https://git.arctel.net";`) {
|
||||
t.Fatal("expected rendered config to avoid resolving https upstream during config load")
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://git.arctel.net;") {
|
||||
t.Fatal("expected rendered config to keep direct proxy_pass for hostname origin when resolvers are blank")
|
||||
}
|
||||
if !strings.Contains(result.Version.SnapshotJSON, `"origin_host":"git.arctel.net"`) {
|
||||
t.Fatal("expected snapshot to include origin_host override")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishConfigVersionUsesRuntimeResolverWhenConfigured(t *testing.T) {
|
||||
setupServiceTestDB(t)
|
||||
if err := model.UpdateOption("OpenRestyResolvers", "1.1.1.1, 8.8.8.8"); err != nil {
|
||||
t.Fatalf("UpdateOption OpenRestyResolvers failed: %v", err)
|
||||
}
|
||||
|
||||
_, err := CreateProxyRoute(ProxyRouteInput{
|
||||
Domain: "resolver.example.com",
|
||||
OriginURL: "https://origin.internal",
|
||||
Enabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProxyRoute failed: %v", err)
|
||||
}
|
||||
|
||||
result, err := PublishConfigVersion("root")
|
||||
if err != nil {
|
||||
t.Fatalf("PublishConfigVersion failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(result.Version.MainConfig, "resolver 1.1.1.1 8.8.8.8 valid=30s ipv6=off;") {
|
||||
t.Fatal("expected main config to render configured resolver directive")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "https://origin.internal";`) {
|
||||
t.Fatal("expected rendered config to use runtime upstream variable when resolvers are configured")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass $openflare_upstream$request_uri;") {
|
||||
t.Fatal("expected rendered config to proxy via runtime-resolved upstream variable when resolvers are configured")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishConfigVersionKeepsDirectProxyPassForIPOrigins(t *testing.T) {
|
||||
setupServiceTestDB(t)
|
||||
|
||||
_, err := CreateProxyRoute(ProxyRouteInput{
|
||||
Domain: "ip-origin.example.com",
|
||||
OriginURL: "http://10.0.0.8:8080",
|
||||
Enabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProxyRoute failed: %v", err)
|
||||
}
|
||||
|
||||
result, err := PublishConfigVersion("root")
|
||||
if err != nil {
|
||||
t.Fatalf("PublishConfigVersion failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass http://10.0.0.8:8080;") {
|
||||
t.Fatal("expected rendered config to keep direct proxy_pass for IP origin")
|
||||
}
|
||||
if strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "http://10.0.0.8:8080"`) {
|
||||
t.Fatal("expected rendered config to avoid runtime resolver variables for IP origin")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewConfigVersionCanDisableWebsocketHeaders(t *testing.T) {
|
||||
setupServiceTestDB(t)
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ const defaultPerformanceFields = {
|
||||
OpenRestyGzipEnabled: true,
|
||||
OpenRestyGzipMinLength: '1024',
|
||||
OpenRestyGzipCompLevel: '5',
|
||||
OpenRestyResolvers: '',
|
||||
OpenRestyCacheEnabled: false,
|
||||
OpenRestyCachePath: '',
|
||||
OpenRestyCacheLevels: '1:2',
|
||||
@@ -103,6 +104,8 @@ const performanceFieldTooltips: Record<string, string> = {
|
||||
gzip_min_length:
|
||||
'只有响应体超过该字节数时才会启用 gzip,避免对极小响应做无意义压缩。',
|
||||
gzip_comp_level: 'gzip 压缩等级,1 更省 CPU,9 压缩更高但更耗 CPU。',
|
||||
resolvers:
|
||||
'可选填写运行时 DNS 解析器 IP,支持逗号、空格或换行分隔;留空时不额外生成 resolver 指令。',
|
||||
proxy_cache_path: '缓存目录路径,对应 proxy_cache_path 指令中的磁盘位置。',
|
||||
levels: '缓存目录层级,例如 1:2,可控制缓存文件的目录分布。',
|
||||
inactive: '缓存对象在未命中访问时的失活时间,例如 30m。',
|
||||
@@ -241,6 +244,7 @@ export function PerformancePage() {
|
||||
OpenRestyGzipEnabled: toBoolean(optionMap.OpenRestyGzipEnabled, true),
|
||||
OpenRestyGzipMinLength: optionMap.OpenRestyGzipMinLength ?? '1024',
|
||||
OpenRestyGzipCompLevel: optionMap.OpenRestyGzipCompLevel ?? '5',
|
||||
OpenRestyResolvers: optionMap.OpenRestyResolvers ?? '',
|
||||
OpenRestyCacheEnabled: toBoolean(optionMap.OpenRestyCacheEnabled, false),
|
||||
OpenRestyCachePath: optionMap.OpenRestyCachePath ?? '',
|
||||
OpenRestyCacheLevels: optionMap.OpenRestyCacheLevels ?? '1:2',
|
||||
@@ -420,6 +424,7 @@ export function PerformancePage() {
|
||||
'OpenRestyProxyReadTimeout',
|
||||
performanceFields.OpenRestyProxyReadTimeout.trim(),
|
||||
],
|
||||
['OpenRestyResolvers', performanceFields.OpenRestyResolvers.trim()],
|
||||
[
|
||||
'OpenRestyWebsocketEnabled',
|
||||
String(performanceFields.OpenRestyWebsocketEnabled),
|
||||
@@ -938,6 +943,23 @@ export function PerformancePage() {
|
||||
}
|
||||
/>
|
||||
</ResourceField>
|
||||
<ResourceField
|
||||
label="resolver"
|
||||
tooltip={performanceFieldTooltips.resolvers}
|
||||
hint="留空时走 OpenResty 默认行为;填写时请使用 DNS 服务器 IP。"
|
||||
>
|
||||
<ResourceTextarea
|
||||
value={performanceFields.OpenRestyResolvers}
|
||||
onChange={(event) =>
|
||||
setPerformanceFields((previous) => ({
|
||||
...previous,
|
||||
OpenRestyResolvers: event.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder="例如:10.0.0.2, 1.1.1.1"
|
||||
minRows={3}
|
||||
/>
|
||||
</ResourceField>
|
||||
<ToggleField
|
||||
label="websocket"
|
||||
tooltip={performanceFieldTooltips.websocket}
|
||||
|
||||
Reference in New Issue
Block a user