mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-09 17:20:52 +08:00
Bring in native Meta (Muse Code) provider support while keeping the existing Devin integration and original Meta commit history.
123 lines
4.0 KiB
Go
123 lines
4.0 KiB
Go
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
|
|
}
|