PC branch was calling TradePagePay (alipay.trade.page.pay), which
returns a gateway redirect URL — not a scannable QR payload. That URL
was being written to both PayURL and QRCode, so the frontend's qr_code
branch encoded the URL string into an image. Users scanning it got a
generic HTTP link instead of an Alipay order.
Switch PC to TradePreCreate (FACE_TO_FACE_PAYMENT): returns a native
qr_code string (qr.alipay.com/...) that renders as a real order QR.
Only fill QRCode, leave PayURL empty so the frontend takes the QR
popup branch. Surface empty-qr_code responses with sub_code/sub_msg
for debugging. H5 path (TradeWapPay) unchanged.
- safe/safe.go: pass context.Background() to slog.LogAttrs (SA1012)
- capture_fingerprint/main.go: wrap ln/raw Close() defers to swallow err
- capture_fingerprint/peek_conn.go, serve_h2.go: ignore bytes.Buffer.Write
return (cannot fail)
- verify_fingerprint/main.go: wrap resp.Body.Close() defer
These were pre-existing issues on release/custom-0.1.114 also failing
on v0.1.114.1. Fixing now so the current CI run turns green.
## Bug fixes
- EditAccountModal: auto-generated profiles (__auto__:acc-*) are no longer
hidden from the dropdown; accounts bound to their own auto profile can
now see and keep the selection.
- AccountResponse DTO: emit tls_fingerprint_randomized so the "randomized"
badge and reshuffle affordance render on subsequent edits.
## Feature
- TLS fingerprint profile list returns bound_account_count per profile,
aggregated via a single grouped SQL query over accounts.extra.
- Dropdowns and admin management table surface the binding count so admins
can judge whether a fingerprint is shared before editing or deleting it.
## Performance
- Migration 108 adds a partial + expression index on
(extra->>'tls_fingerprint_profile_id') WHERE extra ? '...',
enabling Index Only Scan + HashAggregate for the new query.
- Extraction uses (extra->>'...')::bigint so PostgreSQL performs the cast;
Go-side parsing and NullString plumbing are removed.
## Backend
- AccountRepository: add CountByTLSFingerprintProfile; implement in ent
repo via grouped raw SQL.
- TLSFingerprintProfileService: add ProfileWithBinding + ListWithBindingCount.
- Admin handler List now returns the enriched payload.
- AccountResponse DTO: add tls_fingerprint_randomized (optional, omitempty).
- Update all five AccountRepository test stubs for the new interface method
(account_service_delete_test, gateway_multiplatform_test,
gemini_multiplatform_test, ratelimit_session_window_test,
server/api_contract_test).
## Frontend
- TLSFingerprintProfile type: add optional bound_account_count.
- EditAccountModal + CreateAccountModal: show " (N)" suffix on options
with active bindings; drop the auto-profile filter.
- TLSFingerprintProfilesModal admin table: new "使用中 / In use" column
with amber-highlighted count.
- i18n zh/en: add columns.boundAccounts.
## Compatibility
- New fields are all optional (omitempty) — existing OAuth accounts and
older frontends behave as before.
- No data migration required; empty extra entries are ignored by index
and aggregation alike.
Chrome's password manager matched the apikey-type account's Base URL + API Key
inputs as a login form and autofilled the last saved password by domain, so
editing a Gemini account could overwrite its apikey with a Claude key that
shared the same Base URL. Add autocomplete="new-password" plus data-*-ignore
attributes for 1Password / LastPass / Bitwarden to opt the field out of every
major password manager's autofill.
Subscription-mode billing was consuming quota at TotalCost (raw) instead of
ActualCost (TotalCost * RateMultiplier), so per-group rate multipliers —
including free subscriptions (multiplier = 0) — were silently ignored.
Switch the three subscription cost writes in buildUsageBillingCommand,
finalizePostUsageBilling, and the legacy postUsageBilling fallback to
ActualCost, and add a table-driven test covering 2x / 0.5x / free multipliers
plus a balance-mode regression check.
Without TOTP_ENCRYPTION_KEY, saved payment configs were lost on restart because
the AES round-trip failed silently. Write new records as plaintext JSON; read
path tries JSON first, falls back to legacy AES decrypt when a key is present,
and treats unreadable values as empty so admins can re-enter them via the UI.
Each retry in the SetOutboxWatermark loop now gets its own 5s context.
Previously a shared context could already be expired when the second or
third attempt ran, making the retries pointless.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Fixes#1691
- pollOutbox() reused a 10s context for SetOutboxWatermark after event
processing could take much longer, causing "outbox watermark write
failed: context deadline exceeded". The watermark never advanced so
the same 200 events were reprocessed every poll cycle, spiking CPU.
Now uses an independent 5s context with up to 3 retries (200ms apart).
- When multiple Codex accounts sharing the same 21-22 groups are all
rate-limited in quick succession, each account_changed event triggered
redundant bucket rebuild attempts for the same groups. Introduce
batchSeenKey{groupID, platform} and thread a seen map through the
handler chain; rebuildBucketsForPlatform skips (group, platform) pairs
already rebuilt within the same poll batch (~80% fewer rebuild calls in
the 5-accounts-same-groups scenario).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Consolidates 9 call sites of resolveUpstreamResponseReadLimit + readUpstreamResponseBodyLimited + ErrUpstreamResponseBodyTooLarge error handling into a single ReadUpstreamResponseBody function with TooLargeWriter callback for API-format-specific error responses (Anthropic, OpenAI, countTokens).
Consolidates 9 call sites of resolveUpstreamResponseReadLimit + readUpstreamResponseBodyLimited + ErrUpstreamResponseBodyTooLarge error handling into a single ReadUpstreamResponseBody function with TooLargeWriter callback for API-format-specific error responses.