1. 14 Apr, 2026 11 commits
    • erio's avatar
      fix(notify): remove percentage threshold from balance notification · cef22c70
      erio authored
      Balance low notification only supports fixed USD amount threshold.
      Percentage threshold is a quota concept, not applicable to balance.
      Reverted threshold_type from admin settings, user profile, and all
      backend/frontend layers. DB fields (balance_notify_threshold_type,
      total_recharged) retained for potential future quota use.
      cef22c70
    • erio's avatar
      fix: address audit findings for websearch and balance notification · 9e33d0c4
      erio authored
      - Fix GetByKeyForAuth not selecting balance notify fields (notifications
        never triggered in gateway path)
      - Fix provider-level ProxyURL never resolved: inject ProxyRepository into
        SettingService, resolve proxy URLs when building Manager
      - Fix admin manual balance adjustment not updating total_recharged
      - Add threshold_type input validation (reject invalid values)
      - Fix user threshold_type inheritance: custom threshold defaults to "fixed"
        instead of inheriting global type (prevents $5 being treated as 5%)
      - Add try-catch for clipboard.writeText (fails on non-HTTPS)
      - Add SetTotalRecharged to user Update for admin balance operations
      9e33d0c4
    • erio's avatar
      feat(notify): add percentage threshold type for balance low notification · f694afbb
      erio authored
      - Add threshold_type field (fixed/percentage) to system and user settings
      - Add total_recharged field to users table, auto-incremented on balance credit
      - Percentage mode: effective threshold = total_recharged × percentage / 100
      - User-level threshold_type inherits from system default when not set
      - Update admin settings UI with radio selector (fixed amount / percentage)
      - Migration: 102_add_balance_notify_threshold_type.sql
      f694afbb
    • erio's avatar
      feat(websearch): settings UI overhaul and quota improvements · d0674e0f
      erio authored
      - Remove Priority field, auto load-balance by quota remaining
      - Replace QuotaRefreshInterval (daily/weekly/monthly) with SubscribedAt
        (subscription date, monthly lazy refresh via Redis TTL)
      - Add collapsible provider cards, API key show/copy, usage progress bar
      - Add test endpoint (POST /web-search-emulation/test) bypassing quota
      - Wire WebSearchManagerBuilder on startup (was never called before)
      - Fix nextMonthlyReset day-of-month overflow (Jan 31 → Feb 28)
      - Fix non-deterministic sort in selectByQuotaWeight
      - Map ProxyID in builder for provider-level proxy tracking
      - Fix frontend timezone drift in subscribed_at date picker
      - Fix provider deletion index shift for expandedProviders state
      d0674e0f
    • erio's avatar
      feat(notify): add balance low & account quota notification system · b32d1a2c
      erio authored
      - User balance low notification: email alert when balance drops below
        configurable threshold (user email + verified extra emails)
      - Account quota notification: broadcast email to admin-configured
        recipients when daily/weekly/total quota usage exceeds alert threshold
      - Admin settings: global enable/disable, default threshold, quota
        notification email list (Email Settings tab)
      - User profile: enable/disable, custom threshold, add/remove extra
        notification emails with verification code flow
      - Account quota: per-dimension alert toggle and threshold in quota
        control card
      - Trigger logic: first-crossing only (old >= threshold && new < threshold
        for balance; old < threshold && new >= threshold for quota), naturally
        prevents duplicate notifications without Redis dedup
      b32d1a2c
    • erio's avatar
      feat(channels): add custom account stats pricing rules · 7535e312
      erio authored
      Allow channels to configure independent model pricing for account
      statistics cost calculation, decoupled from user billing.
      
      Backend:
      - Migration 101: channels.apply_pricing_to_account_stats toggle,
        channel_account_stats_pricing_rules/model_pricing tables,
        usage_logs.account_stats_cost column
      - resolveAccountStatsCost: match rules by group/account, then channel
        pricing, fallback to original formula when unconfigured
      - Integrate into both GatewayService.recordUsageCore and
        OpenAIGatewayService.RecordUsage
      - Update 8 account stats SQL queries to use
        COALESCE(account_stats_cost, total_cost) * account_rate_multiplier
      - 23 unit tests for matching, pricing lookup, and cost calculation
      
      Frontend:
      - Channel edit dialog: toggle + custom rules UI with group/account
        multi-select and pricing entry cards
      - API types and i18n (zh/en)
      7535e312
    • erio's avatar
      feat(gateway): add web search emulation for Anthropic API Key accounts · 1b53ffca
      erio authored
      Inject web search capability for Claude Console (API Key) accounts that
      don't natively support Anthropic's web_search tool. When a pure
      web_search request is detected, the gateway calls Brave Search or Tavily
      API directly and constructs an Anthropic-protocol-compliant SSE/JSON
      response without forwarding to upstream.
      
      Backend:
      - New `pkg/websearch/` SDK: Brave and Tavily provider implementations
        with io.LimitReader, proxy support, and Redis-based quota tracking
        (Lua atomic INCR + TTL, DECR rollback on failure)
      - Global config via `settings.web_search_emulation_config` (JSON) with
        in-process cache + singleflight, input validation, API key merge on
        save, and sanitized API responses
      - Channel-level toggle via `channels.features_config` JSONB column
        (DB migration 101)
      - Account-level toggle via `accounts.extra.web_search_emulation`
      - Request interception in `Forward()` with SSE streaming response
        construction using json.Marshal (no manual string concatenation)
      - Manager hot-reload: `RebuildWebSearchManager()` called on config save
        and startup via `SetWebSearchRedisClient()`
      - 70 unit tests covering providers, manager, config validation,
        sanitization, tool detection, query extraction, and response building
      
      Frontend:
      - Settings → Gateway tab: Web Search Emulation config card with global
        toggle, provider list (add/remove, API key, priority, quota, proxy)
      - Channels → Anthropic tab: web search emulation toggle with global
        state linkage (disabled when global off)
      - Account Create/Edit modals: web search emulation toggle for API Key
        type with Toggle component
      - Full i18n coverage (zh + en)
      1b53ffca
    • erio's avatar
      fix(payment): critical audit fixes for security, idempotency and correctness · c738cfec
      erio authored
      Backend fixes:
      - #1: doSub subscription idempotency via audit log check
      - #2: markFailed only when status=RECHARGING (prevents overwriting COMPLETED)
      - #3: ExpireTimedOutOrders checks upstream payment before expiring
      - #4: Public verify endpoint for payment result page (no auth required)
      - #5: EasyPay QueryOrder returns amount, confirmPayment handles zero amount
      - #6: WxPay notifyUrl priority: request-first, config-fallback
      - #7: EasyPay remove double URL decode in VerifyNotification
      - #8: checkPaid/cancelUpstreamPayment use order's provider instance
      - #9: Amount NaN/Inf/negative validation in order creation and refund
      - #10: Refund amount comparison uses tolerance instead of float64 ==
      - #11: Skip balance deduction on retry when previous rollback failed
      - #12: checkPaid logs fulfillment errors instead of silently ignoring
      - #13: WxPay certSerial added to required config fields
      
      Frontend fixes:
      - Payment result page no longer requires authentication
      - Public verify API fallback for expired sessions
      c738cfec
    • erio's avatar
      fix: audit fixes - magic strings to constants, frontend any/catch, LB tests · 56e4a9a9
      erio authored
      Backend:
      - Define OrderTypeBalance/Subscription, EntityStatusActive, DeductionType*,
        NotificationStatus* constants in payment/types.go
      - Replace all magic strings in payment_order, payment_fulfillment, payment_refund
      - Add local constants in easypay.go (tradeStatusSuccess, signTypeMD5)
      - Add 27 unit tests for load balancer (filterByLimits, pickLeastAmount,
        getInstanceChannelLimits, startOfDay)
      
      Frontend:
      - Remove all `any` types in SettingsView.vue (18 catch blocks + 1 payload)
      - Fix bare catch blocks in PaymentResultView, PaymentView
      - Add `unknown` type annotation to all catch blocks
      
      chore: bump version to 0.1.108.140
      56e4a9a9
    • erio's avatar
      fix(payment): audit fixes for alipay/wxpay/stripe payment providers · 5bae3b05
      erio authored
      Backend:
      - Extract YuanToFen/FenToYuan to payment/amount.go using shopspring/decimal
      - Require alipay publicKey in config validation
      - Fix wxpay webhook response to return JSON per V3 spec
      - Remove wxpay certSerial fallback to publicKeyId
      - Define magic strings as named constants in wxpay/alipay providers
      - Add slog warning for wxpay H5→Native payment downgrade
      - Make EncryptionKey validation return error on invalid (non-empty) key
      - Make decryptConfig propagate errors instead of returning nil
      - Add idempotency check in doBalance to prevent stuck FAILED retries
      
      Frontend:
      - Fix dashboard currency symbol from $ to ¥
      - Fix AdminPaymentPlansView any type to proper SubscriptionPlan type
      - Make quick amount buttons follow selected payment method limits
      - Center help image with larger height and text below
      5bae3b05
    • erio's avatar
  2. 13 Apr, 2026 1 commit
  3. 11 Apr, 2026 8 commits
    • erio's avatar
    • erio's avatar
    • erio's avatar
      fix(payment): pass expires_at for Stripe countdown timer · a020fc52
      erio authored
      Stripe payment path was setting expiresAt to empty string, causing
      PaymentStatusPanel to fall back to hardcoded 30-minute default when
      the popup redirect switches to the waiting view.
      a020fc52
    • erio's avatar
      feat(payment): add H5/mobile payment support · 75155903
      erio authored
      Backend:
      - Parse EasyPay `payurl2` field, prefer H5 link on mobile
      - Add `device=mobile` to EasyPay submit.php (popup) mode
      - Expand isMobile detection keywords (add ipad/ipod)
      
      Frontend:
      - Add `isMobileDevice()` utility (userAgentData + UA regex)
      - Mobile + pay_url: direct redirect instead of QR/popup
      - Popup blocked fallback: auto-redirect when window.open fails
      - Stripe WeChat Pay: dynamic client param (mobile_web vs web)
      75155903
    • erio's avatar
      refactor(payment): code standards fixes and regression repairs · e3a000e0
      erio authored
      Backend:
      - Split payment_order.go (546→314 lines) into payment_order_lifecycle.go
      - Replace magic strings with constants in factory, easypay, webhook handler
      - Add rate limit/validity unit constants in payment_order_lifecycle, payment_service
      - Fix critical regression: add PaymentEnabled to GetPublicSettings response
      - Add missing migration 099_fix_migrated_purchase_menu_label_icon.sql
      
      Frontend:
      - Fix StripePopupView.vue: replace `as any` with typed interface, use extractApiErrorMessage
      - Fix AdminOrderTable.vue: replace hardcoded column labels with i18n t() calls
      - Fix SubscriptionsView.vue: replace hardcoded Today/Tomorrow with i18n
      - Extract duplicate statusBadgeClass/canRefund/formatOrderDateTime to orderUtils.ts
      - Add missing i18n keys: common.today, common.tomorrow, payment.orders.orderType/actions
      - Remove dead PurchaseSubscriptionView.vue (replaced by PaymentView)
      e3a000e0
    • erio's avatar
      fix(payment): remove purchase_subscription fields replaced by payment system · 27cd2f8e
      erio authored
      The built-in payment system replaces the old external purchase subscription
      iframe approach. Remove purchase_subscription_enabled/url from admin settings
      interface and form defaults, as the Payment tab now handles this functionality.
      Kept in stores/app.ts fallback to match backend DTO response structure.
      27cd2f8e
    • erio's avatar
      fix(payment): resolve PR audit issues · e1547d78
      erio authored
      - Add payment navigation to AppSidebar (user orders + admin payment menu group with collapse)
      - Add 5 missing nav i18n keys (myOrders, orderManagement, paymentDashboard, paymentConfig, paymentPlans)
      - Renumber payment migrations 090-100 → 092-102 to avoid conflict with upstream 090/091
      - Remove non-payment sora_client_enabled change, restore upstream purchase_subscription fields
      - Remove extra 'data' from SettingsTab type union
      e1547d78
    • erio's avatar
      feat(payment): add complete payment system with multi-provider support · 63d1860d
      erio authored
      Add a full payment and subscription system supporting EasyPay (Alipay/WeChat),
      Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
      63d1860d
  4. 10 Apr, 2026 1 commit
  5. 09 Apr, 2026 5 commits
  6. 08 Apr, 2026 1 commit
    • shaw's avatar
      feat: sync billing header cc_version with User-Agent and add opt-in CCH signing · e51c9e50
      shaw authored
      - Sync cc_version in x-anthropic-billing-header with the fingerprint
        User-Agent version, preserving the message-derived suffix
      - Implement xxHash64-based CCH signing to replace the cch=00000
        placeholder with a computed hash
      - Add admin toggle (enable_cch_signing) under gateway forwarding settings,
        disabled by default
      e51c9e50
  7. 07 Apr, 2026 1 commit
  8. 05 Apr, 2026 5 commits
  9. 04 Apr, 2026 7 commits
    • erio's avatar
      fix: channel cache fail-close, group conflict check across pages, status toggle stale data · feb6999d
      erio authored
      - GetGroupPlatforms failure now stores error-TTL cache and returns error (fail-close)
      - Frontend group-to-channel conflict map loads all channels instead of current page only
      - Toggle channel status reloads list when active filter would hide the changed item
      feb6999d
    • erio's avatar
      fix: resolve 5 audit findings in channel/credits/scheduling · 71f61bbc
      erio authored
      P0-1: Credits degraded response retry + fail-open
      - Add isAntigravityDegradedResponse() to detect transient API failures
      - Retry up to 3 times with exponential backoff (500ms/1s/2s)
      - Invalidate singleflight cache between retries
      - Fail-open after exhausting retries instead of 5h circuit break
      
      P1-1: Fix channel restriction pre-check timing conflict
      - Swap checkClaudeCodeRestriction before checkChannelPricingRestriction
      - Ensures channel restriction is checked against final fallback groupID
      
      P1-2: Add interval pricing validation (frontend + backend)
      - Backend: ValidateIntervals() with boundary, price, overlap checks
      - Frontend: validateIntervals() with Chinese error messages
      - Rules: MinTokens>=0, MaxTokens>MinTokens, prices>=0, no overlap
      
      P2: Fix cross-platform same-model pricing/mapping override
      - Store cache keys using original platform instead of group platform
      - Lookup across matching platforms (antigravity→anthropic→gemini)
      - Prevents anthropic/gemini same-name models from overwriting each other
      71f61bbc
    • erio's avatar
    • erio's avatar
      fix(ui): show token breakdown when image model uses token billing · 212eaa3a
      erio authored
      Only display image count format when billing_mode is "image".
      When channel has token pricing, show input/output/cache token details.
      212eaa3a
    • erio's avatar
      fix: address audit findings - cache sync, validation, consistency · 9b213115
      erio authored
      - clearCreditsExhausted: sync Redis scheduler cache after DB update
      - Image billing mode UI: write to per_request_price instead of image_output_price
      - OpenAI RecordUsage: use BillingModelSourceRequested constant, add s.cfg nil guard
      - Fix i18n key path: admin.channels.perRequestPriceRequired → admin.channels.form.perRequestPriceRequired
      9b213115
    • erio's avatar
      feat: image output token billing, channel-mapped billing source, credits balance precheck · d72ac926
      erio authored
      - Parse candidatesTokensDetails from Gemini API to separate image/text output tokens
      - Add image_output_tokens and image_output_cost to usage_log (migration 089)
      - Support per-image-token pricing via output_cost_per_image_token from model pricing data
      - Channel pricing ImageOutputPrice override works in token billing mode
      - Auto-fill image_output_price in channel pricing form from model defaults
      - Add "channel_mapped" billing model source as new default (migration 088)
      - Bills by model name after channel mapping, before account mapping
      - Fix channel cache error TTL sign error (115s → 5s)
      - Fix Update channel only invalidating new groups, not removed groups
      - Fix frontend model_mapping clearing sending undefined instead of {}
      - Credits balance precheck via shared AccountUsageService cache before injection
      - Skip credits injection for accounts with insufficient balance
      - Don't mark credits exhausted for "exhausted your capacity on this model" 429s
      d72ac926
    • erio's avatar
      feat(channel): 渠道管理全链路集成 — 模型映射、定价、限制、用量统计 · 2555951b
      erio authored
      - 渠道模型映射:支持精确匹配和通配符映射,按平台隔离
      - 渠道模型定价:支持 token/按次/图片三种计费模式,区间分层定价
      - 模型限制:渠道可限制仅允许定价列表中的模型
      - 计费模型来源:支持 requested/upstream 两种计费模型选择
      - 用量统计:usage_logs 新增 channel_id/model_mapping_chain/billing_tier/billing_mode 字段
      - Dashboard 支持 model_source 维度(requested/upstream/mapping)查看模型统计
      - 全部 gateway handler 统一接入 ResolveChannelMappingAndRestrict
      - 修复测试:同步 SoraGenerationRepository 接口、SQL INSERT 参数、scan 字段
      2555951b