Files
ds2api/internal/config/validation.go
2026-04-27 23:36:56 +08:00

154 lines
4.3 KiB
Go

package config
import (
"fmt"
"strings"
)
func ValidateConfig(c Config) error {
if err := ValidateProxyConfig(c.Proxies); err != nil {
return err
}
if err := ValidateAdminConfig(c.Admin); err != nil {
return err
}
if err := ValidateRuntimeConfig(c.Runtime); err != nil {
return err
}
if err := ValidateResponsesConfig(c.Responses); err != nil {
return err
}
if err := ValidateEmbeddingsConfig(c.Embeddings); err != nil {
return err
}
if err := ValidateAutoDeleteConfig(c.AutoDelete); err != nil {
return err
}
if err := ValidateCurrentInputFileConfig(c.CurrentInputFile); err != nil {
return err
}
if err := ValidateAccountProxyReferences(c.Accounts, c.Proxies); err != nil {
return err
}
return nil
}
func ValidateProxyConfig(proxies []Proxy) error {
seen := make(map[string]struct{}, len(proxies))
for _, proxy := range proxies {
proxy = NormalizeProxy(proxy)
if err := ValidateTrimmedString("proxies.id", proxy.ID, true); err != nil {
return err
}
switch proxy.Type {
case "socks5", "socks5h":
default:
return fmt.Errorf("proxies.type must be one of socks5, socks5h")
}
if err := ValidateTrimmedString("proxies.host", proxy.Host, true); err != nil {
return err
}
if err := ValidateIntRange("proxies.port", proxy.Port, 1, 65535, true); err != nil {
return err
}
if _, ok := seen[proxy.ID]; ok {
return fmt.Errorf("duplicate proxy id: %s", proxy.ID)
}
seen[proxy.ID] = struct{}{}
}
return nil
}
func ValidateAccountProxyReferences(accounts []Account, proxies []Proxy) error {
if len(accounts) == 0 {
return nil
}
ids := make(map[string]struct{}, len(proxies))
for _, proxy := range proxies {
ids[NormalizeProxy(proxy).ID] = struct{}{}
}
for _, acc := range accounts {
proxyID := strings.TrimSpace(acc.ProxyID)
if proxyID == "" {
continue
}
if _, ok := ids[proxyID]; !ok {
return fmt.Errorf("account proxy_id references unknown proxy: %s", proxyID)
}
}
return nil
}
func ValidateAdminConfig(admin AdminConfig) error {
return ValidateIntRange("admin.jwt_expire_hours", admin.JWTExpireHours, 1, 720, false)
}
func ValidateRuntimeConfig(runtime RuntimeConfig) error {
if err := ValidateIntRange("runtime.account_max_inflight", runtime.AccountMaxInflight, 1, 256, false); err != nil {
return err
}
if err := ValidateIntRange("runtime.account_max_queue", runtime.AccountMaxQueue, 1, 200000, false); err != nil {
return err
}
if err := ValidateIntRange("runtime.global_max_inflight", runtime.GlobalMaxInflight, 1, 200000, false); err != nil {
return err
}
if err := ValidateIntRange("runtime.token_refresh_interval_hours", runtime.TokenRefreshIntervalHours, 1, 720, false); err != nil {
return err
}
if runtime.AccountMaxInflight > 0 && runtime.GlobalMaxInflight > 0 && runtime.GlobalMaxInflight < runtime.AccountMaxInflight {
return fmt.Errorf("runtime.global_max_inflight must be >= runtime.account_max_inflight")
}
return nil
}
func ValidateResponsesConfig(responses ResponsesConfig) error {
return ValidateIntRange("responses.store_ttl_seconds", responses.StoreTTLSeconds, 30, 86400, false)
}
func ValidateEmbeddingsConfig(embeddings EmbeddingsConfig) error {
return ValidateTrimmedString("embeddings.provider", embeddings.Provider, false)
}
func ValidateAutoDeleteConfig(autoDelete AutoDeleteConfig) error {
return ValidateAutoDeleteMode(autoDelete.Mode)
}
func ValidateCurrentInputFileConfig(currentInputFile CurrentInputFileConfig) error {
if currentInputFile.MinChars != 0 {
return ValidateIntRange("current_input_file.min_chars", currentInputFile.MinChars, 1, 100000000, true)
}
return nil
}
func ValidateIntRange(name string, value, min, max int, required bool) error {
if value == 0 && !required {
return nil
}
if value < min || value > max {
return fmt.Errorf("%s must be between %d and %d", name, min, max)
}
return nil
}
func ValidateTrimmedString(name, value string, required bool) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
if !required && value == "" {
return nil
}
return fmt.Errorf("%s cannot be empty", name)
}
return nil
}
func ValidateAutoDeleteMode(mode string) error {
mode = strings.ToLower(strings.TrimSpace(mode))
switch mode {
case "", "none", "single", "all":
return nil
default:
return fmt.Errorf("auto_delete.mode must be one of none, single, all")
}
}