Ambiguous idempotency-store failures can occur after the account transaction commits. Scope durable recovery markers to the authenticated admin, retain the operation key across reloads, and recover only an already committed copy without rerunning active work.
Constraint: Generic idempotent handlers may legitimately remain active while a recovery lookup is attempted
Rejected: Reclaim or rerun an in-progress duplicate request | can execute account creation concurrently
Rejected: Recover by source account and key alone | allows another admin to observe the committed copy
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep ambiguous-response recovery read-only and bind durable operation markers to the authenticated actor
Tested: Full Go unit suite, go vet, server build, integration-test compilation; frontend lint, typecheck, 1,030 Vitest tests, and production build
Not-tested: Docker-backed PostgreSQL integration runtime because Docker is unavailable
Related: Wei-Shaw/sub2api#1379
Related: Wei-Shaw/sub2api#2928
Admins often need another account with the same provider and routing configuration. Duplicate on the server so credentials never return to the browser, preserve exact group priorities atomically, start the copy paused, and recover the same copy after ambiguous idempotency-store failures.
Constraint: Admin account responses redact credentials, so duplication must remain server-side
Constraint: OAuth and setup-token credentials rotate and must not be shared across account rows
Rejected: Copy raw account JSON to the clipboard | exposes credentials outside the server
Rejected: Duplicate rotating credentials | account-scoped refresh locks can race token rotation
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep copies paused, avoid automatic upstream probes, and exclude rotating credential types unless token ownership is redesigned
Tested: Targeted Go tests, Go vet, server build; frontend lint, typecheck, Vitest suite, production build; integration test compiled
Not-tested: Docker-backed PostgreSQL execution because Docker is unavailable
Related: Wei-Shaw/sub2api#1379
Related: Wei-Shaw/sub2api#2928
Members of the same ChatGPT team share chatgpt_account_id, so matching
imports by the account key first could overwrite another member's
account credentials. Identity keys are now ordered by strength
(user > email > access > account), and an account-key hit is rejected
when both sides carry different chatgpt_user_id values.
- Keep the account-key fallback when either side lacks a user id, so
legacy accounts without chatgpt_user_id are updated and backfilled
instead of duplicated
- Index all candidate accounts per shared key so a teammate's row can
no longer shadow a legacy account depending on row order
- Apply the same conflict check to in-batch dedup and emit a warning
when a legacy account is claimed via the shared account key
- Scope a 120s timeout to the Codex session import request instead of
raising the global client timeout
The risk control center's moderation log records had no field for the
keyword that triggered a keyword block, so the admin UI couldn't show
which keyword was hit (only the application slog logged it).
- migration 156: add matched_keyword column to content_moderation_logs
- ContentModerationLog gains MatchedKeyword; set it on keyword block
- repo CreateLog/ListLogs persist and read the column
- frontend: show "命中关键词" inline in the log table and detail modal
- i18n: add matchedKeyword (zh/en)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Route direct fetch calls, setup requests, gateway usage checks, and ops WebSocket connections through the configured API base instead of assuming same-origin backend paths.
Also derive OAuth callback suggestions from the configured API base so split frontend/API deployments show usable redirect URLs.
Adds an admin-side action that mirrors the Codex Desktop "rate-limit reset"
flow against chatgpt.com upstream for OpenAI OAuth accounts.
Backend
- OpenAIQuotaService.QueryUsage / ResetCredit hit /wham/usage and
/wham/rate-limit-reset-credits/consume with the Codex Desktop header set,
reusing OpenAITokenProvider for refreshed tokens and PrivacyClientFactory
for the impersonated Chrome TLS fingerprint.
- Honors the account's configured proxy by reading the eager-loaded
account.Proxy directly (falls back to proxyRepo only when missing).
- GET /api/v1/admin/openai/accounts/:id/quota
POST /api/v1/admin/openai/accounts/:id/reset-quota
- Wire DI for the new service + handler dependency.
Frontend
- OpenAIQuotaResetCell renders a single action row in AccountUsageCell's
OpenAI section: the existing local "查询" (active sampling) is injected
via #pre-actions, alongside a "次数 N" button that doubles as the
upstream query trigger and the available-credit indicator, and a "重置"
button that consumes one credit.
- No duplicate 5h/7d window display; the local UsageProgressBar owns those
bars to avoid confusion.