mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 04:10:51 +08:00
Refactor codebase
This commit is contained in:
@@ -11,9 +11,17 @@ import (
|
||||
// It analyzes the model name string to identify which service provider it belongs to.
|
||||
//
|
||||
// Supported providers:
|
||||
// - "gemini" for Google's Gemini models
|
||||
// - "gpt" for OpenAI's GPT models
|
||||
// - "unknow" for unrecognized model names
|
||||
// - "gemini" for Google's Gemini models
|
||||
// - "gpt" for OpenAI's GPT models
|
||||
// - "claude" for Anthropic's Claude models
|
||||
// - "qwen" for Alibaba's Qwen models
|
||||
// - "unknow" for unrecognized model names
|
||||
//
|
||||
// Parameters:
|
||||
// - modelName: The name of the model to identify the provider for.
|
||||
//
|
||||
// Returns:
|
||||
// - string: The name of the provider.
|
||||
func GetProviderName(modelName string) string {
|
||||
if strings.Contains(modelName, "gemini") {
|
||||
return "gemini"
|
||||
@@ -28,3 +36,40 @@ func GetProviderName(modelName string) string {
|
||||
}
|
||||
return "unknow"
|
||||
}
|
||||
|
||||
// InArray checks if a string exists in a slice of strings.
|
||||
// It iterates through the slice and returns true if the target string is found,
|
||||
// otherwise it returns false.
|
||||
//
|
||||
// Parameters:
|
||||
// - hystack: The slice of strings to search in
|
||||
// - needle: The string to search for
|
||||
//
|
||||
// Returns:
|
||||
// - bool: True if the string is found, false otherwise
|
||||
func InArray(hystack []string, needle string) bool {
|
||||
for _, item := range hystack {
|
||||
if needle == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HideAPIKey obscures an API key for logging purposes, showing only the first and last few characters.
|
||||
//
|
||||
// Parameters:
|
||||
// - apiKey: The API key to hide.
|
||||
//
|
||||
// Returns:
|
||||
// - string: The obscured API key.
|
||||
func HideAPIKey(apiKey string) string {
|
||||
if len(apiKey) > 8 {
|
||||
return apiKey[:4] + "..." + apiKey[len(apiKey)-4:]
|
||||
} else if len(apiKey) > 4 {
|
||||
return apiKey[:2] + "..." + apiKey[len(apiKey)-2:]
|
||||
} else if len(apiKey) > 2 {
|
||||
return apiKey[:1] + "..." + apiKey[len(apiKey)-1:]
|
||||
}
|
||||
return apiKey
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ import (
|
||||
// to route requests through the configured proxy server.
|
||||
func SetProxy(cfg *config.Config, httpClient *http.Client) *http.Client {
|
||||
var transport *http.Transport
|
||||
// Attempt to parse the proxy URL from the configuration.
|
||||
proxyURL, errParse := url.Parse(cfg.ProxyURL)
|
||||
if errParse == nil {
|
||||
// Handle different proxy schemes.
|
||||
if proxyURL.Scheme == "socks5" {
|
||||
// Configure SOCKS5 proxy with optional authentication.
|
||||
username := proxyURL.User.Username()
|
||||
password, _ := proxyURL.User.Password()
|
||||
proxyAuth := &proxy.Auth{User: username, Password: password}
|
||||
@@ -30,15 +33,18 @@ func SetProxy(cfg *config.Config, httpClient *http.Client) *http.Client {
|
||||
log.Errorf("create SOCKS5 dialer failed: %v", errSOCKS5)
|
||||
return httpClient
|
||||
}
|
||||
// Set up a custom transport using the SOCKS5 dialer.
|
||||
transport = &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.Dial(network, addr)
|
||||
},
|
||||
}
|
||||
} else if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" {
|
||||
// Configure HTTP or HTTPS proxy.
|
||||
transport = &http.Transport{Proxy: http.ProxyURL(proxyURL)}
|
||||
}
|
||||
}
|
||||
// If a new transport was created, apply it to the HTTP client.
|
||||
if transport != nil {
|
||||
httpClient.Transport = transport
|
||||
}
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
// Package util provides utility functions for the CLI Proxy API server.
|
||||
// It includes helper functions for JSON manipulation, proxy configuration,
|
||||
// and other common operations used across the application.
|
||||
package util
|
||||
|
||||
import "github.com/tidwall/gjson"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
// Walk recursively traverses a JSON structure to find all occurrences of a specific field.
|
||||
// It builds paths to each occurrence and adds them to the provided paths slice.
|
||||
//
|
||||
// Parameters:
|
||||
// - value: The gjson.Result object to traverse
|
||||
// - path: The current path in the JSON structure (empty string for root)
|
||||
// - field: The field name to search for
|
||||
// - paths: Pointer to a slice where found paths will be stored
|
||||
//
|
||||
// The function works recursively, building dot-notation paths to each occurrence
|
||||
// of the specified field throughout the JSON structure.
|
||||
func Walk(value gjson.Result, path, field string, paths *[]string) {
|
||||
switch value.Type {
|
||||
case gjson.JSON:
|
||||
// For JSON objects and arrays, iterate through each child
|
||||
value.ForEach(func(key, val gjson.Result) bool {
|
||||
var childPath string
|
||||
if path == "" {
|
||||
@@ -19,5 +40,175 @@ func Walk(value gjson.Result, path, field string, paths *[]string) {
|
||||
return true
|
||||
})
|
||||
case gjson.String, gjson.Number, gjson.True, gjson.False, gjson.Null:
|
||||
// Terminal types - no further traversal needed
|
||||
}
|
||||
}
|
||||
|
||||
// RenameKey renames a key in a JSON string by moving its value to a new key path
|
||||
// and then deleting the old key path.
|
||||
//
|
||||
// Parameters:
|
||||
// - jsonStr: The JSON string to modify
|
||||
// - oldKeyPath: The dot-notation path to the key that should be renamed
|
||||
// - newKeyPath: The dot-notation path where the value should be moved to
|
||||
//
|
||||
// Returns:
|
||||
// - string: The modified JSON string with the key renamed
|
||||
// - error: An error if the operation fails
|
||||
//
|
||||
// The function performs the rename in two steps:
|
||||
// 1. Sets the value at the new key path
|
||||
// 2. Deletes the old key path
|
||||
func RenameKey(jsonStr, oldKeyPath, newKeyPath string) (string, error) {
|
||||
value := gjson.Get(jsonStr, oldKeyPath)
|
||||
|
||||
if !value.Exists() {
|
||||
return "", fmt.Errorf("old key '%s' does not exist", oldKeyPath)
|
||||
}
|
||||
|
||||
interimJson, err := sjson.SetRaw(jsonStr, newKeyPath, value.Raw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to set new key '%s': %w", newKeyPath, err)
|
||||
}
|
||||
|
||||
finalJson, err := sjson.Delete(interimJson, oldKeyPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to delete old key '%s': %w", oldKeyPath, err)
|
||||
}
|
||||
|
||||
return finalJson, nil
|
||||
}
|
||||
|
||||
// FixJSON converts non-standard JSON that uses single quotes for strings into
|
||||
// RFC 8259-compliant JSON by converting those single-quoted strings to
|
||||
// double-quoted strings with proper escaping.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// {'a': 1, 'b': '2'} => {"a": 1, "b": "2"}
|
||||
// {"t": 'He said "hi"'} => {"t": "He said \"hi\""}
|
||||
//
|
||||
// Rules:
|
||||
// - Existing double-quoted JSON strings are preserved as-is.
|
||||
// - Single-quoted strings are converted to double-quoted strings.
|
||||
// - Inside converted strings, any double quote is escaped (\").
|
||||
// - Common backslash escapes (\n, \r, \t, \b, \f, \\) are preserved.
|
||||
// - \' inside single-quoted strings becomes a literal ' in the output (no
|
||||
// escaping needed inside double quotes).
|
||||
// - Unicode escapes (\uXXXX) inside single-quoted strings are forwarded.
|
||||
// - The function does not attempt to fix other non-JSON features beyond quotes.
|
||||
func FixJSON(input string) string {
|
||||
var out bytes.Buffer
|
||||
|
||||
inDouble := false
|
||||
inSingle := false
|
||||
escaped := false // applies within the current string state
|
||||
|
||||
// Helper to write a rune, escaping double quotes when inside a converted
|
||||
// single-quoted string (which becomes a double-quoted string in output).
|
||||
writeConverted := func(r rune) {
|
||||
if r == '"' {
|
||||
out.WriteByte('\\')
|
||||
out.WriteByte('"')
|
||||
return
|
||||
}
|
||||
out.WriteRune(r)
|
||||
}
|
||||
|
||||
runes := []rune(input)
|
||||
for i := 0; i < len(runes); i++ {
|
||||
r := runes[i]
|
||||
|
||||
if inDouble {
|
||||
out.WriteRune(r)
|
||||
if escaped {
|
||||
// end of escape sequence in a standard JSON string
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
if r == '\\' {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if r == '"' {
|
||||
inDouble = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if inSingle {
|
||||
if escaped {
|
||||
// Handle common escape sequences after a backslash within a
|
||||
// single-quoted string
|
||||
escaped = false
|
||||
switch r {
|
||||
case 'n', 'r', 't', 'b', 'f', '/', '"':
|
||||
// Keep the backslash and the character (except for '"' which
|
||||
// rarely appears, but if it does, keep as \" to remain valid)
|
||||
out.WriteByte('\\')
|
||||
out.WriteRune(r)
|
||||
case '\\':
|
||||
out.WriteByte('\\')
|
||||
out.WriteByte('\\')
|
||||
case '\'':
|
||||
// \' inside single-quoted becomes a literal '
|
||||
out.WriteRune('\'')
|
||||
case 'u':
|
||||
// Forward \uXXXX if possible
|
||||
out.WriteByte('\\')
|
||||
out.WriteByte('u')
|
||||
// Copy up to next 4 hex digits if present
|
||||
for k := 0; k < 4 && i+1 < len(runes); k++ {
|
||||
peek := runes[i+1]
|
||||
// simple hex check
|
||||
if (peek >= '0' && peek <= '9') || (peek >= 'a' && peek <= 'f') || (peek >= 'A' && peek <= 'F') {
|
||||
out.WriteRune(peek)
|
||||
i++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Unknown escape: preserve the backslash and the char
|
||||
out.WriteByte('\\')
|
||||
out.WriteRune(r)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '\\' { // start escape sequence
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if r == '\'' { // end of single-quoted string
|
||||
out.WriteByte('"')
|
||||
inSingle = false
|
||||
continue
|
||||
}
|
||||
// regular char inside converted string; escape double quotes
|
||||
writeConverted(r)
|
||||
continue
|
||||
}
|
||||
|
||||
// Outside any string
|
||||
if r == '"' {
|
||||
inDouble = true
|
||||
out.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
if r == '\'' { // start of non-standard single-quoted string
|
||||
inSingle = true
|
||||
out.WriteByte('"')
|
||||
continue
|
||||
}
|
||||
out.WriteRune(r)
|
||||
}
|
||||
|
||||
// If input ended while still inside a single-quoted string, close it to
|
||||
// produce the best-effort valid JSON.
|
||||
if inSingle {
|
||||
out.WriteByte('"')
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user