fix(openai): harden reset-credit refresh and account recovery

Review follow-ups on the reset-credit caching flow:

- Recover account state BEFORE (and independently of) the reset-credit
  display cache. A failed cache refresh could previously abort the run and
  leave the account rate-limited — the very reason the credit was spent
  (#3672 / #3740). The recovered account row is now returned even when the
  cache refresh fails.
- Run the post-reset bookkeeping on a detached, time-boxed context and give
  the panel reset call a larger timeout. A client abort no longer strands a
  consumed (non-refundable) credit with an unrecovered account, and the
  chained upstream calls can no longer exceed the client timeout and invite a
  retry that spends a second credit.
- Persist the reset-credit snapshot through POST /accounts/:id/quota/refresh
  instead of a side-effecting GET flag, so the write is covered by the audit
  middleware. A rejected snapshot write now degrades to cache_persisted=false
  instead of turning a successful upstream read into a 502 that left the card
  without a credit count and the reset button permanently disabled.
- Reject snapshots whose positive count carries no expiration entries, and
  drop expired credits (clamping the count) when rehydrating, so a stale
  cache can no longer light up the reset button.
- Keep nil quota / rate-limit services nil in the handler's interface fields;
  storing a nil *Service made the "not enabled" guards non-nil.
- Time-box the usage-refresh suppression and reuse handleAccountUpdated so the
  patched row also enters the auto-refresh silent window.
This commit is contained in:
shaw
2026-08-03 14:40:55 +08:00
parent f970bd48c9
commit 54a2bcfd15
14 changed files with 681 additions and 215 deletions
+4
View File
@@ -1070,6 +1070,10 @@ export interface Account {
upstream_billing_probe_enabled?: boolean
upstream_billing_rate_sync_enabled?: boolean
upstream_billing_probe?: UpstreamBillingProbeSnapshot
codex_reset_credit_snapshot?: {
available_count?: number
credits?: { expires_at?: string }[]
}
} & Record<string, unknown>)
proxy_id: number | null
proxy_fallback_origin_id?: number | null