mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
refactor(auth): simplify inline API key provider logic and improve configuration consistency
- Replaced `SyncInlineAPIKeys` with `MakeInlineAPIKeyProvider` for better clarity and reduced redundancy. - Removed legacy logic for inline API key syncing and migration. - Enhanced provider synchronization logic to handle empty states consistently. - Added normalization to API key handling across configurations. - Updated handlers to reflect streamlined provider update logic.
This commit is contained in:
@@ -79,51 +79,35 @@ func ReconcileProviders(oldCfg, newCfg *config.Config, existing []sdkaccess.Prov
|
|||||||
finalIDs[key] = struct{}{}
|
finalIDs[key] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result) == 0 && len(newCfg.APIKeys) > 0 {
|
if len(result) == 0 {
|
||||||
sdkConfig.SyncInlineAPIKeys(&newCfg.SDKConfig, newCfg.APIKeys)
|
if inline := sdkConfig.MakeInlineAPIKeyProvider(newCfg.APIKeys); inline != nil {
|
||||||
if providerCfg := newCfg.ConfigAPIKeyProvider(); providerCfg != nil {
|
key := providerIdentifier(inline)
|
||||||
key := providerIdentifier(providerCfg)
|
|
||||||
if key != "" {
|
if key != "" {
|
||||||
if oldCfgProvider, ok := oldCfgMap[key]; ok {
|
if oldCfgProvider, ok := oldCfgMap[key]; ok {
|
||||||
isAliased := oldCfgProvider == providerCfg
|
if providerConfigEqual(oldCfgProvider, inline) {
|
||||||
if !isAliased && providerConfigEqual(oldCfgProvider, providerCfg) {
|
|
||||||
if existingProvider, okExisting := existingMap[key]; okExisting {
|
if existingProvider, okExisting := existingMap[key]; okExisting {
|
||||||
result = append(result, existingProvider)
|
result = append(result, existingProvider)
|
||||||
} else {
|
finalIDs[key] = struct{}{}
|
||||||
provider, buildErr := sdkaccess.BuildProvider(providerCfg, &newCfg.SDKConfig)
|
goto inlineDone
|
||||||
if buildErr != nil {
|
|
||||||
return nil, nil, nil, nil, buildErr
|
|
||||||
}
|
|
||||||
if _, existed := existingMap[key]; existed {
|
|
||||||
appendChange(&updated, key)
|
|
||||||
} else {
|
|
||||||
appendChange(&added, key)
|
|
||||||
}
|
|
||||||
result = append(result, provider)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
provider, buildErr := sdkaccess.BuildProvider(providerCfg, &newCfg.SDKConfig)
|
|
||||||
if buildErr != nil {
|
|
||||||
return nil, nil, nil, nil, buildErr
|
|
||||||
}
|
|
||||||
if _, existed := existingMap[key]; existed {
|
|
||||||
appendChange(&updated, key)
|
|
||||||
} else {
|
|
||||||
appendChange(&added, key)
|
|
||||||
}
|
|
||||||
result = append(result, provider)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
provider, buildErr := sdkaccess.BuildProvider(providerCfg, &newCfg.SDKConfig)
|
|
||||||
if buildErr != nil {
|
|
||||||
return nil, nil, nil, nil, buildErr
|
|
||||||
}
|
|
||||||
appendChange(&added, key)
|
|
||||||
result = append(result, provider)
|
|
||||||
}
|
}
|
||||||
|
provider, buildErr := sdkaccess.BuildProvider(inline, &newCfg.SDKConfig)
|
||||||
|
if buildErr != nil {
|
||||||
|
return nil, nil, nil, nil, buildErr
|
||||||
|
}
|
||||||
|
if _, existed := existingMap[key]; existed {
|
||||||
|
appendChange(&updated, key)
|
||||||
|
} else if _, hadOld := oldCfgMap[key]; hadOld {
|
||||||
|
appendChange(&updated, key)
|
||||||
|
} else {
|
||||||
|
appendChange(&added, key)
|
||||||
|
}
|
||||||
|
result = append(result, provider)
|
||||||
finalIDs[key] = struct{}{}
|
finalIDs[key] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inlineDone:
|
||||||
}
|
}
|
||||||
|
|
||||||
removedSet := make(map[string]struct{})
|
removedSet := make(map[string]struct{})
|
||||||
@@ -192,7 +176,7 @@ func accessProviderMap(cfg *config.Config) map[string]*sdkConfig.AccessProvider
|
|||||||
result[key] = providerCfg
|
result[key] = providerCfg
|
||||||
}
|
}
|
||||||
if len(result) == 0 && len(cfg.APIKeys) > 0 {
|
if len(result) == 0 && len(cfg.APIKeys) > 0 {
|
||||||
if provider := cfg.ConfigAPIKeyProvider(); provider != nil {
|
if provider := sdkConfig.MakeInlineAPIKeyProvider(cfg.APIKeys); provider != nil {
|
||||||
if key := providerIdentifier(provider); key != "" {
|
if key := providerIdentifier(provider); key != "" {
|
||||||
result[key] = provider
|
result[key] = provider
|
||||||
}
|
}
|
||||||
@@ -212,6 +196,11 @@ func collectProviderEntries(cfg *config.Config) []*sdkConfig.AccessProvider {
|
|||||||
entries = append(entries, providerCfg)
|
entries = append(entries, providerCfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(entries) == 0 && len(cfg.APIKeys) > 0 {
|
||||||
|
if inline := sdkConfig.MakeInlineAPIKeyProvider(cfg.APIKeys); inline != nil {
|
||||||
|
entries = append(entries, inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
sdkConfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generic helpers for list[string]
|
// Generic helpers for list[string]
|
||||||
@@ -108,13 +107,16 @@ func (h *Handler) deleteFromStringList(c *gin.Context, target *[]string, after f
|
|||||||
// api-keys
|
// api-keys
|
||||||
func (h *Handler) GetAPIKeys(c *gin.Context) { c.JSON(200, gin.H{"api-keys": h.cfg.APIKeys}) }
|
func (h *Handler) GetAPIKeys(c *gin.Context) { c.JSON(200, gin.H{"api-keys": h.cfg.APIKeys}) }
|
||||||
func (h *Handler) PutAPIKeys(c *gin.Context) {
|
func (h *Handler) PutAPIKeys(c *gin.Context) {
|
||||||
h.putStringList(c, func(v []string) { sdkConfig.SyncInlineAPIKeys(&h.cfg.SDKConfig, v) }, nil)
|
h.putStringList(c, func(v []string) {
|
||||||
|
h.cfg.APIKeys = append([]string(nil), v...)
|
||||||
|
h.cfg.Access.Providers = nil
|
||||||
|
}, nil)
|
||||||
}
|
}
|
||||||
func (h *Handler) PatchAPIKeys(c *gin.Context) {
|
func (h *Handler) PatchAPIKeys(c *gin.Context) {
|
||||||
h.patchStringList(c, &h.cfg.APIKeys, func() { sdkConfig.SyncInlineAPIKeys(&h.cfg.SDKConfig, h.cfg.APIKeys) })
|
h.patchStringList(c, &h.cfg.APIKeys, func() { h.cfg.Access.Providers = nil })
|
||||||
}
|
}
|
||||||
func (h *Handler) DeleteAPIKeys(c *gin.Context) {
|
func (h *Handler) DeleteAPIKeys(c *gin.Context) {
|
||||||
h.deleteFromStringList(c, &h.cfg.APIKeys, func() { sdkConfig.SyncInlineAPIKeys(&h.cfg.SDKConfig, h.cfg.APIKeys) })
|
h.deleteFromStringList(c, &h.cfg.APIKeys, func() { h.cfg.Access.Providers = nil })
|
||||||
}
|
}
|
||||||
|
|
||||||
// generative-language-api-key
|
// generative-language-api-key
|
||||||
@@ -203,7 +205,7 @@ func (h *Handler) DeleteClaudeKey(c *gin.Context) {
|
|||||||
|
|
||||||
// openai-compatibility: []OpenAICompatibility
|
// openai-compatibility: []OpenAICompatibility
|
||||||
func (h *Handler) GetOpenAICompat(c *gin.Context) {
|
func (h *Handler) GetOpenAICompat(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{"openai-compatibility": h.cfg.OpenAICompatibility})
|
c.JSON(200, gin.H{"openai-compatibility": normalizedOpenAICompatibilityEntries(h.cfg.OpenAICompatibility)})
|
||||||
}
|
}
|
||||||
func (h *Handler) PutOpenAICompat(c *gin.Context) {
|
func (h *Handler) PutOpenAICompat(c *gin.Context) {
|
||||||
data, err := c.GetRawData()
|
data, err := c.GetRawData()
|
||||||
@@ -381,3 +383,22 @@ func normalizeOpenAICompatibilityEntry(entry *config.OpenAICompatibility) {
|
|||||||
}
|
}
|
||||||
entry.APIKeys = nil
|
entry.APIKeys = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizedOpenAICompatibilityEntries(entries []config.OpenAICompatibility) []config.OpenAICompatibility {
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]config.OpenAICompatibility, len(entries))
|
||||||
|
for i := range entries {
|
||||||
|
copyEntry := entries[i]
|
||||||
|
if len(copyEntry.APIKeyEntries) > 0 {
|
||||||
|
copyEntry.APIKeyEntries = append([]config.OpenAICompatibilityAPIKey(nil), copyEntry.APIKeyEntries...)
|
||||||
|
}
|
||||||
|
if len(copyEntry.APIKeys) > 0 {
|
||||||
|
copyEntry.APIKeys = append([]string(nil), copyEntry.APIKeys...)
|
||||||
|
}
|
||||||
|
normalizeOpenAICompatibilityEntry(©Entry)
|
||||||
|
out[i] = copyEntry
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|||||||
@@ -217,33 +217,12 @@ func syncInlineAccessProvider(cfg *Config) {
|
|||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(cfg.Access.Providers) == 0 {
|
if len(cfg.APIKeys) == 0 {
|
||||||
if len(cfg.APIKeys) == 0 {
|
if provider := cfg.ConfigAPIKeyProvider(); provider != nil && len(provider.APIKeys) > 0 {
|
||||||
return
|
cfg.APIKeys = append([]string(nil), provider.APIKeys...)
|
||||||
}
|
}
|
||||||
cfg.Access.Providers = append(cfg.Access.Providers, config.AccessProvider{
|
|
||||||
Name: config.DefaultAccessProviderName,
|
|
||||||
Type: config.AccessProviderTypeConfigAPIKey,
|
|
||||||
APIKeys: append([]string(nil), cfg.APIKeys...),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
provider := cfg.ConfigAPIKeyProvider()
|
cfg.Access.Providers = nil
|
||||||
if provider == nil {
|
|
||||||
if len(cfg.APIKeys) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Access.Providers = append(cfg.Access.Providers, config.AccessProvider{
|
|
||||||
Name: config.DefaultAccessProviderName,
|
|
||||||
Type: config.AccessProviderTypeConfigAPIKey,
|
|
||||||
APIKeys: append([]string(nil), cfg.APIKeys...),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(provider.APIKeys) == 0 && len(cfg.APIKeys) > 0 {
|
|
||||||
provider.APIKeys = append([]string(nil), cfg.APIKeys...)
|
|
||||||
}
|
|
||||||
cfg.APIKeys = append([]string(nil), provider.APIKeys...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// looksLikeBcrypt returns true if the provided string appears to be a bcrypt hash.
|
// looksLikeBcrypt returns true if the provided string appears to be a bcrypt hash.
|
||||||
@@ -264,6 +243,7 @@ func hashSecret(secret string) (string, error) {
|
|||||||
// SaveConfigPreserveComments writes the config back to YAML while preserving existing comments
|
// SaveConfigPreserveComments writes the config back to YAML while preserving existing comments
|
||||||
// and key ordering by loading the original file into a yaml.Node tree and updating values in-place.
|
// and key ordering by loading the original file into a yaml.Node tree and updating values in-place.
|
||||||
func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
||||||
|
persistCfg := sanitizeConfigForPersist(cfg)
|
||||||
// Load original YAML as a node tree to preserve comments and ordering.
|
// Load original YAML as a node tree to preserve comments and ordering.
|
||||||
data, err := os.ReadFile(configFile)
|
data, err := os.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -282,7 +262,7 @@ func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Marshal the current cfg to YAML, then unmarshal to a yaml.Node we can merge from.
|
// Marshal the current cfg to YAML, then unmarshal to a yaml.Node we can merge from.
|
||||||
rendered, err := yaml.Marshal(cfg)
|
rendered, err := yaml.Marshal(persistCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -297,6 +277,9 @@ func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
|||||||
return fmt.Errorf("expected generated root mapping node")
|
return fmt.Errorf("expected generated root mapping node")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove deprecated auth block before merging to avoid persisting it again.
|
||||||
|
removeMapKey(original.Content[0], "auth")
|
||||||
|
|
||||||
// Merge generated into original in-place, preserving comments/order of existing nodes.
|
// Merge generated into original in-place, preserving comments/order of existing nodes.
|
||||||
mergeMappingPreserve(original.Content[0], generated.Content[0])
|
mergeMappingPreserve(original.Content[0], generated.Content[0])
|
||||||
|
|
||||||
@@ -315,6 +298,16 @@ func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
|||||||
return enc.Close()
|
return enc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeConfigForPersist(cfg *Config) *Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clone := *cfg
|
||||||
|
clone.SDKConfig = cfg.SDKConfig
|
||||||
|
clone.SDKConfig.Access = config.AccessConfig{}
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
// SaveConfigPreserveCommentsUpdateNestedScalar updates a nested scalar key path like ["a","b"]
|
// SaveConfigPreserveCommentsUpdateNestedScalar updates a nested scalar key path like ["a","b"]
|
||||||
// while preserving comments and positions.
|
// while preserving comments and positions.
|
||||||
func SaveConfigPreserveCommentsUpdateNestedScalar(configFile string, path []string, value string) error {
|
func SaveConfigPreserveCommentsUpdateNestedScalar(configFile string, path []string, value string) error {
|
||||||
@@ -517,3 +510,15 @@ func copyNodeShallow(dst, src *yaml.Node) {
|
|||||||
dst.Content = nil
|
dst.Content = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeMapKey(mapNode *yaml.Node, key string) {
|
||||||
|
if mapNode == nil || mapNode.Kind != yaml.MappingNode || key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i+1 < len(mapNode.Content); i += 2 {
|
||||||
|
if mapNode.Content[i] != nil && mapNode.Content[i].Value == key {
|
||||||
|
mapNode.Content = append(mapNode.Content[:i], mapNode.Content[i+2:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,10 +74,9 @@ func BuildProviders(root *config.SDKConfig) ([]Provider, error) {
|
|||||||
}
|
}
|
||||||
providers = append(providers, provider)
|
providers = append(providers, provider)
|
||||||
}
|
}
|
||||||
if len(providers) == 0 && len(root.APIKeys) > 0 {
|
if len(providers) == 0 {
|
||||||
config.SyncInlineAPIKeys(root, root.APIKeys)
|
if inline := config.MakeInlineAPIKeyProvider(root.APIKeys); inline != nil {
|
||||||
if providerCfg := root.ConfigAPIKeyProvider(); providerCfg != nil {
|
provider, err := BuildProvider(inline, root)
|
||||||
provider, err := BuildProvider(providerCfg, root)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ type SDKConfig struct {
|
|||||||
APIKeys []string `yaml:"api-keys" json:"api-keys"`
|
APIKeys []string `yaml:"api-keys" json:"api-keys"`
|
||||||
|
|
||||||
// Access holds request authentication provider configuration.
|
// Access holds request authentication provider configuration.
|
||||||
Access AccessConfig `yaml:"auth" json:"auth"`
|
Access AccessConfig `yaml:"auth,omitempty" json:"auth,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessConfig groups request authentication providers.
|
// AccessConfig groups request authentication providers.
|
||||||
type AccessConfig struct {
|
type AccessConfig struct {
|
||||||
// Providers lists configured authentication providers.
|
// Providers lists configured authentication providers.
|
||||||
Providers []AccessProvider `yaml:"providers" json:"providers"`
|
Providers []AccessProvider `yaml:"providers,omitempty" json:"providers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessProvider describes a request authentication provider entry.
|
// AccessProvider describes a request authentication provider entry.
|
||||||
@@ -51,27 +51,6 @@ const (
|
|||||||
DefaultAccessProviderName = "config-inline"
|
DefaultAccessProviderName = "config-inline"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncInlineAPIKeys updates the inline API key provider and top-level APIKeys field.
|
|
||||||
func SyncInlineAPIKeys(cfg *SDKConfig, keys []string) {
|
|
||||||
if cfg == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cloned := append([]string(nil), keys...)
|
|
||||||
cfg.APIKeys = cloned
|
|
||||||
if provider := cfg.ConfigAPIKeyProvider(); provider != nil {
|
|
||||||
if provider.Name == "" {
|
|
||||||
provider.Name = DefaultAccessProviderName
|
|
||||||
}
|
|
||||||
provider.APIKeys = cloned
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Access.Providers = append(cfg.Access.Providers, AccessProvider{
|
|
||||||
Name: DefaultAccessProviderName,
|
|
||||||
Type: AccessProviderTypeConfigAPIKey,
|
|
||||||
APIKeys: cloned,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigAPIKeyProvider returns the first inline API key provider if present.
|
// ConfigAPIKeyProvider returns the first inline API key provider if present.
|
||||||
func (c *SDKConfig) ConfigAPIKeyProvider() *AccessProvider {
|
func (c *SDKConfig) ConfigAPIKeyProvider() *AccessProvider {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
@@ -87,3 +66,17 @@ func (c *SDKConfig) ConfigAPIKeyProvider() *AccessProvider {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeInlineAPIKeyProvider constructs an inline API key provider configuration.
|
||||||
|
// It returns nil when no keys are supplied.
|
||||||
|
func MakeInlineAPIKeyProvider(keys []string) *AccessProvider {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
provider := &AccessProvider{
|
||||||
|
Name: DefaultAccessProviderName,
|
||||||
|
Type: AccessProviderTypeConfigAPIKey,
|
||||||
|
APIKeys: append([]string(nil), keys...),
|
||||||
|
}
|
||||||
|
return provider
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user