mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
refactor(auth): Centralize logging for saving credentials
The logic for logging the path where credentials are saved was duplicated across several client implementations. This commit refactors this behavior by creating a new centralized function, `misc.LogSavingCredentials`, to handle this logging. The `SaveTokenToFile` method in each authentication token storage struct now calls this new function, ensuring consistent logging and reducing code duplication. The redundant logging statements in the client-level `SaveTokenToFile` methods have been removed.
This commit is contained in:
@@ -8,6 +8,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClaudeTokenStorage stores OAuth2 token information for Anthropic Claude API authentication.
|
// ClaudeTokenStorage stores OAuth2 token information for Anthropic Claude API authentication.
|
||||||
@@ -46,6 +48,7 @@ type ClaudeTokenStorage struct {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - error: An error if the operation fails, nil otherwise
|
// - error: An error if the operation fails, nil otherwise
|
||||||
func (ts *ClaudeTokenStorage) SaveTokenToFile(authFilePath string) error {
|
func (ts *ClaudeTokenStorage) SaveTokenToFile(authFilePath string) error {
|
||||||
|
misc.LogSavingCredentials(authFilePath)
|
||||||
ts.Type = "claude"
|
ts.Type = "claude"
|
||||||
|
|
||||||
// Create directory structure if it doesn't exist
|
// Create directory structure if it doesn't exist
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CodexTokenStorage stores OAuth2 token information for OpenAI Codex API authentication.
|
// CodexTokenStorage stores OAuth2 token information for OpenAI Codex API authentication.
|
||||||
@@ -42,6 +44,7 @@ type CodexTokenStorage struct {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - error: An error if the operation fails, nil otherwise
|
// - error: An error if the operation fails, nil otherwise
|
||||||
func (ts *CodexTokenStorage) SaveTokenToFile(authFilePath string) error {
|
func (ts *CodexTokenStorage) SaveTokenToFile(authFilePath string) error {
|
||||||
|
misc.LogSavingCredentials(authFilePath)
|
||||||
ts.Type = "codex"
|
ts.Type = "codex"
|
||||||
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
||||||
return fmt.Errorf("failed to create directory: %v", err)
|
return fmt.Errorf("failed to create directory: %v", err)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ type GeminiWebTokenStorage struct {
|
|||||||
|
|
||||||
// SaveTokenToFile serializes the Gemini Web token storage to a JSON file.
|
// SaveTokenToFile serializes the Gemini Web token storage to a JSON file.
|
||||||
func (ts *GeminiWebTokenStorage) SaveTokenToFile(authFilePath string) error {
|
func (ts *GeminiWebTokenStorage) SaveTokenToFile(authFilePath string) error {
|
||||||
|
misc.LogSavingCredentials(authFilePath)
|
||||||
ts.Type = "gemini-web"
|
ts.Type = "gemini-web"
|
||||||
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
||||||
return fmt.Errorf("failed to create directory: %v", err)
|
return fmt.Errorf("failed to create directory: %v", err)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ type GeminiTokenStorage struct {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - error: An error if the operation fails, nil otherwise
|
// - error: An error if the operation fails, nil otherwise
|
||||||
func (ts *GeminiTokenStorage) SaveTokenToFile(authFilePath string) error {
|
func (ts *GeminiTokenStorage) SaveTokenToFile(authFilePath string) error {
|
||||||
|
misc.LogSavingCredentials(authFilePath)
|
||||||
ts.Type = "gemini"
|
ts.Type = "gemini"
|
||||||
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
||||||
return fmt.Errorf("failed to create directory: %v", err)
|
return fmt.Errorf("failed to create directory: %v", err)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QwenTokenStorage stores OAuth2 token information for Alibaba Qwen API authentication.
|
// QwenTokenStorage stores OAuth2 token information for Alibaba Qwen API authentication.
|
||||||
@@ -40,6 +42,7 @@ type QwenTokenStorage struct {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - error: An error if the operation fails, nil otherwise
|
// - error: An error if the operation fails, nil otherwise
|
||||||
func (ts *QwenTokenStorage) SaveTokenToFile(authFilePath string) error {
|
func (ts *QwenTokenStorage) SaveTokenToFile(authFilePath string) error {
|
||||||
|
misc.LogSavingCredentials(authFilePath)
|
||||||
ts.Type = "qwen"
|
ts.Type = "qwen"
|
||||||
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
||||||
return fmt.Errorf("failed to create directory: %v", err)
|
return fmt.Errorf("failed to create directory: %v", err)
|
||||||
|
|||||||
@@ -831,7 +831,6 @@ func (c *GeminiCLIClient) GetProjectList(ctx context.Context) (*interfaces.GCPPr
|
|||||||
// - error: An error if the save operation fails, nil otherwise.
|
// - error: An error if the save operation fails, nil otherwise.
|
||||||
func (c *GeminiCLIClient) SaveTokenToFile() error {
|
func (c *GeminiCLIClient) SaveTokenToFile() error {
|
||||||
fileName := filepath.Join(c.cfg.AuthDir, fmt.Sprintf("%s-%s.json", c.tokenStorage.(*geminiAuth.GeminiTokenStorage).Email, c.tokenStorage.(*geminiAuth.GeminiTokenStorage).ProjectID))
|
fileName := filepath.Join(c.cfg.AuthDir, fmt.Sprintf("%s-%s.json", c.tokenStorage.(*geminiAuth.GeminiTokenStorage).Email, c.tokenStorage.(*geminiAuth.GeminiTokenStorage).ProjectID))
|
||||||
log.Infof("Saving credentials to %s", fileName)
|
|
||||||
return c.tokenStorage.SaveTokenToFile(fileName)
|
return c.tokenStorage.SaveTokenToFile(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -842,7 +842,6 @@ func (c *GeminiWebClient) SaveTokenToFile() error {
|
|||||||
}
|
}
|
||||||
return ts.SaveTokenToFile(c.tokenFilePath)
|
return ts.SaveTokenToFile(c.tokenFilePath)
|
||||||
}
|
}
|
||||||
log.Debugf("Saving Gemini Web cookie snapshot to %s", filepath.Base(util.CookieSnapshotPath(c.tokenFilePath)))
|
|
||||||
return c.snapshotManager.Persist()
|
return c.snapshotManager.Persist()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
internal/misc/credentials.go
Normal file
16
internal/misc/credentials.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package misc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogSavingCredentials emits a consistent log message when persisting auth material.
|
||||||
|
func LogSavingCredentials(path string) {
|
||||||
|
if path == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Use filepath.Clean so logs remain stable even if callers pass redundant separators.
|
||||||
|
log.Infof("Saving credentials to %s", filepath.Clean(path))
|
||||||
|
}
|
||||||
@@ -6,15 +6,19 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const cookieSnapshotExt = ".cookie"
|
||||||
|
|
||||||
// CookieSnapshotPath derives the cookie snapshot file path from the main token JSON path.
|
// CookieSnapshotPath derives the cookie snapshot file path from the main token JSON path.
|
||||||
// It replaces the .json suffix with .cookies, or appends .cookies if missing.
|
// It replaces the .json suffix with .cookie, or appends .cookie if missing.
|
||||||
func CookieSnapshotPath(mainPath string) string {
|
func CookieSnapshotPath(mainPath string) string {
|
||||||
if strings.HasSuffix(mainPath, ".json") {
|
if strings.HasSuffix(mainPath, ".json") {
|
||||||
return strings.TrimSuffix(mainPath, ".json") + ".cookies"
|
return strings.TrimSuffix(mainPath, ".json") + cookieSnapshotExt
|
||||||
}
|
}
|
||||||
return mainPath + ".cookies"
|
return mainPath + cookieSnapshotExt
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRegularFile reports whether the given path exists and is a regular file.
|
// IsRegularFile reports whether the given path exists and is a regular file.
|
||||||
@@ -66,9 +70,8 @@ func RemoveFile(path string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryReadCookieSnapshotInto tries to read a cookie snapshot into v.
|
// TryReadCookieSnapshotInto tries to read a cookie snapshot into v using the .cookie suffix.
|
||||||
// It attempts the .cookies suffix; returns (true, nil) when found and decoded,
|
// Returns (true, nil) when a snapshot was decoded, or (false, nil) when none exists.
|
||||||
// or (false, nil) when none exists.
|
|
||||||
func TryReadCookieSnapshotInto(mainPath string, v any) (bool, error) {
|
func TryReadCookieSnapshotInto(mainPath string, v any) (bool, error) {
|
||||||
snap := CookieSnapshotPath(mainPath)
|
snap := CookieSnapshotPath(mainPath)
|
||||||
if err := ReadJSON(snap, v); err != nil {
|
if err := ReadJSON(snap, v); err != nil {
|
||||||
@@ -80,13 +83,20 @@ func TryReadCookieSnapshotInto(mainPath string, v any) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteCookieSnapshot writes v to the snapshot path derived from mainPath using the .cookies suffix.
|
// WriteCookieSnapshot writes v to the snapshot path derived from mainPath using the .cookie suffix.
|
||||||
func WriteCookieSnapshot(mainPath string, v any) error {
|
func WriteCookieSnapshot(mainPath string, v any) error {
|
||||||
return WriteJSON(CookieSnapshotPath(mainPath), v)
|
path := CookieSnapshotPath(mainPath)
|
||||||
|
misc.LogSavingCredentials(path)
|
||||||
|
if err := WriteJSON(path, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveCookieSnapshots removes both modern and legacy snapshot files.
|
// RemoveCookieSnapshots removes the snapshot file if it exists.
|
||||||
func RemoveCookieSnapshots(mainPath string) { _ = RemoveFile(CookieSnapshotPath(mainPath)) }
|
func RemoveCookieSnapshots(mainPath string) {
|
||||||
|
_ = RemoveFile(CookieSnapshotPath(mainPath))
|
||||||
|
}
|
||||||
|
|
||||||
// Hooks provide customization points for snapshot lifecycle operations.
|
// Hooks provide customization points for snapshot lifecycle operations.
|
||||||
type Hooks[T any] struct {
|
type Hooks[T any] struct {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -141,7 +142,7 @@ func (w *Watcher) handleEvent(event fsnotify.Event) {
|
|||||||
isConfigEvent := event.Name == w.configPath && (event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create)
|
isConfigEvent := event.Name == w.configPath && (event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create)
|
||||||
isAuthJSON := strings.HasPrefix(event.Name, w.authDir) && strings.HasSuffix(event.Name, ".json")
|
isAuthJSON := strings.HasPrefix(event.Name, w.authDir) && strings.HasSuffix(event.Name, ".json")
|
||||||
if !isConfigEvent && !isAuthJSON {
|
if !isConfigEvent && !isAuthJSON {
|
||||||
// Ignore unrelated files (e.g., cookie snapshots *.cookies) and other noise.
|
// Ignore unrelated files (e.g., cookie snapshots *.cookie) and other noise.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,18 +418,40 @@ func (w *Watcher) clientsToSlice(clientMap map[string]interfaces.Client) []inter
|
|||||||
// readAuthFileWithRetry attempts to read the auth file multiple times to work around
|
// readAuthFileWithRetry attempts to read the auth file multiple times to work around
|
||||||
// short-lived locks on Windows while token files are being written.
|
// short-lived locks on Windows while token files are being written.
|
||||||
func readAuthFileWithRetry(path string, attempts int, delay time.Duration) ([]byte, error) {
|
func readAuthFileWithRetry(path string, attempts int, delay time.Duration) ([]byte, error) {
|
||||||
var lastErr error
|
read := func(target string) ([]byte, error) {
|
||||||
for i := 0; i < attempts; i++ {
|
var lastErr error
|
||||||
data, err := os.ReadFile(path)
|
for i := 0; i < attempts; i++ {
|
||||||
|
data, err := os.ReadFile(target)
|
||||||
|
if err == nil {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
lastErr = err
|
||||||
|
if i < attempts-1 {
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := []string{
|
||||||
|
util.CookieSnapshotPath(path),
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, candidate := range candidates {
|
||||||
|
data, err := read(candidate)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
lastErr = err
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
if i < attempts-1 {
|
if idx < len(candidates)-1 {
|
||||||
time.Sleep(delay)
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, lastErr
|
|
||||||
|
return nil, os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// addOrUpdateClient handles the addition or update of a single client.
|
// addOrUpdateClient handles the addition or update of a single client.
|
||||||
|
|||||||
Reference in New Issue
Block a user