1. 19 Apr, 2026 1 commit
  2. 18 Apr, 2026 1 commit
    • erio's avatar
      feat(payment): redact provider secrets in admin config API · 235f7108
      erio authored
      Admin GET /api/v1/admin/payment/providers previously returned every
      config value — including privateKey / apiV3Key / secretKey etc. —
      verbatim. Any future XSS on the admin UI would hand attackers the
      full set of production payment credentials, and the plaintext values
      sat unnecessarily in browser memory for every operator.
      
      Treat those fields as write-only from the admin surface:
      
      - decryptAndMaskConfig() strips sensitive keys from the GET response.
        The authoritative list is an explicit per-provider registry that
        mirrors the frontend's PROVIDER_CONFIG_FIELDS sensitive flag:
          alipay   → privateKey, publicKey, alipayPublicKey
          wxpay    → privateKey, apiV3Key, publicKey
          stripe   → secretKey, webhookSecret (publishableKey stays plain)
          easypay  → pkey
        Payment runtime still reads the full config via decryptConfig, so
        nothing at the gateway changes.
      
      - mergeConfig() treats an empty value for a sensitive key as "leave
        unchanged" — the admin UI omits unchanged secrets so operators can
        tweak non-sensitive settings without re-entering credentials.
      
      - Admin dialog (PaymentProviderDialog.vue):
        * secret inputs get autocomplete="new-password", data-1p-ignore,
          data-lpignore and data-bwignore so password managers do not
          offer to save provider credentials
        * in edit mode the required-field check skips sensitive fields
          (empty is the "keep existing" signal) and the placeholder shows
          "leave empty to keep" instead of the default example value
        * create mode still requires every non-optional field, including
          secrets, since there is nothing to preserve
      
      - Unit test renamed to TestIsSensitiveProviderConfigField, covers
        the per-provider registry and specifically asserts that Stripe's
        publishableKey is NOT treated as a secret.
      235f7108
  3. 17 Apr, 2026 7 commits
    • erio's avatar
      chore(payment): mark legacy AES ciphertext fallback as deprecated · 61a008f7
      erio authored
      明文 JSON 已经是新写入的默认格式;保留 AES 密文读取仅为兼容迁移期间的旧
      记录,一旦所有部署通过管理后台重存过一次即可删除。标记为 deprecated 并加
      TODO,几个版本后统一清理掉:payment.Encrypt / payment.Decrypt、两处
      decryptConfig 的 AES 分支、PaymentConfigService.encryptionKey 和
      DefaultLoadBalancer.encryptionKey 字段。
      61a008f7
    • erio's avatar
      feat(gateway): raise upstream response read limit 8MB -> 128MB (configurable) · bf0bbe0b
      erio authored
      图片生成 API 返回的 base64 内联图响应经常超过 8MB 单次读取上限,被
      ReadUpstreamResponseBody 拦截成 502 upstream_error。
      
      单张 4K PNG base64 最坏约 67MB,多张候选图或 imageSize=4K 的 image_generation
      一次请求能轻松到 30MB+。把默认上限提到 128MB 能覆盖 2-3 张 4K 图,相对
      请求体上限 256MB 仍有缓冲;同时抽出 config.DefaultUpstreamResponseReadMaxBytes
      共享常量,viper 默认值和 service 层兜底共用,消除两处同步魔法数字。
      
      仍可通过 gateway.upstream_response_read_max_bytes 配置项覆盖。
      bf0bbe0b
    • erio's avatar
      fix(billing): reject rate_multiplier <= 0 on save; clamp negatives to 0 in compute · df57d277
      erio authored
      分组倍率和用户专属倍率在保存时没有校验,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` 行为的测试。
      df57d277
    • erio's avatar
      fix(usage): subscription billing honours group rate multiplier · 44cdef79
      erio authored
      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.
      44cdef79
    • erio's avatar
      fix(payment): store provider config as plaintext JSON with legacy ciphertext fallback · fd0c9a13
      erio authored
      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.
      fd0c9a13
    • shaw's avatar
      5d586a9f
    • shaw's avatar
      feat: 支持opus-4.7 · a789c8c4
      shaw authored
      a789c8c4
  4. 16 Apr, 2026 3 commits
  5. 15 Apr, 2026 3 commits
  6. 14 Apr, 2026 25 commits
    • erio's avatar
      fix: gofmt payment_service.go and payment_order.go · c2108421
      erio authored
      c2108421
    • erio's avatar
      fix(payment): use original recharge amount in product name, not pay_amount · 342dbd2e
      erio authored
      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.
      342dbd2e
    • erio's avatar
      fix: gofmt formatting and update API contract test for new fields · 60614e6f
      erio authored
      - Fix gofmt alignment in setting_handler.go, settings.go, payment_config_service.go
      - Add payment_balance_recharge_multiplier and payment_recharge_fee_rate
        to API contract test expected JSON
      60614e6f
    • erio's avatar
      fix(payment): enhance fee rate input validation and UI · d149dbc9
      erio authored
      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
      d149dbc9
    • erio's avatar
      fix(payment): integrate recharge fee rate in order flow and fix UI display · e761d38f
      erio authored
      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
      e761d38f
    • erio's avatar
      feat(payment): add recharge fee rate setting and fix provider card UI · 98140f6c
      erio authored
      - 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
      98140f6c
    • erio's avatar
      feat(payment): balance recharge multiplier and refund amount separation · 60a4b931
      erio authored
      - 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
      60a4b931
    • erio's avatar
      fix: Messages() routing refactor and subscription group test coverage · 8548a130
      erio authored
      - Refactor OpenAI Messages() routing: pre-compute dispatch model using
        resolveOpenAIMessagesDispatchMappedModel + NormalizeOpenAICompatRequestedModel
        instead of try-fail-retry pattern with gin context passing
      - Remove openai_messages_fallback_model context anti-pattern
      - Use effectiveMappedModel directly for forward default mapped model
      - Add 3 subscription group tests covering all branch paths:
        _Blocked (no active subscription → SUBSCRIPTION_REQUIRED),
        _RequiresRepo (nil repo → SUBSCRIPTION_REPOSITORY_UNAVAILABLE),
        _AllowsActiveSubscription (valid subscription → success)
      8548a130
    • erio's avatar
      fix: flaky WebSocket test, usage request queue, and test improvements · 3fa5b8bc
      erio authored
      - Fix flaky WebSocket passthrough test: allow StatusNormalClosure after
        client close instead of requiring NoError (race condition fix)
      - Fix ratelimit 401 test: use PlatformOpenAI instead of PlatformGemini
        for OAuth token cache invalidation scenario (more accurate)
      - Add usageLoadQueue: Anthropic OAuth/setup-token accounts sharing the
        same proxy exit are serialized with 1-2s jitter to prevent upstream 429
      - AccountUsageCell: add module-level usage cache (5min TTL), unmounted
        safety guard, and integrate enqueueUsageRequest for throttled fetching
      3fa5b8bc
    • erio's avatar
      fix: merge general improvements from release branch · 63f539b3
      erio authored
      Backend:
      - gateway_handler: pass subject.UserID instead of int64(0) for user-level routing
      - setting_handler: add missing BalanceLowNotifyRechargeURL to UpdateSettings response
      - openai_gateway_service: use applyAccountStatsCost for account stats pricing integration
      - embed_on: add local file override (data/public/) for embedded frontend assets
      
      Frontend:
      - useTableSelection: add batchUpdate method for batch operations
      - AccountsView: virtual scrolling params, Set-based isSelected, swipe virtualization
      - ProxiesView: add batchUpdate to selection and swipe-select
      - BulkEditAccountModal: fix submit handler to prevent event object as argument
      - SettingsView: move payload construction outside try block
      - i18n: add general translation keys (saved, deleted, view, validation, allowUserRefund)
      - api/client: reorder error fields for consistency
      - stores/payment: clarify pollOrderStatus JSDoc
      63f539b3
    • erio's avatar
      fix: resolve 3 code review issues in allow_user_refund · c14d7393
      erio authored
      1. PrepareRefund: block refund on provider instance lookup failure
         instead of silently skipping permission check (medium severity)
      
      2. UpdateProviderInstance: allow enabling refund_enabled and
         allow_user_refund in the same request by checking req.RefundEnabled
         value before falling back to DB read
      
      3. ExecuteRefund: only revoke subscription on ErrAdjustWouldExpire,
         abort on other errors (DB failure, not found) instead of
         unconditionally revoking
      c14d7393
    • erio's avatar
      fix: merge 30 general improvements from release branch · 6ac8ccde
      erio authored
      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
      6ac8ccde
    • erio's avatar
      feat: add per-provider allow_user_refund control and align wildcard matching · f1297a36
      erio authored
      allow_user_refund:
      - Add allow_user_refund field to PaymentProviderInstance ent schema
      - Migration 103: ALTER TABLE payment_provider_instances ADD COLUMN
      - Cascade logic: disabling refund_enabled auto-disables allow_user_refund
      - User refund validation: check provider instance allows user refund
      - Admin refund validation: check provider instance allows admin refund
      - Subscription refund: deduct days on refund, rollback on failure
      - New endpoint: GET /payment/orders/refund-eligible-providers
      - Frontend: ToggleSwitch in ProviderCard/Dialog, cascade in SettingsView
      
      Wildcard matching:
      - Change findPricingForModel from "longest prefix wins" to "config order
        priority (first match wins)", aligning with channel service behavior
      f1297a36
    • erio's avatar
      fix: resolve remaining lint errors for upstream CI · e8ee400a
      erio authored
      - Fix errcheck: brave.go resp.Body.Close, manager_test.go Encode
      - Fix gofmt: payment_config_service.go
      - Fix unused: use shouldFallbackGeminiModel (with modelName param) in handler
      e8ee400a
    • erio's avatar
      fix: resolve upstream CI failures (lint, test, gofmt) · 6a08efee
      erio authored
      - Fix errcheck: handle Write/Encode return values in brave_test.go
      - Fix errcheck: defer resp.Body.Close() with _ assignment in tavily.go
      - Fix gofmt: payment.go, channel.go, payment_config_providers.go
      - Fix unused: remove dead decodeURLValue in easypay.go
      - Restore shouldFallbackGeminiModel function (deleted during cherry-pick)
      - Add missing balanceNotifyService param to NewGatewayService in test
      - Fix platform default test expectation (empty stays empty)
      - Fix wildcard pricing test (longest prefix wins, not config order)
      - Fix subscription group test (SUBSCRIPTION_REPOSITORY_UNAVAILABLE)
      6a08efee
    • erio's avatar
      fix: resolve test compilation errors and restore upstream VERSION · b42f34c3
      erio authored
      - Add missing interface methods to test stubs (RemoveGroupFromUserAllowedGroups,
        GetNotifyCodeUserRate, IncrNotifyCodeUserRate, UpdateGroupIDByUserAndGroup)
      - Fix NewUserService call signatures (add 4th param)
      - Fix GetAccountCount return signature (3 values)
      - Update api_contract_test.go snapshots for balance_notify fields
      - Restore resolveOpenAIMessagesDispatchMappedModel function
      - Reset VERSION to upstream 0.1.112
      b42f34c3
    • erio's avatar
      fix: resolve cherry-pick conflicts and restore compilation · d6965b06
      erio authored
      - Restore gateway_cache.go to upstream (no lua embeds)
      - Restore payment_order.go to upstream (use out_trade_no lookup)
      - Restore payment_fulfillment.go to upstream (same reason)
      - Add FeaturesConfig field and IsWebSearchEmulationEnabled to Channel
      - Add applyAccountStatsCost wrapper function
      - Add SettingKeyWebSearchEmulationConfig constant
      - Add WebSearchEmulationEnabled to SystemSettings
      - Add notify code rate limiting methods to EmailCache interface
      - Remove AllowUserRefund references (ent schema not present)
      - Fix duplicate import in payment_handler.go
      - Fix wire_gen.go argument mismatches
      d6965b06
    • erio's avatar
      test: add unit tests for billing, websearch, and notify systems · 9028d208
      erio authored
      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
      9028d208
    • erio's avatar
      feat: websearch quota enhancements and balance notify hint · 7c729293
      erio authored
      - 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
      7c729293
    • erio's avatar
      fix: gofmt formatting across all Go source files · 1e6912ea
      erio authored
      1e6912ea
    • erio's avatar
      fix: show websearch API key visibility/copy buttons for saved providers · 9e0d12d3
      erio authored
      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).
      9e0d12d3
    • erio's avatar
      fix: add opportunistic STARTTLS to sendMailPlain for 587 port compatibility · b402c367
      erio authored
      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.
      b402c367
    • erio's avatar
      fix: audit round-3 — proxy safety, intervals persistence, SMTP timeout, sort fix · 0a4ece5f
      erio authored
      - 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
      0a4ece5f
    • erio's avatar
      fix: websearch features_config cleanup and pricing rules validation · 9c09bd19
      erio authored
      - 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)
      9c09bd19
    • erio's avatar
      fix: round-2 audit fixes — security, code quality, and UI improvements · a9880ee7
      erio authored
      Security (HIGH):
      - Normalize all Redis cache keys to lowercase (verifyCode, passwordReset)
      - Fix verify code TTL renewal on failed attempts: use remaining TTL via
        ExpiresAt field instead of resetting to full 15-minute window
      - Add 3 missing fields to diffSettings audit log (promo_code, invitation_code,
        custom_endpoints)
      
      Code quality (MEDIUM):
      - Extract filterVerifiedEmails shared helper (balance_notify_service.go)
      - Add Pricing array non-empty validation for channel pricing rules
      - Add platform token semantics comment in gateway_service.go
      - Complete validatePlanPatch test coverage (+10 test cases)
      - Replace string types with QuotaThresholdType/QuotaResetMode across frontend
      - Remove duplicate getPlatformTextColor/getRateBadgeClass in ChannelsView
      - Return EMAIL_NOT_FOUND error on RemoveNotifyEmail miss
      
      UI improvements:
      - Reorder cost tooltip: user billing above separator, account billing below
      - Add NaN guard to accountBilled function
      - Move timezone selector inline into reset-mode row (no longer standalone)
      a9880ee7