Commit Graph
3965 Commits
Author SHA1 Message Date
erio 60bc062b6c fix(payment): use alipay.trade.precreate for PC QR code
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.
2026-04-18 23:15:58 +08:00
erio 5794dc96ef fix(lint): resolve 6 golangci-lint issues blocking CI
- 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.
2026-04-18 21:55:29 +08:00
erio cfd9566905 feat(tls-fingerprint): show binding count + fix randomized fingerprint visibility (v0.1.114.2)
## 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.
2026-04-18 21:45:17 +08:00
erio 6d0e056244 feat(fingerprint): Claude Code CLI fingerprint mimicry suite (v0.1.114.1)
Squash-merge feat/fingerprint-mimic into release/custom-0.1.114.

Functional changes:
- TLS ClientHello mimicry: 17→52 cipher suites, ALPN http/1.1 only,
  X25519MLKEM768, JA3/JA4 aligned with official CLI baseline
- HTTP layer: User-Agent 2.1.112 sdk-cli, 3 new anthropic-beta tokens,
  Stainless headers synced
- Sidecar traffic: usage poll + count_tokens injection + startup probe
  (three fire-and-forget channels with jittered intervals)
- Sticky session UUID: per-account metadata.user_id reuse via Redis,
  WithSessionHash propagation through gateway
- 429 no-switch: short-circuit on rate-limit instead of cross-account fail-over
- Admin UI: per-account "randomize fingerprint" button + ConfirmDialog +
  i18n (zh/en, 9 keys)

Engineering hardening (fixes carried over from hai/snapshot):
- safe.Go/Run package: panic-recovering goroutine helper with slog
- redis.Nil treated as cache miss in identity cache
- SOCKS5 dialer uses ContextDialer for ctx cancellation propagation
- Token refresh selects on stopCh during retry backoff
- Sidecar goroutines wrapped with safe.Go
- .gitattributes forces LF for *.json (fixes baseline file CRLF drift)

Refactoring (zero behavior change):
- sidecar probe constants centralized
- identity_service rewrite preflight extracted into helpers
- capture_fingerprint tool split from 894 lines into 9 files
- gofmt sweep on all hai-imported files

Tests:
- safe package: panic recovery + attrs propagation
- tlsfingerprint: SOCKS5 dialer + capture parity
- identity sticky-session preflight paths (early-return + cache-hit)
- sidecar probe: jittered interval + dry-run
- token refresh: graceful shutdown during backoff

Beta-validated on 0.1.112.21 (commit 565da163 of feat/fingerprint-mimic).
Bumps VERSION 0.1.112.20 -> 0.1.114.1.
2026-04-18 16:07:36 +08:00
erio e89f879533 chore(payment): mark legacy AES ciphertext fallback as deprecated
明文 JSON 已经是新写入的默认格式;保留 AES 密文读取仅为兼容迁移期间的旧
记录,一旦所有部署通过管理后台重存过一次即可删除。标记为 deprecated 并加
TODO,几个版本后统一清理掉:payment.Encrypt / payment.Decrypt、两处
decryptConfig 的 AES 分支、PaymentConfigService.encryptionKey 和
DefaultLoadBalancer.encryptionKey 字段。
2026-04-17 23:32:51 +08:00
erio 2e360412ce Merge tag 'v0.1.114' into release/custom-0.1.114
支持 opus-4.7 模型,修复 outbox watermark 上下文过期导致 CPU 飙升的问题。

- 支持 opus-4.7 模型
- OpenAI API Key 账号调用 Claude Code 时注入 prompt_cache_key,提升缓存命中率

- 重构上游响应体读取逻辑,合并 9 处重复代码为统一的 ReadUpstreamResponseBody 函数

- 修复 outbox watermark 上下文过期导致水位线无法推进、相同事件反复处理引发 CPU 飙升的问题
- 修复 watermark 写入重试时复用已过期上下文的问题
- 新增同批次内 group rebuild 去重,减少约 80% 的冗余 rebuild 调用
- 上游返回 KYC 身份验证要求时停止账号调度
- 修复 OpenAI WS 标志在调度器缓存中丢失的问题
- 修复账号 UI 中连接池 WS 模式选项未显示的问题

