mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-03-09 15:25:17 +00:00
- 在 BackgroundRefresher 中添加 onTokenRefreshed 回调函数和并发安全锁 - 实现 WithOnTokenRefreshed 选项函数用于设置刷新成功回调 - 在 RefreshManager 中添加 SetOnTokenRefreshed 方法支持运行时更新回调 - 为 KiroExecutor 添加 reloadAuthFromFile 方法实现文件重新加载回退机制 - 在 Watcher 中实现 NotifyTokenRefreshed 方法处理刷新通知并更新内存Auth对象 - 通过 Service.GetWatcher 连接刷新器回调到 Watcher 通知链路 - 添加方案A和方案B双重保障解决后台刷新与内存对象时间差问题
163 lines
5.8 KiB
Go
163 lines
5.8 KiB
Go
// Package cliproxy provides the core service implementation for the CLI Proxy API.
|
||
// It includes service lifecycle management, authentication handling, file watching,
|
||
// and integration with various AI service providers through a unified interface.
|
||
package cliproxy
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher"
|
||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||
)
|
||
|
||
// TokenClientProvider loads clients backed by stored authentication tokens.
|
||
// It provides an interface for loading authentication tokens from various sources
|
||
// and creating clients for AI service providers.
|
||
type TokenClientProvider interface {
|
||
// Load loads token-based clients from the configured source.
|
||
//
|
||
// Parameters:
|
||
// - ctx: The context for the loading operation
|
||
// - cfg: The application configuration
|
||
//
|
||
// Returns:
|
||
// - *TokenClientResult: The result containing loaded clients
|
||
// - error: An error if loading fails
|
||
Load(ctx context.Context, cfg *config.Config) (*TokenClientResult, error)
|
||
}
|
||
|
||
// TokenClientResult represents clients generated from persisted tokens.
|
||
// It contains metadata about the loading operation and the number of successful authentications.
|
||
type TokenClientResult struct {
|
||
// SuccessfulAuthed is the number of successfully authenticated clients.
|
||
SuccessfulAuthed int
|
||
}
|
||
|
||
// APIKeyClientProvider loads clients backed directly by configured API keys.
|
||
// It provides an interface for loading API key-based clients for various AI service providers.
|
||
type APIKeyClientProvider interface {
|
||
// Load loads API key-based clients from the configuration.
|
||
//
|
||
// Parameters:
|
||
// - ctx: The context for the loading operation
|
||
// - cfg: The application configuration
|
||
//
|
||
// Returns:
|
||
// - *APIKeyClientResult: The result containing loaded clients
|
||
// - error: An error if loading fails
|
||
Load(ctx context.Context, cfg *config.Config) (*APIKeyClientResult, error)
|
||
}
|
||
|
||
// APIKeyClientResult is returned by APIKeyClientProvider.Load()
|
||
type APIKeyClientResult struct {
|
||
// GeminiKeyCount is the number of Gemini API keys loaded
|
||
GeminiKeyCount int
|
||
|
||
// VertexCompatKeyCount is the number of Vertex-compatible API keys loaded
|
||
VertexCompatKeyCount int
|
||
|
||
// ClaudeKeyCount is the number of Claude API keys loaded
|
||
ClaudeKeyCount int
|
||
|
||
// CodexKeyCount is the number of Codex API keys loaded
|
||
CodexKeyCount int
|
||
|
||
// OpenAICompatCount is the number of OpenAI compatibility API keys loaded
|
||
OpenAICompatCount int
|
||
}
|
||
|
||
// WatcherFactory creates a watcher for configuration and token changes.
|
||
// The reload callback receives the updated configuration when changes are detected.
|
||
//
|
||
// Parameters:
|
||
// - configPath: The path to the configuration file to watch
|
||
// - authDir: The directory containing authentication tokens to watch
|
||
// - reload: The callback function to call when changes are detected
|
||
//
|
||
// Returns:
|
||
// - *WatcherWrapper: A watcher wrapper instance
|
||
// - error: An error if watcher creation fails
|
||
type WatcherFactory func(configPath, authDir string, reload func(*config.Config)) (*WatcherWrapper, error)
|
||
|
||
// WatcherWrapper exposes the subset of watcher methods required by the SDK.
|
||
type WatcherWrapper struct {
|
||
start func(ctx context.Context) error
|
||
stop func() error
|
||
|
||
setConfig func(cfg *config.Config)
|
||
snapshotAuths func() []*coreauth.Auth
|
||
setUpdateQueue func(queue chan<- watcher.AuthUpdate)
|
||
dispatchRuntimeUpdate func(update watcher.AuthUpdate) bool
|
||
notifyTokenRefreshed func(tokenID, accessToken, refreshToken, expiresAt string) // 方案 A: 后台刷新通知
|
||
}
|
||
|
||
// Start proxies to the underlying watcher Start implementation.
|
||
func (w *WatcherWrapper) Start(ctx context.Context) error {
|
||
if w == nil || w.start == nil {
|
||
return nil
|
||
}
|
||
return w.start(ctx)
|
||
}
|
||
|
||
// Stop proxies to the underlying watcher Stop implementation.
|
||
func (w *WatcherWrapper) Stop() error {
|
||
if w == nil || w.stop == nil {
|
||
return nil
|
||
}
|
||
return w.stop()
|
||
}
|
||
|
||
// SetConfig updates the watcher configuration cache.
|
||
func (w *WatcherWrapper) SetConfig(cfg *config.Config) {
|
||
if w == nil || w.setConfig == nil {
|
||
return
|
||
}
|
||
w.setConfig(cfg)
|
||
}
|
||
|
||
// DispatchRuntimeAuthUpdate forwards runtime auth updates (e.g., websocket providers)
|
||
// into the watcher-managed auth update queue when available.
|
||
// Returns true if the update was enqueued successfully.
|
||
func (w *WatcherWrapper) DispatchRuntimeAuthUpdate(update watcher.AuthUpdate) bool {
|
||
if w == nil || w.dispatchRuntimeUpdate == nil {
|
||
return false
|
||
}
|
||
return w.dispatchRuntimeUpdate(update)
|
||
}
|
||
|
||
// SetClients updates the watcher file-backed clients registry.
|
||
// SetClients and SetAPIKeyClients removed; watcher manages its own caches
|
||
|
||
// SnapshotClients returns the current combined clients snapshot from the underlying watcher.
|
||
// SnapshotClients removed; use SnapshotAuths
|
||
|
||
// SnapshotAuths returns the current auth entries derived from legacy clients.
|
||
func (w *WatcherWrapper) SnapshotAuths() []*coreauth.Auth {
|
||
if w == nil || w.snapshotAuths == nil {
|
||
return nil
|
||
}
|
||
return w.snapshotAuths()
|
||
}
|
||
|
||
// SetAuthUpdateQueue registers the channel used to propagate auth updates.
|
||
func (w *WatcherWrapper) SetAuthUpdateQueue(queue chan<- watcher.AuthUpdate) {
|
||
if w == nil || w.setUpdateQueue == nil {
|
||
return
|
||
}
|
||
w.setUpdateQueue(queue)
|
||
}
|
||
|
||
// NotifyTokenRefreshed 通知 Watcher 后台刷新器已更新 token
|
||
// 这是方案 A 的核心方法,用于解决后台刷新与内存 Auth 对象的时间差问题
|
||
// tokenID: token 文件名(如 kiro-xxx.json)
|
||
// accessToken: 新的 access token
|
||
// refreshToken: 新的 refresh token
|
||
// expiresAt: 新的过期时间(RFC3339 格式)
|
||
func (w *WatcherWrapper) NotifyTokenRefreshed(tokenID, accessToken, refreshToken, expiresAt string) {
|
||
if w == nil || w.notifyTokenRefreshed == nil {
|
||
return
|
||
}
|
||
w.notifyTokenRefreshed(tokenID, accessToken, refreshToken, expiresAt)
|
||
}
|