refactor(auth): Introduce generic cookie snapshot manager

This commit introduces a generic `cookies.Manager` to centralize the logic for handling cookie snapshots, which was previously duplicated across the Gemini and PaLM clients. This refactoring eliminates code duplication and improves maintainability.

The new `cookies.Manager[T]` in `internal/auth/cookies` orchestrates the lifecycle of cookie data between a temporary snapshot file and the main token file. It provides `Apply`, `Persist`, and `Flush` methods to manage this process.

Key changes:
- A generic `Manager` is created in `internal/auth/cookies`, usable for any token storage type.
- A `Hooks` struct allows for customizable behavior, such as custom merging strategies for different token types.
- Duplicated snapshot handling code has been removed from the `gemini-web` and `palm` persistence packages.
- The `GeminiWebClient` and `PaLMClient` have been updated to use the new `cookies.Manager`.
- The `auth_gemini` and `auth_palm` CLI commands now leverage the client's `Flush` method, simplifying the command logic.
- Cookie snapshot utility functions have been moved from `internal/util/files.go` to a new `internal/util/cookies.go` for better organization.
This commit is contained in:
hkfires
2025-09-18 20:06:14 +08:00
parent 7632204966
commit 56b2dabcca
4 changed files with 228 additions and 146 deletions

View File

@@ -9,9 +9,6 @@ import (
"path/filepath"
"strings"
"time"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
)
// StoredMessage represents a single message in a conversation record.
@@ -268,67 +265,3 @@ func FindReusableSessionIn(items map[string]ConversationRecord, index map[string
}
return nil, nil
}
// ApplyCookieSnapshotToTokenStorage loads cookies from cookie snapshot into the provided token storage.
// Returns true when a snapshot was found and applied.
func ApplyCookieSnapshotToTokenStorage(tokenFilePath string, ts *gemini.GeminiWebTokenStorage) (bool, error) {
if ts == nil {
return false, nil
}
var latest gemini.GeminiWebTokenStorage
if ok, err := util.TryReadCookieSnapshotInto(tokenFilePath, &latest); err != nil {
return false, err
} else if !ok {
return false, nil
}
if latest.Secure1PSID != "" {
ts.Secure1PSID = latest.Secure1PSID
}
if latest.Secure1PSIDTS != "" {
ts.Secure1PSIDTS = latest.Secure1PSIDTS
}
return true, nil
}
// SaveCookieSnapshot writes the current cookies into a snapshot file next to the token file.
// This keeps the main token JSON stable until an orderly flush.
func SaveCookieSnapshot(tokenFilePath string, cookies map[string]string) error {
ts := &gemini.GeminiWebTokenStorage{Type: "gemini-web"}
if v := cookies["__Secure-1PSID"]; v != "" {
ts.Secure1PSID = v
}
if v := cookies["__Secure-1PSIDTS"]; v != "" {
ts.Secure1PSIDTS = v
}
return util.WriteCookieSnapshot(tokenFilePath, ts)
}
// FlushCookieSnapshotToMain merges the cookie snapshot into the main token file and removes the snapshot.
// If snapshot is missing, it will combine the provided base token storage with the latest cookies.
func FlushCookieSnapshotToMain(tokenFilePath string, cookies map[string]string, base *gemini.GeminiWebTokenStorage) error {
if tokenFilePath == "" {
return nil
}
var merged gemini.GeminiWebTokenStorage
var fromSnapshot bool
if ok, _ := util.TryReadCookieSnapshotInto(tokenFilePath, &merged); ok {
fromSnapshot = true
}
if !fromSnapshot {
if base != nil {
merged = *base
}
if v := cookies["__Secure-1PSID"]; v != "" {
merged.Secure1PSID = v
}
if v := cookies["__Secure-1PSIDTS"]; v != "" {
merged.Secure1PSIDTS = v
}
}
merged.Type = "gemini-web"
if err := merged.SaveTokenToFile(tokenFilePath); err != nil {
return err
}
util.RemoveCookieSnapshots(tokenFilePath)
return nil
}