mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
fix(config): dedupe and normalize Gemini keys and headers
This commit is contained in:
@@ -12,7 +12,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) GetConfig(c *gin.Context) {
|
func (h *Handler) GetConfig(c *gin.Context) {
|
||||||
c.JSON(200, h.cfg)
|
if h == nil || h.cfg == nil {
|
||||||
|
c.JSON(200, gin.H{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfgCopy := *h.cfg
|
||||||
|
cfgCopy.GlAPIKey = geminiKeyStringsFromConfig(h.cfg)
|
||||||
|
c.JSON(200, &cfgCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) GetConfigYAML(c *gin.Context) {
|
func (h *Handler) GetConfigYAML(c *gin.Context) {
|
||||||
|
|||||||
@@ -87,10 +87,10 @@ func (h *Handler) deleteFromStringList(c *gin.Context, target *[]string, after f
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if val := c.Query("value"); val != "" {
|
if val := strings.TrimSpace(c.Query("value")); val != "" {
|
||||||
out := make([]string, 0, len(*target))
|
out := make([]string, 0, len(*target))
|
||||||
for _, v := range *target {
|
for _, v := range *target {
|
||||||
if v != val {
|
if strings.TrimSpace(v) != val {
|
||||||
out = append(out, v)
|
out = append(out, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,6 +104,53 @@ func (h *Handler) deleteFromStringList(c *gin.Context, target *[]string, after f
|
|||||||
c.JSON(400, gin.H{"error": "missing index or value"})
|
c.JSON(400, gin.H{"error": "missing index or value"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeStringSlice(in []string) []string {
|
||||||
|
out := make([]string, 0, len(in))
|
||||||
|
for i := range in {
|
||||||
|
if trimmed := strings.TrimSpace(in[i]); trimmed != "" {
|
||||||
|
out = append(out, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func geminiKeyStringsFromConfig(cfg *config.Config) []string {
|
||||||
|
if cfg == nil || len(cfg.GeminiKey) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]string, 0, len(cfg.GeminiKey))
|
||||||
|
for i := range cfg.GeminiKey {
|
||||||
|
if key := strings.TrimSpace(cfg.GeminiKey[i].APIKey); key != "" {
|
||||||
|
out = append(out, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) applyLegacyKeys(keys []string) {
|
||||||
|
if h == nil || h.cfg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sanitized := sanitizeStringSlice(keys)
|
||||||
|
existing := make(map[string]config.GeminiKey, len(h.cfg.GeminiKey))
|
||||||
|
for _, entry := range h.cfg.GeminiKey {
|
||||||
|
if key := strings.TrimSpace(entry.APIKey); key != "" {
|
||||||
|
existing[key] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newList := make([]config.GeminiKey, 0, len(sanitized))
|
||||||
|
for _, key := range sanitized {
|
||||||
|
if entry, ok := existing[key]; ok {
|
||||||
|
newList = append(newList, entry)
|
||||||
|
} else {
|
||||||
|
newList = append(newList, config.GeminiKey{APIKey: key})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.cfg.GeminiKey = newList
|
||||||
|
h.cfg.GlAPIKey = sanitized
|
||||||
|
h.cfg.SyncGeminiKeys()
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -121,20 +168,20 @@ func (h *Handler) DeleteAPIKeys(c *gin.Context) {
|
|||||||
|
|
||||||
// generative-language-api-key
|
// generative-language-api-key
|
||||||
func (h *Handler) GetGlKeys(c *gin.Context) {
|
func (h *Handler) GetGlKeys(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{"generative-language-api-key": h.cfg.GlAPIKey})
|
c.JSON(200, gin.H{"generative-language-api-key": geminiKeyStringsFromConfig(h.cfg)})
|
||||||
}
|
}
|
||||||
func (h *Handler) PutGlKeys(c *gin.Context) {
|
func (h *Handler) PutGlKeys(c *gin.Context) {
|
||||||
h.putStringList(c, func(v []string) {
|
h.putStringList(c, func(v []string) {
|
||||||
h.cfg.GlAPIKey = append([]string(nil), v...)
|
h.applyLegacyKeys(v)
|
||||||
}, func() {
|
}, nil)
|
||||||
h.cfg.SyncGeminiKeys()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
func (h *Handler) PatchGlKeys(c *gin.Context) {
|
func (h *Handler) PatchGlKeys(c *gin.Context) {
|
||||||
h.patchStringList(c, &h.cfg.GlAPIKey, func() { h.cfg.SyncGeminiKeys() })
|
target := append([]string(nil), geminiKeyStringsFromConfig(h.cfg)...)
|
||||||
|
h.patchStringList(c, &target, func() { h.applyLegacyKeys(target) })
|
||||||
}
|
}
|
||||||
func (h *Handler) DeleteGlKeys(c *gin.Context) {
|
func (h *Handler) DeleteGlKeys(c *gin.Context) {
|
||||||
h.deleteFromStringList(c, &h.cfg.GlAPIKey, func() { h.cfg.SyncGeminiKeys() })
|
target := append([]string(nil), geminiKeyStringsFromConfig(h.cfg)...)
|
||||||
|
h.deleteFromStringList(c, &target, func() { h.applyLegacyKeys(target) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// gemini-api-key: []GeminiKey
|
// gemini-api-key: []GeminiKey
|
||||||
|
|||||||
@@ -303,56 +303,40 @@ func (cfg *Config) SyncGeminiKeys() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.GeminiKey) > 0 {
|
seen := make(map[string]struct{}, len(cfg.GeminiKey))
|
||||||
out := make([]GeminiKey, 0, len(cfg.GeminiKey))
|
out := cfg.GeminiKey[:0]
|
||||||
for i := range cfg.GeminiKey {
|
for i := range cfg.GeminiKey {
|
||||||
entry := cfg.GeminiKey[i]
|
entry := cfg.GeminiKey[i]
|
||||||
entry.APIKey = strings.TrimSpace(entry.APIKey)
|
entry.APIKey = strings.TrimSpace(entry.APIKey)
|
||||||
entry.BaseURL = strings.TrimSpace(entry.BaseURL)
|
if entry.APIKey == "" {
|
||||||
entry.ProxyURL = strings.TrimSpace(entry.ProxyURL)
|
continue
|
||||||
if entry.APIKey == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(entry.Headers) > 0 {
|
|
||||||
clean := make(map[string]string, len(entry.Headers))
|
|
||||||
for hk, hv := range entry.Headers {
|
|
||||||
key := strings.TrimSpace(hk)
|
|
||||||
val := strings.TrimSpace(hv)
|
|
||||||
if key == "" || val == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
clean[key] = val
|
|
||||||
}
|
|
||||||
if len(clean) == 0 {
|
|
||||||
entry.Headers = nil
|
|
||||||
} else {
|
|
||||||
entry.Headers = clean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out = append(out, entry)
|
|
||||||
}
|
}
|
||||||
cfg.GeminiKey = out
|
entry.BaseURL = strings.TrimSpace(entry.BaseURL)
|
||||||
|
entry.ProxyURL = strings.TrimSpace(entry.ProxyURL)
|
||||||
|
entry.Headers = normalizeGeminiHeaders(entry.Headers)
|
||||||
|
if _, exists := seen[entry.APIKey]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[entry.APIKey] = struct{}{}
|
||||||
|
out = append(out, entry)
|
||||||
}
|
}
|
||||||
|
cfg.GeminiKey = out
|
||||||
|
|
||||||
if len(cfg.GeminiKey) == 0 && len(cfg.GlAPIKey) > 0 {
|
if len(cfg.GlAPIKey) > 0 {
|
||||||
out := make([]GeminiKey, 0, len(cfg.GlAPIKey))
|
for _, raw := range cfg.GlAPIKey {
|
||||||
for i := range cfg.GlAPIKey {
|
key := strings.TrimSpace(raw)
|
||||||
key := strings.TrimSpace(cfg.GlAPIKey[i])
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, GeminiKey{APIKey: key})
|
if _, exists := seen[key]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg.GeminiKey = append(cfg.GeminiKey, GeminiKey{APIKey: key})
|
||||||
|
seen[key] = struct{}{}
|
||||||
}
|
}
|
||||||
cfg.GeminiKey = out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.GlAPIKey = cfg.GlAPIKey[:0]
|
cfg.GlAPIKey = nil
|
||||||
if len(cfg.GeminiKey) > 0 {
|
|
||||||
cfg.GlAPIKey = make([]string, 0, len(cfg.GeminiKey))
|
|
||||||
for i := range cfg.GeminiKey {
|
|
||||||
cfg.GlAPIKey = append(cfg.GlAPIKey, cfg.GeminiKey[i].APIKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncInlineAccessProvider(cfg *Config) {
|
func syncInlineAccessProvider(cfg *Config) {
|
||||||
@@ -372,6 +356,25 @@ func looksLikeBcrypt(s string) bool {
|
|||||||
return len(s) > 4 && (s[:4] == "$2a$" || s[:4] == "$2b$" || s[:4] == "$2y$")
|
return len(s) > 4 && (s[:4] == "$2a$" || s[:4] == "$2b$" || s[:4] == "$2y$")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeGeminiHeaders(headers map[string]string) map[string]string {
|
||||||
|
if len(headers) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clean := make(map[string]string, len(headers))
|
||||||
|
for k, v := range headers {
|
||||||
|
key := strings.TrimSpace(k)
|
||||||
|
val := strings.TrimSpace(v)
|
||||||
|
if key == "" || val == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clean[key] = val
|
||||||
|
}
|
||||||
|
if len(clean) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return clean
|
||||||
|
}
|
||||||
|
|
||||||
// hashSecret hashes the given secret using bcrypt.
|
// hashSecret hashes the given secret using bcrypt.
|
||||||
func hashSecret(secret string) (string, error) {
|
func hashSecret(secret string) (string, error) {
|
||||||
// Use default cost for simplicity.
|
// Use default cost for simplicity.
|
||||||
|
|||||||
Reference in New Issue
Block a user