- 19 Apr, 2026 1 commit
-
-
erio authored
Add quota exceeded check to IsSchedulable() and refactor shouldClearStickySession to delegate to IsSchedulable(), eliminating duplicated logic and fixing missed overload/rate-limit/expired checks. Frontend displays quota exceeded status independently via quota fields.
-
- 18 Apr, 2026 1 commit
-
-
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.
-
- 17 Apr, 2026 7 commits
-
-
erio authored
明文 JSON 已经是新写入的默认格式;保留 AES 密文读取仅为兼容迁移期间的旧 记录,一旦所有部署通过管理后台重存过一次即可删除。标记为 deprecated 并加 TODO,几个版本后统一清理掉:payment.Encrypt / payment.Decrypt、两处 decryptConfig 的 AES 分支、PaymentConfigService.encryptionKey 和 DefaultLoadBalancer.encryptionKey 字段。
-
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 配置项覆盖。
-
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` 行为的测试。
-
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.
-
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.
-
shaw authored
-
shaw authored
-
- 16 Apr, 2026 3 commits
-
-
Elysia authored
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>
-
Elysia authored
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>
-
KnowSky404 authored
-
- 15 Apr, 2026 3 commits
-
-
erio authored
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).
-
fjl5 authored
-
Wesley Liddick authored
-
- 14 Apr, 2026 25 commits
-
-
erio authored
-
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.
-
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
-
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
-
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
-
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
-
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
-
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)
-
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
-
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
-
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
-
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
-
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
-
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
-
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)
-
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
-
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
-
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
-
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
-
erio authored
-
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).
-
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.
-
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
-
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)
-
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)
-