diff --git a/internal/apps/openflare/config_version/logics.go b/internal/apps/openflare/config_version/logics.go index 2202c962..76bb6c58 100644 --- a/internal/apps/openflare/config_version/logics.go +++ b/internal/apps/openflare/config_version/logics.go @@ -528,6 +528,9 @@ func diffOpenRestyOptionDetails(left openRestyConfigSnapshot, right openRestyCon appendIfChanged("OpenRestyCacheLockEnabled", fmt.Sprintf("%t", left.CacheLockEnabled), fmt.Sprintf("%t", right.CacheLockEnabled)) appendIfChanged("OpenRestyCacheLockTimeout", left.CacheLockTimeout, right.CacheLockTimeout) appendIfChanged("OpenRestyCacheUseStale", left.CacheUseStale, right.CacheUseStale) + appendIfChanged("OpenRestyDefaultLimitConnPerServer", fmt.Sprintf("%d", left.DefaultLimitConnPerServer), fmt.Sprintf("%d", right.DefaultLimitConnPerServer)) + appendIfChanged("OpenRestyDefaultLimitConnPerIP", fmt.Sprintf("%d", left.DefaultLimitConnPerIP), fmt.Sprintf("%d", right.DefaultLimitConnPerIP)) + appendIfChanged("OpenRestyDefaultLimitRate", left.DefaultLimitRate, right.DefaultLimitRate) return changes } @@ -576,5 +579,8 @@ func openRestyOptionKeys() []string { "OpenRestyCacheLockEnabled", "OpenRestyCacheLockTimeout", "OpenRestyCacheUseStale", + "OpenRestyDefaultLimitConnPerServer", + "OpenRestyDefaultLimitConnPerIP", + "OpenRestyDefaultLimitRate", } } diff --git a/internal/apps/openflare/config_version/snapshot.go b/internal/apps/openflare/config_version/snapshot.go index d69de94d..895c41b9 100644 --- a/internal/apps/openflare/config_version/snapshot.go +++ b/internal/apps/openflare/config_version/snapshot.go @@ -135,6 +135,9 @@ type openRestyConfigSnapshot struct { CacheLockTimeout string `json:"cache_lock_timeout"` CacheUseStale string `json:"cache_use_stale"` MainConfigTemplate string `json:"main_config_template,omitempty"` + DefaultLimitConnPerServer int `json:"default_limit_conn_per_server,omitempty"` + DefaultLimitConnPerIP int `json:"default_limit_conn_per_ip,omitempty"` + DefaultLimitRate string `json:"default_limit_rate,omitempty"` } type snapshotDocument struct { @@ -479,6 +482,15 @@ func buildOpenRestyConfigSnapshot(ctx context.Context) openRestyConfigSnapshot { return val } + // 0 为合法关闭值,不能与 getIntConfig 的 val<=0 语义混用 + getNonNegIntConfig := func(key string, defaultVal int) int { + val, err := repository.GetIntByKey(ctx, key) + if err != nil || val < 0 { + return defaultVal + } + return val + } + getBoolConfig := func(key string, defaultVal bool) bool { val, err := repository.GetBoolByKey(ctx, key) if err != nil { @@ -533,6 +545,12 @@ func buildOpenRestyConfigSnapshot(ctx context.Context) openRestyConfigSnapshot { CacheLockTimeout: getStringConfig(model.ConfigKeyOpenRestyCacheLockTimeout, "5s"), CacheUseStale: getStringConfig(model.ConfigKeyOpenRestyCacheUseStale, "error timeout updating http_500 http_502 http_503 http_504"), MainConfigTemplate: getStringConfig(model.ConfigKeyOpenRestyMainConfigTemplate, model.DefaultOpenRestyMainConfigTemplate), + DefaultLimitConnPerServer: getNonNegIntConfig(model.ConfigKeyOpenRestyDefaultLimitConnPerServer, 0), + DefaultLimitConnPerIP: getNonNegIntConfig(model.ConfigKeyOpenRestyDefaultLimitConnPerIP, 0), + DefaultLimitRate: strings.ToLower(strings.TrimSpace(getStringConfig(model.ConfigKeyOpenRestyDefaultLimitRate, ""))), + } + if snapshot.DefaultLimitRate == "0" { + snapshot.DefaultLimitRate = "" } snapshot.CachePath = normalizeProxyCachePathForSnapshot(snapshot.CacheEnabled, snapshot.CachePath) return snapshot diff --git a/internal/apps/openflare/option/openresty_validators.go b/internal/apps/openflare/option/openresty_validators.go index f7964737..43e54ad4 100644 --- a/internal/apps/openflare/option/openresty_validators.go +++ b/internal/apps/openflare/option/openresty_validators.go @@ -50,8 +50,13 @@ var openRestyOptionValidators = map[string]func(key, value string) error{ model.ConfigKeyOpenRestyCacheKeyTemplate: validateOpenRestyCacheKeyTemplate, model.ConfigKeyOpenRestyCacheUseStale: validateOpenRestyCacheUseStale, model.ConfigKeyOpenRestyMainConfigTemplate: validateOpenRestyMainConfigTemplate, + model.ConfigKeyOpenRestyDefaultLimitConnPerServer: validateNonNegativeIntegerOption, + model.ConfigKeyOpenRestyDefaultLimitConnPerIP: validateNonNegativeIntegerOption, + model.ConfigKeyOpenRestyDefaultLimitRate: validateOpenRestyDefaultLimitRate, } +var openRestyDefaultLimitRatePattern = regexp.MustCompile(`^\d+[kKmM]?$`) + func validateOpenRestyOption(key, value string) error { trimmed := strings.TrimSpace(value) if validator, ok := openRestyOptionValidators[key]; ok { @@ -179,3 +184,13 @@ func validateOpenRestyMainConfigTemplate(key, value string) error { } return nil } + +func validateOpenRestyDefaultLimitRate(key, trimmed string) error { + if trimmed == "" || trimmed == "0" { + return nil + } + if !openRestyDefaultLimitRatePattern.MatchString(strings.ToLower(trimmed)) { + return fmt.Errorf("%s 格式不合法,请使用 512k、1m 或纯数字,空表示关闭", key) + } + return nil +} diff --git a/internal/apps/openflare/option/validate.go b/internal/apps/openflare/option/validate.go index 4de58baa..8606caa9 100644 --- a/internal/apps/openflare/option/validate.go +++ b/internal/apps/openflare/option/validate.go @@ -73,6 +73,14 @@ func validatePositiveIntegerOption(key, value string) error { return nil } +func validateNonNegativeIntegerOption(key, value string) error { + intValue, err := strconv.Atoi(value) + if err != nil || intValue < 0 { + return fmt.Errorf("%s 必须为大于等于 0 的整数", key) + } + return nil +} + func validateBooleanOption(key, value string) error { switch value { case optionValueTrue, "false": diff --git a/internal/db/migrator/goose/postgres/202607190001_add_openresty_default_rate_limits.sql b/internal/db/migrator/goose/postgres/202607190001_add_openresty_default_rate_limits.sql new file mode 100644 index 00000000..ab39b72f --- /dev/null +++ b/internal/db/migrator/goose/postgres/202607190001_add_openresty_default_rate_limits.sql @@ -0,0 +1,14 @@ +-- +goose Up +INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at) +VALUES + ('openresty_default_limit_conn_per_server', '0', 'business', 0, '默认站点并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('openresty_default_limit_conn_per_ip', '0', 'business', 0, '默认单 IP 并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('openresty_default_limit_rate', '', 'business', 0, '默认单请求带宽限速(空关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +ON CONFLICT (key) DO NOTHING; + +-- +goose Down +DELETE FROM w_system_configs WHERE key IN ( + 'openresty_default_limit_conn_per_server', + 'openresty_default_limit_conn_per_ip', + 'openresty_default_limit_rate' +); diff --git a/internal/db/migrator/goose/sqlite/202607190001_add_openresty_default_rate_limits.sql b/internal/db/migrator/goose/sqlite/202607190001_add_openresty_default_rate_limits.sql new file mode 100644 index 00000000..ab39b72f --- /dev/null +++ b/internal/db/migrator/goose/sqlite/202607190001_add_openresty_default_rate_limits.sql @@ -0,0 +1,14 @@ +-- +goose Up +INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at) +VALUES + ('openresty_default_limit_conn_per_server', '0', 'business', 0, '默认站点并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('openresty_default_limit_conn_per_ip', '0', 'business', 0, '默认单 IP 并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('openresty_default_limit_rate', '', 'business', 0, '默认单请求带宽限速(空关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +ON CONFLICT (key) DO NOTHING; + +-- +goose Down +DELETE FROM w_system_configs WHERE key IN ( + 'openresty_default_limit_conn_per_server', + 'openresty_default_limit_conn_per_ip', + 'openresty_default_limit_rate' +); diff --git a/internal/model/system_configs.go b/internal/model/system_configs.go index 9b7f1063..fd9ba9d3 100644 --- a/internal/model/system_configs.go +++ b/internal/model/system_configs.go @@ -105,6 +105,9 @@ const ( ConfigKeyOpenRestyCacheLockTimeout = "openresty_cache_lock_timeout" // 缓存锁超时 ConfigKeyOpenRestyCacheUseStale = "openresty_cache_use_stale" // 缓存失效策略 ConfigKeyOpenRestyMainConfigTemplate = "openresty_main_config_template" // 主配置模板 + ConfigKeyOpenRestyDefaultLimitConnPerServer = "openresty_default_limit_conn_per_server" // 默认站点并发连接 + ConfigKeyOpenRestyDefaultLimitConnPerIP = "openresty_default_limit_conn_per_ip" // 默认单 IP 并发连接 + ConfigKeyOpenRestyDefaultLimitRate = "openresty_default_limit_rate" // 默认单请求带宽 ) const (