# Conflicts:
#	backend/cmd/server/VERSION
#	backend/internal/server/api_contract_test.go
#	backend/internal/service/auth_service_register_test.go
#	backend/internal/service/billing_service_unified_test.go
#	backend/internal/service/channel.go
#	backend/internal/service/domain_constants.go
#	backend/internal/service/email_service.go
#	backend/internal/service/payment_config_providers.go
#	backend/internal/service/payment_refund.go
#	backend/internal/service/settings_view.go
#	frontend/src/api/payment.ts
#	frontend/src/components/account/AccountUsageCell.vue
#	frontend/src/utils/__tests__/usageLoadQueue.spec.ts
2026-04-17 20:36:18 +08:00
erio 801b53d6af refactor(gateway): extract upstream response read limit default to shared const
config.DefaultUpstreamResponseReadMaxBytes 同时被 viper 默认值和 service
层兜底引用,消除两份 128*1024*1024 魔法值的同步负担。
2026-04-17 19:38:07 +08:00
erio d359c1f762 chore: bump version to 0.1.112.20 2026-04-17 19:34:52 +08:00
erio 5ffa106e8b chore(gateway): bump upstream response read limit 64MB -> 128MB
单张 4K PNG base64 最坏情况可达 67MB,image_generation_call 返回多张
候选图时 64MB 仍可能被撑爆。128MB 能覆盖 2-3 张 4K PNG 的常见场景,且相对
请求体上限 256MB 仍有缓冲。
2026-04-17 19:34:52 +08:00
erio 7acec3a60d chore: bump version to 0.1.112.19 2026-04-17 18:32:53 +08:00
erio c3fcc37cc4 feat(gateway): raise upstream non-streaming response read limit to 64MB
非流式上游响应读取上限从 8MB 提到 64MB,避免图片生成(image_generation_call
返回 base64 内联图)单张高清图就把响应撑爆、被 `ReadUpstreamResponseBody`
直接拒绝成 upstream_error 502。

64MB ≈ 48MB 原始字节(base64 膨胀 33%),足够一次响应带多张高清图。依然通过
gateway.upstream_response_read_max_bytes 可配置。
2026-04-17 18:32:33 +08:00
erio 93efc12812 fix(billing): reject rate_multiplier <= 0 on save; clamp negatives to 0 in compute
分组倍率和用户专属倍率在保存时没有校验,0 会触发计费层的 `<=0 → 1.0`
防御条款,结果订阅/余额分组按标准价扣费;完全是沉默地绕过了业务规则。

- 保存校验(admin_service):CreateGroup / UpdateGroup / BatchSetGroupRateMultipliers /
  UpdateUser.SyncUserGroupRates 全部要求 > 0
- 计算层(billing_service):三处 `<=0 → 1.0` 改为 `<0 → 0`;负数按 0 结算,
  避免配置异常被静默按 1x 收费
- 前端:分组倍率 / 用户专属倍率输入 min 统一到 0.001
- 删除未使用的 IsFreeSubscription 方法

