122 lines · 4.0 KB · Go
RawDownload
package config import (	"fmt"	"strings" 	log "github.com/sirupsen/logrus"	"golang.org/x/crypto/bcrypt"	"gopkg.in/yaml.v3") // ParseConfigBytes parses a YAML configuration payload into Config and applies the same// in-memory normalizations as LoadConfigOptional, without persisting any changes to disk.func ParseConfigBytes(data []byte) (*Config, error) {	if len(data) == 0 {		return nil, fmt.Errorf("config payload is empty")	} 	if errValidate := validateCredentialWeightYAML(data); errValidate != nil {		return nil, errValidate	} 	var cfg Config	// Keep defaults aligned with LoadConfigOptional.	cfg.Host = "" // Default empty: binds to all interfaces (IPv4 + IPv6)	cfg.LoggingToFile = false	cfg.LogsMaxTotalSizeMB = 0	cfg.ErrorLogsMaxFiles = 10	cfg.UsageStatisticsEnabled = false	cfg.RedisUsageQueueRetentionSeconds = 60	cfg.DisableCooling = false	cfg.SaveCooldownStatus = false	cfg.TransientErrorCooldownSeconds = 0	cfg.DisableImageGeneration = DisableImageGenerationOff	cfg.WebsocketAuth = true	cfg.Pprof.Enable = false	cfg.Pprof.Addr = DefaultPprofAddr	cfg.Discovery.Enabled = false	cfg.Discovery.ServiceType = DefaultDiscoveryServiceType	cfg.Discovery.Subtypes = []string{"_chat-completions", "_responses", "_messages", "_generate-content", "_interactions"}	cfg.RemoteManagement.PanelGitHubRepository = DefaultPanelGitHubRepository	cfg.CredentialInFlight = DefaultCredentialInFlightConfig() 	if err := yaml.Unmarshal(data, &cfg); err != nil {		return nil, fmt.Errorf("parse config payload: %w", err)	} 	cfg.CredentialConcurrency = cfg.CredentialConcurrency.WithDefaults()	if errValidate := cfg.CredentialInFlight.Validate(); errValidate != nil {		return nil, errValidate	}	if errValidate := cfg.ValidateCredentialWeights(); errValidate != nil {		return nil, errValidate	}	if cfg.Discovery.ServiceType == "" {		cfg.Discovery.ServiceType = DefaultDiscoveryServiceType	}	if len(cfg.Discovery.Subtypes) == 0 {		cfg.Discovery.Subtypes = []string{"_chat-completions", "_responses", "_messages", "_generate-content", "_interactions"}	} 	// Hash remote management key if plaintext is detected (nested), but do NOT persist.	if cfg.RemoteManagement.SecretKey != "" && !looksLikeBcrypt(cfg.RemoteManagement.SecretKey) {		hashed, errHash := bcrypt.GenerateFromPassword([]byte(cfg.RemoteManagement.SecretKey), bcrypt.DefaultCost)		if errHash != nil {			return nil, fmt.Errorf("hash remote management key: %w", errHash)		}		cfg.RemoteManagement.SecretKey = string(hashed)	} 	cfg.RemoteManagement.PanelGitHubRepository = strings.TrimSpace(cfg.RemoteManagement.PanelGitHubRepository)	if cfg.RemoteManagement.PanelGitHubRepository == "" {		cfg.RemoteManagement.PanelGitHubRepository = DefaultPanelGitHubRepository	} 	cfg.Pprof.Addr = strings.TrimSpace(cfg.Pprof.Addr)	if cfg.Pprof.Addr == "" {		cfg.Pprof.Addr = DefaultPprofAddr	} 	if cfg.LogsMaxTotalSizeMB < 0 {		cfg.LogsMaxTotalSizeMB = 0	} 	if cfg.ErrorLogsMaxFiles < 0 {		cfg.ErrorLogsMaxFiles = 10	} 	if cfg.RedisUsageQueueRetentionSeconds <= 0 {		cfg.RedisUsageQueueRetentionSeconds = 60	} else if cfg.RedisUsageQueueRetentionSeconds > 3600 {		log.WithField("value", cfg.RedisUsageQueueRetentionSeconds).Warn("redis-usage-queue-retention-seconds too large; clamping to 3600")		cfg.RedisUsageQueueRetentionSeconds = 3600	} 	if cfg.MaxRetryCredentials < 0 {		cfg.MaxRetryCredentials = 0	} 	cfg.NormalizePluginsConfig()	if errResolvePluginsDir := cfg.ResolvePluginsDir(); errResolvePluginsDir != nil && cfg.Plugins.Enabled {		return nil, errResolvePluginsDir	} 	// Apply the same sanitization pipeline.	cfg.SanitizeGeminiKeys()	cfg.SanitizeInteractionsKeys()	cfg.SanitizeVertexCompatKeys()	cfg.SanitizeCodexKeys()	cfg.SanitizeXAIKeys()	cfg.SanitizeMetaKeys()	cfg.SanitizeCodexHeaderDefaults()	cfg.SanitizeClaudeHeaderDefaults()	cfg.SanitizeClaudeKeys()	cfg.SanitizeOpenAICompatibility()	cfg.OAuthExcludedModels = NormalizeOAuthExcludedModels(cfg.OAuthExcludedModels)	cfg.SanitizeOAuthModelAlias()	cfg.SanitizeOAuthRequestScopedErrors()	cfg.SanitizePayloadRules() 	return &cfg, nil}