mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-03 06:52:13 +08:00
Introduce OAuthRefreshAPI as the single entry point for all OAuth token refresh operations, eliminating the race condition where background refresh and inline refresh could simultaneously use the same refresh_token (fixes #1035). Key changes: - Add OAuthRefreshExecutor interface extending TokenRefresher with CacheKey - Add OAuthRefreshAPI.RefreshIfNeeded with lock → DB re-read → double-check flow - Add ProviderRefreshPolicy / BackgroundRefreshPolicy strategy types - Simplify all 4 TokenProviders to delegate to OAuthRefreshAPI - Rewrite TokenRefreshService.refreshWithRetry to use unified API path - Add MergeCredentials and BuildClaudeAccountCredentials helpers - Add 40 unit tests covering all new and modified code paths
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
package service
|
||
|
||
import "time"
|
||
|
||
// ProviderRefreshErrorAction 定义 provider 在刷新失败时的处理动作。
|
||
type ProviderRefreshErrorAction int
|
||
|
||
const (
|
||
// ProviderRefreshErrorReturn 失败即返回错误(不降级旧 token)。
|
||
ProviderRefreshErrorReturn ProviderRefreshErrorAction = iota
|
||
// ProviderRefreshErrorUseExistingToken 失败后继续使用现有 token。
|
||
ProviderRefreshErrorUseExistingToken
|
||
)
|
||
|
||
// ProviderLockHeldAction 定义 provider 在刷新锁被占用时的处理动作。
|
||
type ProviderLockHeldAction int
|
||
|
||
const (
|
||
// ProviderLockHeldUseExistingToken 直接使用现有 token。
|
||
ProviderLockHeldUseExistingToken ProviderLockHeldAction = iota
|
||
// ProviderLockHeldWaitForCache 等待后重试缓存读取。
|
||
ProviderLockHeldWaitForCache
|
||
)
|
||
|
||
// ProviderRefreshPolicy 描述 provider 的平台差异策略。
|
||
type ProviderRefreshPolicy struct {
|
||
OnRefreshError ProviderRefreshErrorAction
|
||
OnLockHeld ProviderLockHeldAction
|
||
FailureTTL time.Duration
|
||
}
|
||
|
||
func ClaudeProviderRefreshPolicy() ProviderRefreshPolicy {
|
||
return ProviderRefreshPolicy{
|
||
OnRefreshError: ProviderRefreshErrorUseExistingToken,
|
||
OnLockHeld: ProviderLockHeldWaitForCache,
|
||
FailureTTL: time.Minute,
|
||
}
|
||
}
|
||
|
||
func OpenAIProviderRefreshPolicy() ProviderRefreshPolicy {
|
||
return ProviderRefreshPolicy{
|
||
OnRefreshError: ProviderRefreshErrorUseExistingToken,
|
||
OnLockHeld: ProviderLockHeldWaitForCache,
|
||
FailureTTL: time.Minute,
|
||
}
|
||
}
|
||
|
||
func GeminiProviderRefreshPolicy() ProviderRefreshPolicy {
|
||
return ProviderRefreshPolicy{
|
||
OnRefreshError: ProviderRefreshErrorReturn,
|
||
OnLockHeld: ProviderLockHeldUseExistingToken,
|
||
FailureTTL: 0,
|
||
}
|
||
}
|
||
|
||
func AntigravityProviderRefreshPolicy() ProviderRefreshPolicy {
|
||
return ProviderRefreshPolicy{
|
||
OnRefreshError: ProviderRefreshErrorReturn,
|
||
OnLockHeld: ProviderLockHeldUseExistingToken,
|
||
FailureTTL: 0,
|
||
}
|
||
}
|
||
|
||
// BackgroundSkipAction 定义后台刷新服务在“未实际刷新”场景的计数方式。
|
||
type BackgroundSkipAction int
|
||
|
||
const (
|
||
// BackgroundSkipAsSkipped 计入 skipped(保持当前默认行为)。
|
||
BackgroundSkipAsSkipped BackgroundSkipAction = iota
|
||
// BackgroundSkipAsSuccess 计入 success(仅用于兼容旧统计口径时可选)。
|
||
BackgroundSkipAsSuccess
|
||
)
|
||
|
||
// BackgroundRefreshPolicy 描述后台刷新服务的调用侧策略。
|
||
type BackgroundRefreshPolicy struct {
|
||
OnLockHeld BackgroundSkipAction
|
||
OnAlreadyRefresh BackgroundSkipAction
|
||
}
|
||
|
||
func DefaultBackgroundRefreshPolicy() BackgroundRefreshPolicy {
|
||
return BackgroundRefreshPolicy{
|
||
OnLockHeld: BackgroundSkipAsSkipped,
|
||
OnAlreadyRefresh: BackgroundSkipAsSkipped,
|
||
}
|
||
}
|
||
|
||
func (p BackgroundRefreshPolicy) handleLockHeld() error {
|
||
if p.OnLockHeld == BackgroundSkipAsSuccess {
|
||
return nil
|
||
}
|
||
return errRefreshSkipped
|
||
}
|
||
|
||
func (p BackgroundRefreshPolicy) handleAlreadyRefreshed() error {
|
||
if p.OnAlreadyRefresh == BackgroundSkipAsSuccess {
|
||
return nil
|
||
}
|
||
return errRefreshSkipped
|
||
}
|