Replace the 4-file service-layer cleanup with the upstream PR's simpler
transaction-level DELETE in accountRepository.Delete.
Bump version to 0.1.114.13.
Accounts use soft-delete (setting deleted_at), so PostgreSQL's
ON DELETE CASCADE on scheduled_test_plans.account_id never fires.
This leaves orphaned plans that continue executing and cannot be
managed from the frontend since the account no longer exists.
ClosesWei-Shaw/sub2api#1728
Introduces the pool-replace retry strategy end-to-end. When
RectifierSettings has SignaturePoolSize>0 and the account's per-type
sub-switch is on, the factory returns a PoolClaudeRectifier /
PoolAntigravityRectifier that fetches the freshest signatures from the
Redis pool and cycles them through the request's thinking blocks
(M>N cycling). Rule A: an empty pool transparently passes the
original upstream error back — no fallback to strip.
Key pieces:
- PoolClaudeRectifier / PoolAntigravityRectifier in internal/service/signature
- signatureRectifierFactory in the service package selects strategy per
request via shouldUsePool(ctx, account) which implements the agreed
decision table (Enabled + SignaturePoolSize>0 + per-type sub-switch)
- WrapResponseBody helper on the factory is invoked at each Claude-native
DoWithTLS entry point (main Forward, Anthropic passthrough, Bedrock) to
run the Harvester over the upstream body; no-op when pool disabled
- ctxkey.IsSignatureRectifyRetry is set on all retry contexts (Claude
two-stage, count_tokens, Antigravity signature retries) so the harvester
skips ingesting signatures from retry responses and does not pollute the
pool with values we ourselves injected
- NewGatewayService / NewAntigravityGatewayService now accept
signature.SignaturePool; wire_gen.go updated accordingly
Antigravity responses are raw Gemini format so WrapResponseBody is not
called there — harvesting stays Claude-only as designed. Antigravity can
still *read* from the shared OAuth pool on retry; whether cross-ecosystem
signatures verify upstream is the question the future PoC will answer.
Alipay's page is ~1200x900; a fixed 1250x780 still clipped vertically.
Replace the constant with getPaymentPopupFeatures(): it prefers
1250x900, clamps to window.screen.avail*, and centers the popup so
it never overflows the visible work area on smaller laptops.
Drop POPUP_WINDOW_FEATURES and STRIPE_POPUP_WINDOW_FEATURES in favor of
the helper — all five call sites migrated.
Bump version to 0.1.114.9
Alipay's standard checkout (QR + account login panel) needs ~1200px
width. The default popup was 1000x750, forcing a horizontal scrollbar.
Unify to the wider size that the Stripe-alipay popup already used.
Bump version to 0.1.114.8
Previous pattern-based isSensitiveConfigField treated any key containing
"key" as a secret and stripped it from the admin response, which wrongly
hid Stripe's publishableKey (a public value). Edit dialogs then failed
its required-field validation because the backend no longer returned it.
Replace the pattern matcher with an explicit per-provider registry that
mirrors the frontend PROVIDER_CONFIG_FIELDS, so only true secrets are
masked/preserved:
- alipay: privateKey, publicKey, alipayPublicKey
- wxpay: privateKey, apiV3Key, publicKey
- stripe: secretKey, webhookSecret (publishableKey stays plaintext)
- easypay: pkey
Bump version to 0.1.114.7
- Strip sensitive provider config keys (privateKey/publicKey/apiV3Key/
secretKey/webhookSecret/pkey/password/etc.) from admin list/detail API
- mergeConfig: empty value for a sensitive key preserves the stored secret
- Admin dialog: sensitive inputs add autocomplete="new-password" +
data-*ignore + spellcheck="false" to block browser password managers;
edit-mode placeholder shows "leave empty to keep"; skip required
validation for sensitive fields when editing
Bump version to 0.1.114.6
## 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.
wire_gen.go was calling NewSchedulerCache(redisClient) but wire.go had
been updated to register ProvideSchedulerCache(redisClient, config),
which reads SnapshotMGetChunkSize and SnapshotWriteChunkSize from config.
Without this fix, those config values were silently ignored.
Bug fixes:
- Detached context for GetAccountConcurrencyBatch (prevent all-zero on request cancel)
- Filter soft-deleted users in GetByGroupID
- Stripe CSP policy (allow Stripe.js in script-src and frame-src)
- WebSearch API key validation on save
- RECHARGING status in payment result success check
- Windows test fixes (logger Sync deadlock, config path escaping)
Feature enhancements:
- Webhook multi-instance dispatch (extractOutTradeNo + GetWebhookProvider)
- EasyPay mobile H5 payment (device param + PayURL2)
- SSE error propagation in WebSearch emulation
- AccountStatsCost DTO field for admin usage logs
- Plans sort by sort_order instead of created_at
- UsageMapHook for streaming response usage data
- apicompat Instructions field passthrough
- EffectiveLoadFactor for ops concurrency/metrics
- Usage billing RETURNING balance for notify system
- BulkUpdate mixed channel warning with details
- println to slog migration in auth cache
- Wire ProviderSet cleanup
- CI cache-dependency-path optimization
Frontend:
- Refund eligibility check per provider (canRequestRefund)
- Plan sort_order editing
- Dead code cleanup (simulate_claude_max, client_affinity)
- GroupsView platform switch guard
- channels features_config API type
- UsageView account_stats_cost export
Checkout page aggregates Stripe sub-types (card,link,alipay,wxpay) under
"stripe", but SelectInstance matched against supported_types literally,
which doesn't contain "stripe". Now matches by provider_key for Stripe.
- QuotaLimit changed to *int64 (null=unlimited, >0=limited)
- Add reset-usage endpoint (POST /admin/settings/web-search-emulation/reset-usage)
- Show quota usage in header always (collapsed and expanded)
- Add reset quota button in expanded provider view
- Quota input: empty=unlimited with ∞ placeholder, must be >0 if set
- Add email verification hint on balance notify card
The buttons were hidden because v-if only checked provider.api_key,
which is always empty for saved providers (backend sanitizes it).
Now also checks api_key_configured. Copy button is disabled when
no actual key is available (only configured placeholder shown).
- EditAccountModal width changed from "normal" to "wide" (match CreateAccountModal)
- CreateAccountModal now passes all quota notify props to QuotaLimitCard
- QuotaLimitCard: when global notify disabled, hide title row, input takes full width
- Quota alert email: show remaining quota + threshold (fixed/$, percentage/%) instead of usage trigger point