package settings import ( "fmt" "strings" "ds2api/internal/config" ) func boolFrom(v any) bool { if v == nil { return false } switch x := v.(type) { case bool: return x case string: return strings.ToLower(strings.TrimSpace(x)) == "true" default: return false } } func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *config.RuntimeConfig, *config.ResponsesConfig, *config.EmbeddingsConfig, *config.AutoDeleteConfig, *config.CurrentInputFileConfig, *config.ThinkingInjectionConfig, map[string]string, error) { var ( adminCfg *config.AdminConfig runtimeCfg *config.RuntimeConfig respCfg *config.ResponsesConfig embCfg *config.EmbeddingsConfig autoDeleteCfg *config.AutoDeleteConfig currentInputCfg *config.CurrentInputFileConfig thinkingInjCfg *config.ThinkingInjectionConfig aliasMap map[string]string ) if raw, ok := req["admin"].(map[string]any); ok { cfg := &config.AdminConfig{} if v, exists := raw["jwt_expire_hours"]; exists { n := intFrom(v) if err := config.ValidateIntRange("admin.jwt_expire_hours", n, 1, 720, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.JWTExpireHours = n } adminCfg = cfg } if raw, ok := req["runtime"].(map[string]any); ok { cfg := &config.RuntimeConfig{} if v, exists := raw["account_max_inflight"]; exists { n := intFrom(v) if err := config.ValidateIntRange("runtime.account_max_inflight", n, 1, 256, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.AccountMaxInflight = n } if v, exists := raw["account_max_queue"]; exists { n := intFrom(v) if err := config.ValidateIntRange("runtime.account_max_queue", n, 1, 200000, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.AccountMaxQueue = n } if v, exists := raw["global_max_inflight"]; exists { n := intFrom(v) if err := config.ValidateIntRange("runtime.global_max_inflight", n, 1, 200000, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.GlobalMaxInflight = n } if v, exists := raw["token_refresh_interval_hours"]; exists { n := intFrom(v) if err := config.ValidateIntRange("runtime.token_refresh_interval_hours", n, 1, 720, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.TokenRefreshIntervalHours = n } if cfg.AccountMaxInflight > 0 && cfg.GlobalMaxInflight > 0 && cfg.GlobalMaxInflight < cfg.AccountMaxInflight { return nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("runtime.global_max_inflight must be >= runtime.account_max_inflight") } runtimeCfg = cfg } if raw, ok := req["responses"].(map[string]any); ok { cfg := &config.ResponsesConfig{} if v, exists := raw["store_ttl_seconds"]; exists { n := intFrom(v) if err := config.ValidateIntRange("responses.store_ttl_seconds", n, 30, 86400, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.StoreTTLSeconds = n } respCfg = cfg } if raw, ok := req["embeddings"].(map[string]any); ok { cfg := &config.EmbeddingsConfig{} if v, exists := raw["provider"]; exists { p := strings.TrimSpace(fmt.Sprintf("%v", v)) if err := config.ValidateTrimmedString("embeddings.provider", p, false); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.Provider = p } embCfg = cfg } if raw, ok := req["model_aliases"].(map[string]any); ok { if aliasMap == nil { aliasMap = map[string]string{} } for k, v := range raw { key := strings.TrimSpace(k) val := strings.TrimSpace(fmt.Sprintf("%v", v)) if key == "" || val == "" { continue } aliasMap[key] = val } } if raw, ok := req["auto_delete"].(map[string]any); ok { cfg := &config.AutoDeleteConfig{} if v, exists := raw["mode"]; exists { mode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", v))) if err := config.ValidateAutoDeleteMode(mode); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } if mode == "" { mode = "none" } cfg.Mode = mode } if v, exists := raw["sessions"]; exists { cfg.Sessions = boolFrom(v) } autoDeleteCfg = cfg } if raw, ok := req["current_input_file"].(map[string]any); ok { cfg := &config.CurrentInputFileConfig{} if v, exists := raw["enabled"]; exists { enabled := boolFrom(v) cfg.Enabled = &enabled } if v, exists := raw["min_chars"]; exists { n := intFrom(v) if err := config.ValidateIntRange("current_input_file.min_chars", n, 0, 100000000, true); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } cfg.MinChars = n } if err := config.ValidateCurrentInputFileConfig(*cfg); err != nil { return nil, nil, nil, nil, nil, nil, nil, nil, err } currentInputCfg = cfg } if raw, ok := req["thinking_injection"].(map[string]any); ok { cfg := &config.ThinkingInjectionConfig{} if v, exists := raw["enabled"]; exists { b := boolFrom(v) cfg.Enabled = &b } if v, exists := raw["prompt"]; exists { cfg.Prompt = strings.TrimSpace(fmt.Sprintf("%v", v)) } thinkingInjCfg = cfg } return adminCfg, runtimeCfg, respCfg, embCfg, autoDeleteCfg, currentInputCfg, thinkingInjCfg, aliasMap, nil }