Commit Graph
3827 Commits
Author SHA1 Message Date
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
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
erio d235f6e70c fix(usage): remove label text from inline account cost, keep orange color 2026-04-15 16:05:58 +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 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
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
erio 2436478dfe chore: bump version to 0.1.112.15 2026-04-15 11:13:04 +08:00
erio a7598dcc30 fix(ui): allow closing test dialog during active SSE stream
Replace dead EventSource variable with AbortController to enable
cancelling fetch streams. Remove close-button disable during connecting
status so users can dismiss the dialog at any time.
2026-04-15 11:12:53 +08:00
erio 374211bbe4 chore: bump version to 0.1.112.14 2026-04-15 01:56:50 +08:00
erio ddcf865533 fix: gofmt payment_service.go 2026-04-15 01:56:50 +08:00
erio 40797fda77 chore: bump version to 0.1.112.13 2026-04-15 01:43:37 +08:00
erio c7f114c0ed fix(payment): use original recharge amount in product name, not pay_amount
Product name (e.g. "快代码科技工作室 100 元") should show the user's
original recharge amount (limitAmount), not the fee-inclusive pay amount.
The gateway receives payAmount separately for actual charging.
2026-04-15 01:43:37 +08:00
erio 6c87ee45aa chore: bump version to 0.1.112.12 2026-04-15 01:41:03 +08:00
erio 999f4e8cb1 fix: gofmt formatting and update API contract test for new payment fields 2026-04-15 01:40:41 +08:00
erio a0beed6616 chore: bump version to 0.1.112.11 2026-04-15 01:20:46 +08:00
erio 2b09f815d1 fix(payment): show full amount breakdown on payment result page
- Show base amount (充值金额) as first line
- Show fee amount with percentage when fee_rate > 0
- Show pay_amount (实付金额) in bold primary color
- Show credited amount (到账金额) when different from pay_amount
- Compute baseAmount and feeAmount from backend order data
2026-04-15 01:20:46 +08:00
erio 36fc547fbd chore: bump version to 0.1.112.10 2026-04-15 01:11:49 +08:00
erio fd6fa240f7 fix(payment): enhance fee rate input validation and UI
Backend:
- Validate recharge_fee_rate: 0 ≤ rate ≤ 100, max 2 decimal places

Frontend settings:
- Add % suffix icon to fee rate input
- Enforce max=100, min=0, step=0.01 with 2 decimal precision
2026-04-15 01:11:49 +08:00
erio e7963b3ddc chore: bump version to 0.1.112.9 2026-04-15 01:04:01 +08:00
erio c133f1811e fix(payment): integrate recharge fee rate in order flow and fix UI display
Backend:
- Use cfg.RechargeFeeRate in order creation instead of hardcoded 0
- Remove dead getFeeRate stub method
- All amounts computed server-side: order_amount, pay_amount, fee_rate

Frontend - PaymentView:
- Read recharge_fee_rate from checkout-info API (not per-method)
- Show fee breakdown only when fee_rate > 0
- Show credited amount only when multiplier ≠ 1

Frontend - Order display (user + admin):
- Fix fee_rate * 100 bug (fee_rate is already a percentage)
- OrderTable: show pay_amount as primary, fee/credited as sub-lines
- AdminOrderDetail: full breakdown (base/fee/paid/credited)
- AdminRefundDialog: label "到账金额" for clarity
- PaymentResultView: show pay_amount with fee info

Types + i18n:
- Add recharge_fee_rate to CheckoutInfoResponse
- Add fee_rate to CreateOrderResult
- Add translations: creditedAmount, fee, baseAmount, includedInPayAmount
2026-04-15 01:04:01 +08:00
erio 509b451c9a chore: bump version to 0.1.112.8 2026-04-15 00:41:49 +08:00
erio 8f892a6a7b feat(payment): add recharge fee rate setting and fix provider card UI
- Add recharge_fee_rate system setting (percentage fee on top of recharge amount)
- Full backend chain: config constant, PaymentConfig struct, update validation,
  read/write persistence, DTO, handler GET/PUT responses
- Frontend: settings input with preview, i18n (zh/en), API types
- Fix provider card toggle layout: labels above switches to save width
- Fix Chinese translation: "EasyPay" → "易支付" in provider description
2026-04-15 00:41:33 +08:00
erio c83283b9e8 chore: bump version to 0.1.112.7 2026-04-15 00:18:40 +08:00
erio 7288503a45 feat(payment): balance recharge multiplier and refund amount separation
- Add balance_recharge_multiplier system setting (e.g. 1.2 = charge 100 get 120)
- Separate order_amount (credited balance) from pay_amount (actual payment)
- Refund calculates gateway amount proportionally from pay_amount
- Frontend shows both amounts in order details, payment status, refund dialog
- Admin settings UI for configuring recharge multiplier
2026-04-15 00:16:07 +08:00
erio 358eb897e4 chore: bump version to 0.1.112.6 2026-04-14 21:55:25 +08:00
erio d19e8385fa refactor: align Antigravity/ClaudeMax/Sora code with upstream main
- Restore 14 Antigravity/model-mapping files to upstream/main versions
  - Remove fork-specific credits pre-check and degraded retry logic
  - Revert sonnet 4.5→4.6 model mapping back to upstream defaults
  - Remove accountUsageService dependency from AntigravityGatewayService
  - Restore original applyThinkingModelSuffix behavior