测试:新增 billing_service_rate_multiplier_test.go 端到端验证;更新原有锁定
旧 `<=0 → 1.0` 行为的测试。
2026-04-17 18:32:12 +08:00
erio 017f517a38 chore: bump version to 0.1.112.18 2026-04-17 17:01:26 +08:00
erio 718c886245 fix(admin): prevent browser password manager from autofilling account API key
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.
2026-04-17 17:01:01 +08:00
erio b9d5613169 fix(usage): subscription billing honours group rate multiplier
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.
2026-04-17 17:00:45 +08:00
erio 40df055888 fix(payment): store provider config as plaintext JSON with legacy ciphertext fallback
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.
2026-04-17 17:00:29 +08:00
Wesley Liddick 358ff6a608 Merge pull request #1683 from FjlI5/dev-main
fix:修复上游账号为OpenAI API key时Claude Code调用缓存率低的问题
2026-04-17 10:28:12 +08:00
Wesley Liddick 41fbdba104 Merge pull request #1687 from touwaeriol/refactor/upstream-response-limit-dedup
refactor: extract ReadUpstreamResponseBody to deduplicate response read + too-large handling
2026-04-17 10:19:14 +08:00
Wesley Liddick c22d11cedd Merge pull request #1702 from StarryKira/fix/outbox-watermark-context-dedup-1691
fix: fix outbox watermark context expiry and add in-batch group rebuild dedup
2026-04-17 10:18:56 +08:00
shaw 5d586a9f3a fix: 上游返回 KYC 身份验证要求时停止账号调度 2026-04-17 10:17:50 +08:00
shaw a789c8c4c7 feat: 支持opus-4.7 2026-04-17 09:37:25 +08:00
ElysiaandClaude Sonnet 4.6 697c41a3f6 fix: create fresh context per watermark write retry attempt
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>
2026-04-16 20:41:40 +08:00
ElysiaandClaude Sonnet 4.6 e44baa1094 fix: fix outbox watermark context expiry and add in-batch group rebuild dedup
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>
2026-04-16 19:09:40 +08:00
Wesley Liddick e6e73b4f52 Merge pull request #1690 from KnowSky404/fix/ws-codex-scheduler-cache-1662
fix: preserve openai ws flags in scheduler cache
2026-04-16 17:21:32 +08:00
shaw 7ea8e7e667 chore: update sponsors 2026-04-16 17:19:32 +08:00
shaw a55ead5ea8 chore: remove empty dir Antigravity-Manager 2026-04-16 16:42:40 +08:00
KnowSky404 836092a666 fix: restore ctx pool ws mode option in account ui 2026-04-16 02:13:04 +00:00
KnowSky404 3944b3d216 fix: preserve openai ws flags in scheduler cache 2026-04-16 02:01:50 +00:00
erio 10699eeb34 refactor: extract ReadUpstreamResponseBody to deduplicate upstream response read + too-large error handling
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).
2026-04-16 01:53:22 +08:00
erio 645271d07a chore: bump version to 0.1.112.17 2026-04-16 01:34:03 +08:00
erio c48f44d109 refactor: extract ReadUpstreamResponseBody to deduplicate upstream response read + too-large error handling
Consolidates 9 call sites of resolveUpstreamResponseReadLimit + readUpstreamResponseBodyLimited + ErrUpstreamResponseBodyTooLarge error handling into a single ReadUpstreamResponseBody function with TooLargeWriter callback for API-format-specific error responses.
2026-04-16 01:33:47 +08:00
fjl5 6c89d8d35c add prompt_cache_key injection for messages→responses 2026-04-15 23:56:56 +08:00
github-actions[bot] be7551b9f4 chore: sync VERSION to 0.1.113 [skip ci] 2026-04-15 09:34:24 +00:00
Wesley Liddick 70d0569f08 Merge pull request #1668 from tyqy12/main
修复 OpenAI 账号限流回流误判:7d 窗口可用时不因 5h 窗口为 0 回写 429
2026-04-15 16:48:48 +08:00
Wesley Liddick 1db32d692b Merge pull request #1666 from touwaeriol/feat/account-cost-display
feat(usage): add account cost display to admin dashboard and usage pages
2026-04-15 16:43:07 +08:00
Wesley Liddick 8fd29082c0 Merge pull request #1663 from touwaeriol/fix/test-dialog-close-during-stream
fix(ui): allow closing test dialog during active SSE stream
2026-04-15 16:40:40 +08:00
Wesley Liddick 9bf079b725 Merge pull request #1655 from touwaeriol/feat/payment-fee-multiplier
feat(payment): balance recharge multiplier and fee rate
2026-04-15 16:40:14 +08:00
erio e180dd0710 fix(usage): remove label text from inline account cost, keep orange color 2026-04-15 16:09:58 +08:00
erio d235f6e70c fix(usage): remove label text from inline account cost, keep orange color 2026-04-15 16:05:58 +08:00
erio a7dd535d47 fix(usage): show account cost inline under cost column, remove separate column
- Cost cell: change gray "A $xxx" to orange "成本 $xxx" with i18n
- Remove standalone account_cost column from column settings (redundant)
2026-04-15 15:59:51 +08:00
erio 6c24c96500 fix(usage): show account cost inline under cost column, remove separate column
- Cost cell: change gray "A $xxx" to orange "成本 $xxx" with i18n
- Remove standalone account_cost column from column settings (redundant)
2026-04-15 15:56:39 +08:00
erio db27e8f000 feat(usage): add account cost to breakdown sub-table and admin usage log
- UserBreakdownItem: add AccountCost field + SQL aggregation
- UserBreakdownSubTable: add orange account cost column
- Admin usage table: add account_cost column (after cost, default visible)
- Column settings: add account_cost toggle option
2026-04-15 15:40:40 +08:00
erio 28d045e4f0 feat(usage): add account cost to breakdown sub-table and admin usage log
- UserBreakdownItem: add AccountCost field + SQL aggregation
- UserBreakdownSubTable: add orange account cost column
- Admin usage table: add account_cost column (after cost, default visible)
- Column settings: add account_cost toggle option
2026-04-15 15:31:55 +08:00
Wesley Liddick 7451b6f9ae 修复 OpenAI 账号限流回流误判:7d 窗口可用时不因 5h 窗口为 0 回写 429 2026-04-15 15:29:52 +08:00
erio e0b12b7512 fix(usage): put cost label before value in usage stats card 2026-04-15 15:02:21 +08:00
erio 22680dc602 test(usage): add unit tests for account_cost and fix gofmt
- Fix mock for GetModelStatsWithFilters: add account_cost column
- Add assertion: GetStatsWithFilters always returns TotalAccountCost
- New test: GetModelStatsAccountCostColumn verifies scan of AccountCost
- New test: GetGroupStatsAccountCostColumn verifies scan of AccountCost
- New test: GetStatsWithFiltersAlwaysReturnsAccountCost (no AccountID filter)
- Integration test: add TotalAccountCost/TodayAccountCost assertions
- Fix gofmt alignment in usage_log_types.go
2026-04-15 15:02:21 +08:00
erio 6ade6d30a8 feat(usage): add account cost display to admin dashboard and usage pages
- Add account_cost column to dashboard aggregation tables (migration 107)
- DashboardStats: add TotalAccountCost/TodayAccountCost fields
- ModelStat/GroupStat: add AccountCost field with SQL aggregation
- GetStatsWithFilters: always return TotalAccountCost (remove accountID filter)
- Dashboard Token cards: show user(green)/cost(orange)/standard(gray)
- Usage stats card: show account cost and standard below main value
- Model/Group distribution tables: add orange cost column
2026-04-15 15:02:21 +08:00
erio 02aebfd6fd fix(usage): put cost label before value in usage stats card 2026-04-15 14:17:57 +08:00
erio 5bc7eb42fb test(usage): add unit tests for account_cost and fix gofmt
- Fix mock for GetModelStatsWithFilters: add account_cost column
- Add assertion: GetStatsWithFilters always returns TotalAccountCost
- New test: GetModelStatsAccountCostColumn verifies scan of AccountCost
- New test: GetGroupStatsAccountCostColumn verifies scan of AccountCost
- New test: GetStatsWithFiltersAlwaysReturnsAccountCost (no AccountID filter)
- Integration test: add TotalAccountCost/TodayAccountCost assertions
- Fix gofmt alignment in usage_log_types.go
2026-04-15 14:10:16 +08:00
erio dc743a91b9 feat(usage): add account cost display to admin dashboard and usage pages
- Add account_cost column to dashboard aggregation tables (migration 107)
- DashboardStats: add TotalAccountCost/TodayAccountCost fields
- ModelStat/GroupStat: add AccountCost field with SQL aggregation
- GetStatsWithFilters: always return TotalAccountCost (remove accountID filter)
- Dashboard Token cards: show user(green)/cost(orange)/standard(gray)
- Usage stats card: show account cost and standard below main value
- Model/Group distribution tables: add orange cost column
2026-04-15 13:47:38 +08:00