fix(accounts): fix OpenAI OAuth usage quota never refreshing on manual refresh
The refresh button had no effect because two independent gates blocked the Codex probe: 1. isOpenAICodexSnapshotStale returned false for non-WS-v2 accounts even when data was stale — this is correct for background auto-refresh (Codex quota is only auto-tracked for Codex CLI / WS v2 accounts), so this behavior is preserved. 2. shouldProbeOpenAICodexSnapshot had a 10-min in-memory rate limit with no bypass for explicit user requests. Fix: add a force parameter threaded from handler → GetUsage → getOpenAIUsage → shouldProbeOpenAICodexSnapshot. When force=true (manual refresh button), both gates are bypassed and the probe always runs. Background auto-loads continue to respect the original WS v2 logic and 10-min rate limit.
This commit is contained in:
parent
6db02f0069
commit
41e7ae534c
@ -1643,7 +1643,7 @@ func (h *OAuthHandler) SetupTokenCookieAuth(c *gin.Context) {
|
||||
}
|
||||
|
||||
// GetUsage handles getting account usage information
|
||||
// GET /api/v1/admin/accounts/:id/usage?source=passive|active
|
||||
// GET /api/v1/admin/accounts/:id/usage?source=passive|active&force=true
|
||||
func (h *AccountHandler) GetUsage(c *gin.Context) {
|
||||
accountID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
@ -1652,12 +1652,13 @@ func (h *AccountHandler) GetUsage(c *gin.Context) {
|
||||
}
|
||||
|
||||
source := c.DefaultQuery("source", "active")
|
||||
force := c.Query("force") == "true"
|
||||
|
||||
var usage *service.UsageInfo
|
||||
if source == "passive" {
|
||||
usage, err = h.accountUsageService.GetPassiveUsage(c.Request.Context(), accountID)
|
||||
} else {
|
||||
usage, err = h.accountUsageService.GetUsage(c.Request.Context(), accountID)
|
||||
usage, err = h.accountUsageService.GetUsage(c.Request.Context(), accountID, force)
|
||||
}
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
|
||||
@ -295,14 +295,16 @@ func NewAccountUsageService(
|
||||
// OAuth账号: 调用Anthropic API获取真实数据(需要profile scope),API响应缓存10分钟,窗口统计缓存1分钟
|
||||
// Setup Token账号: 根据session_window推算5h窗口,7d数据不可用(没有profile scope)
|
||||
// API Key账号: 不支持usage查询
|
||||
func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64) (*UsageInfo, error) {
|
||||
func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64, force ...bool) (*UsageInfo, error) {
|
||||
forceProbe := len(force) > 0 && force[0]
|
||||
|
||||
account, err := s.accountRepo.GetByID(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get account failed: %w", err)
|
||||
}
|
||||
|
||||
if account.Platform == PlatformOpenAI && account.Type == AccountTypeOAuth {
|
||||
usage, err := s.getOpenAIUsage(ctx, account)
|
||||
usage, err := s.getOpenAIUsage(ctx, account, forceProbe)
|
||||
if err == nil {
|
||||
s.tryClearRecoverableAccountError(ctx, account)
|
||||
}
|
||||
@ -492,7 +494,7 @@ func (s *AccountUsageService) syncActiveToPassive(ctx context.Context, accountID
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AccountUsageService) getOpenAIUsage(ctx context.Context, account *Account) (*UsageInfo, error) {
|
||||
func (s *AccountUsageService) getOpenAIUsage(ctx context.Context, account *Account, force bool) (*UsageInfo, error) {
|
||||
now := time.Now()
|
||||
usage := &UsageInfo{UpdatedAt: &now}
|
||||
|
||||
@ -507,7 +509,7 @@ func (s *AccountUsageService) getOpenAIUsage(ctx context.Context, account *Accou
|
||||
usage.SevenDay = progress
|
||||
}
|
||||
|
||||
if shouldRefreshOpenAICodexSnapshot(account, usage, now) && s.shouldProbeOpenAICodexSnapshot(account.ID, now) {
|
||||
if (force || shouldRefreshOpenAICodexSnapshot(account, usage, now)) && s.shouldProbeOpenAICodexSnapshot(account.ID, now, force) {
|
||||
if updates, err := s.probeOpenAICodexSnapshot(ctx, account); err == nil && len(updates) > 0 {
|
||||
mergeAccountExtra(account, updates)
|
||||
if usage.UpdatedAt == nil {
|
||||
@ -577,13 +579,16 @@ func isOpenAICodexSnapshotStale(account *Account, now time.Time) bool {
|
||||
return now.Sub(ts) >= openAIProbeCacheTTL
|
||||
}
|
||||
|
||||
func (s *AccountUsageService) shouldProbeOpenAICodexSnapshot(accountID int64, now time.Time) bool {
|
||||
func (s *AccountUsageService) shouldProbeOpenAICodexSnapshot(accountID int64, now time.Time, force ...bool) bool {
|
||||
if s == nil || s.cache == nil || accountID <= 0 {
|
||||
return true
|
||||
}
|
||||
if cached, ok := s.cache.openAIProbeCache.Load(accountID); ok {
|
||||
if ts, ok := cached.(time.Time); ok && now.Sub(ts) < openAIProbeCacheTTL {
|
||||
return false
|
||||
forceProbe := len(force) > 0 && force[0]
|
||||
if !forceProbe {
|
||||
if cached, ok := s.cache.openAIProbeCache.Load(accountID); ok {
|
||||
if ts, ok := cached.(time.Time); ok && now.Sub(ts) < openAIProbeCacheTTL {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
s.cache.openAIProbeCache.Store(accountID, now)
|
||||
|
||||
@ -140,7 +140,7 @@ func TestAccountUsageService_GetOpenAIUsage_DoesNotPromoteCodexExtraToRateLimit(
|
||||
},
|
||||
}
|
||||
|
||||
usage, err := svc.getOpenAIUsage(context.Background(), account)
|
||||
usage, err := svc.getOpenAIUsage(context.Background(), account, false)
|
||||
if err != nil {
|
||||
t.Fatalf("getOpenAIUsage() error = %v", err)
|
||||
}
|
||||
|
||||
@ -232,9 +232,12 @@ export async function clearError(id: number): Promise<Account> {
|
||||
* @param id - Account ID
|
||||
* @returns Account usage info
|
||||
*/
|
||||
export async function getUsage(id: number, source?: 'passive' | 'active'): Promise<AccountUsageInfo> {
|
||||
export async function getUsage(id: number, source?: 'passive' | 'active', force?: boolean): Promise<AccountUsageInfo> {
|
||||
const params: Record<string, string> = {}
|
||||
if (source) params.source = source
|
||||
if (force) params.force = 'true'
|
||||
const { data } = await apiClient.get<AccountUsageInfo>(`/admin/accounts/${id}/usage`, {
|
||||
params: source ? { source } : undefined
|
||||
params: Object.keys(params).length > 0 ? params : undefined
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user