- Delete 6 fork-only migration files:
  - 056/057 (Antigravity model mapping)
  - 060 (Claude Max simulation)
  - 070 (Sora tasks table)
  - 077/078 (sonnet rollback/migration)
- Fix wire_gen.go to match updated constructor signature
2026-04-14 21:28:20 +08:00
erio 3be0321bfe fix: Stripe payment type matching in load balancer
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.
2026-04-14 09:50:12 +08:00
erio 8084565d81 test: add unit tests for billing, websearch, and notify systems
Billing (25 tests):
- CalculateCostUnified: nil resolver fallback, token/per_request/image modes
- GetModelPricingWithChannel: nil/partial/full channel overrides
- resolveAccountStatsCost: four-level priority chain integration tests

WebSearch (18 tests):
- PopulateWebSearchUsage: nil input, manager states, QuotaLimit nil/*int64
- ResetWebSearchUsage: nil manager error
- Manager.ResetUsage: nil Redis
- shouldEmulateWebSearch: full decision chain (8 scenarios)

Notify (36 tests):
- ParseNotifyEmails/MarshalNotifyEmails: old/new format, roundtrip
- crossedDownward: boundary values, threshold semantics
- checkQuotaDimCrossings: mixed dimensions, disabled/zero skip
2026-04-14 08:42:28 +08:00
erio 9f9c3a9384 feat: websearch quota enhancements and balance notify hint
- 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
2026-04-14 08:07:29 +08:00
erio 63ae7365d6 fix: gofmt formatting across all Go source files 2026-04-14 07:43:08 +08:00
erio 9a820a1749 fix: show websearch API key visibility/copy buttons for saved providers
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).
2026-04-14 07:35:24 +08:00
erio 0104ff55c8 chore: bump version to 0.1.112.2 2026-04-14 07:01:29 +08:00
erio a232407486 fix: resolve remaining test failures after v0.1.112 merge
- Add missing balanceNotifyService param to NewGatewayService in warmup test
- Fix config_test YAML path escaping on Windows (use filepath.ToSlash)
- Update pricingRequestToService test: empty platform no longer defaults
2026-04-14 06:56:58 +08:00
erio 33a0ad767c fix: post-merge fixes for upstream v0.1.112 integration
- Fix AccountUsageCell loadUsage() parameter type mismatch (string→object)
- Add missing i18n keys for openaiMessages family mapping (en/zh, 17 keys)
- Remove duplicate payment provider declarations in wire.go
- Add sort_order field to PlanEditDialog.vue
- Remove 10 duplicate upstream-renumbered migration files
- Fix NewUserService call signatures in test files (missing 4th param)
- Update api_contract_test.go snapshots for new balance_notify fields
2026-04-14 02:57:24 +08:00
erio 54e55e8da0 Merge upstream v0.1.112
Merge upstream v0.1.112 into release/custom-0.1.112.
Resolved conflicts preserving our customizations (allow_user_refund,
balance/quota notify, web search emulation) while adopting upstream
improvements (BySortOrder for plans, CSS sidebar collapse animation,
mobile lazy-load optimization, Anthropic empty output fix).
2026-04-14 02:00:02 +08:00
erio 63315cb7db chore: bump version to 0.1.110.56 2026-04-14 01:39:06 +08:00
erio 3fdb63e2b6 fix: add opportunistic STARTTLS to sendMailPlain for 587 port compatibility
smtp.SendMail automatically upgrades to STARTTLS when the server
supports it. Our replacement sendMailPlain skipped this, causing
credentials to be sent in plaintext on port 587. Add STARTTLS
negotiation before Auth to restore the original security behavior.
2026-04-14 01:38:42 +08:00
erio 325d8eb2ae chore: bump version to 0.1.110.55 2026-04-14 01:11:13 +08:00
erio 541dd3b467 fix: audit round-3 — proxy safety, intervals persistence, SMTP timeout, sort fix
- Skip websearch provider when ProxyID is set but proxy not found (prevent
  silent direct connection bypass)
- Fix sortByStableRandomWeight: pair factors with items so sort.Slice swap
  keeps weights aligned
- Allow empty platform in account_stats_pricing_rules (wildcard matching),
  only force anthropic default for main model_pricing
- Add channel_account_stats_pricing_intervals table and repo layer support
  for interval-based pricing in account stats rules
- calculateTokenStatsCost now uses interval pricing when available
- Replace smtp.SendMail/tls.Dial with net.Dialer timeout (10s dial, 20s IO)
  to prevent goroutine leak on SMTP hang
- Fix gofmt formatting issues
- Web Search label: black text with red warning hint
2026-04-14 01:10:46 +08:00
erio b1c64dcdda chore: bump version to 0.1.110.54 2026-04-14 00:43:09 +08:00
erio 6d61455232 fix: websearch features_config cleanup and pricing rules validation
- Fix web_search_emulation toggle: explicitly write false for disabled
  platforms instead of leaving stale true from cloned features_config
- Extract validatePricingEntries from validateChannelConfig for reuse
- Validate account_stats_pricing_rules[].pricing in both Create and
  Update paths (negative prices, bad intervals, missing per_request price)
2026-04-14 00:42:40 +08:00
erio 61f702ddab chore: bump version to 0.1.110.53 2026-04-14 00:26:49 +08:00