1. 14 Apr, 2026 7 commits
    • 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): 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
      refactor: remove PaymentChannel, reuse upstream Channel with features field · 794e8172
      erio authored
      - Delete payment_channels table and PaymentChannel Ent schema
      - Add `features` column to upstream channels table (migration 095)
      - Add Features field to Channel struct, input types, handler request/response
      - Payment user/admin handlers now use ChannelService directly
      - Remove Channel CRUD from PaymentConfigService and admin payment routes
      - Remove "渠道管理" tab from admin orders page (use /admin/channels)
      794e8172
    • erio's avatar
      fix: address review findings for channel restriction refactoring · 160903fc
      erio authored
      - Fix 7 stale comments still mentioning "限制检查" in handlers/services
      - Make billingModelForRestriction explicitly list channel_mapped case
      - Add slog.Warn for error swallowing in ResolveChannelMapping and
        needsUpstreamChannelRestrictionCheck
      - Document sticky session upstream check exemption
      160903fc
    • erio's avatar
      refactor: move channel model restriction from handler to scheduling phase · 2dce4306
      erio authored
      Move the model pricing restriction check from 8 handler entry points
      to the account scheduling phase (SelectAccountForModelWithExclusions /
      SelectAccountWithLoadAwareness), aligning restriction with billing:
      
      - requested: check original request model against pricing list
      - channel_mapped: check channel-mapped model against pricing list
      - upstream: per-account check using account-mapped model
      
      Handler layer now only resolves channel mapping (no restriction).
      Scheduling layer performs pre-check for requested/channel_mapped,
      and per-account filtering for upstream billing source.
      2dce4306
  2. 11 Apr, 2026 3 commits
    • 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
      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
  3. 10 Apr, 2026 1 commit
  4. 09 Apr, 2026 9 commits
  5. 08 Apr, 2026 2 commits
  6. 07 Apr, 2026 1 commit
  7. 05 Apr, 2026 4 commits
    • erio's avatar
      refactor(channel): split long functions, extract shared validation, move... · 9151d34d
      erio authored
      refactor(channel): split long functions, extract shared validation, move billing validation to service
      
      - Split Update (98→25 lines), buildCache (54→20 lines), Create (51→25 lines)
        into focused sub-functions: applyUpdateInput, checkGroupConflicts,
        fetchChannelData, populateChannelCache, storeErrorCache, getOldGroupIDs,
        invalidateAuthCacheForGroups
      - Extract validateChannelConfig to eliminate duplicated validation calls
        between Create and Update
      - Move validatePricingBillingMode from handler to service layer for
        proper separation of concerns
      - Add error logging to IsModelRestricted (was silently swallowing errors)
      - Add 12 new tests: ToUsageFields, billing mode validation, antigravity
        wildcard mapping isolation, Create/Update mapping conflict integration
      9151d34d
    • erio's avatar
    • erio's avatar
      fix: resolve CI failures — gofmt, unused functions, test parameter mismatches · 5bb8b2ad
      erio authored
      - gofmt: user.go, config_test.go, group_handler.go, smart_retry_test.go
      - Remove unused: mergeGroupIDs, resolveProxyURL, "time" import
      - Fix api_contract_test.go: remove extra Sora args from NewAdminService,
        NewSettingHandler, NewAccountHandler; remove Sora field expectations
      - Fix account_test_service_openai_test.go: restore test helpers
      5bb8b2ad
    • erio's avatar
      revert: completely remove all Sora functionality · 62e80c60
      erio authored
      62e80c60
  8. 04 Apr, 2026 13 commits
    • erio's avatar
      fix: resolve cherry-pick compilation and test issues · e59fa863
      erio authored
      - Add int64(0) param to SelectAccountWithLoadAwareness callers (signature change from channel scheduling refactor)
      - Add UsageMapHook type and struct field to StreamingProcessor
      - Revert Claude Max cache billing code to upstream/main (not part of channel feature)
      - Revert credits overages logic to upstream/main (non-channel change)
      - Remove Instructions field reference (non-channel OpenAI feature)
      - Restore sora_client_handler_test.go from upstream + add channel service nil params
      e59fa863
    • erio's avatar
      fix: address review findings for channel restriction refactoring · 1fca2bfa
      erio authored
      - Fix 7 stale comments still mentioning "限制检查" in handlers/services
      - Make billingModelForRestriction explicitly list channel_mapped case
      - Add slog.Warn for error swallowing in ResolveChannelMapping and
        needsUpstreamChannelRestrictionCheck
      - Document sticky session upstream check exemption
      1fca2bfa
    • erio's avatar
      refactor: move channel model restriction from handler to scheduling phase · ce41afb7
      erio authored
      Move the model pricing restriction check from 8 handler entry points
      to the account scheduling phase (SelectAccountForModelWithExclusions /
      SelectAccountWithLoadAwareness), aligning restriction with billing:
      
      - requested: check original request model against pricing list
      - channel_mapped: check channel-mapped model against pricing list
      - upstream: per-account check using account-mapped model
      
      Handler layer now only resolves channel mapping (no restriction).
      Scheduling layer performs pre-check for requested/channel_mapped,
      and per-account filtering for upstream billing source.
      ce41afb7
    • erio's avatar
    • erio's avatar
      refactor: use structured error responses in channel handler · d3127b8e
      erio authored
      Replace response.BadRequest with response.ErrorFrom + infraerrors.BadRequest
      to provide machine-readable reason codes (VALIDATION_ERROR, INVALID_CHANNEL_ID,
      MISSING_PARAMETER) for frontend i18n support.
      d3127b8e
    • erio's avatar
      refactor: replace magic strings with named constants · 0d241d52
      erio authored
      - PricingSourceChannel/LiteLLM/Fallback for resolver source
      - MediaTypeImage/Video/Prompt for result.MediaType
      - Reuse BillingModeToken/BillingModeImage for billing mode
      - Reuse BillingModelSourceChannelMapped/PlatformAnthropic in handler
      0d241d52
    • erio's avatar
      b8c56ff9
    • erio's avatar
      fix: validate empty intervals + antigravity platform pricing match · 2355029d
      erio authored
      - Backend: reject intervals with all-null price fields on save
      - Backend: filterValidIntervals skips empty intervals in pricing resolver
      - Frontend: red border + asterisk on empty interval rows
      - Backend: antigravity groups now match anthropic/gemini channel pricing
      2355029d
    • erio's avatar
      fix: golangci-lint test assertion and gofmt · c9145ad4
      erio authored
      c9145ad4
    • erio's avatar
      fix: resolve golangci-lint issues · 3851628a
      erio authored
      - Fix errcheck: defer rows.Close() with nolint
      - Fix errcheck: type assertion with ok check in channel cache
      - Fix staticcheck ST1005: lowercase error string
      - Fix staticcheck SA5011: nil check cost before use in openai gateway
      - Fix gofmt: format chatcompletions_to_responses.go
      3851628a
    • 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
    • erio's avatar
      fix(channel): 全平台渠道映射覆盖 + 公共函数抽取 + 死代码清理 · eb385457
      erio authored
      - 4个缺失handler入口添加渠道映射+限制检查(ChatCompletions/Responses/Gemini)
      - 模型限制错误信息优化,区分"模型不可用"和"无账号"
      - OpenAI RecordUsage RequestedModel 改用 OriginalModel
      - ResolveChannelMappingAndRestrict/ReplaceModelInBody 抽取到 ChannelService 消除跨service重复
      - validateNoDuplicateModels 按 platform:model 去重
      - 删除 Channel.ResolveMappedModel 死代码和 CalculateCostWithChannel Deprecated方法
      - 移除冗余nil检查,抽取 validatePricingBillingMode 公共校验
      eb385457