1. 14 Apr, 2026 10 commits
    • 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
    • erio's avatar
      fix: address audit findings for websearch, email verification, and pricing · 74f8a30f
      erio authored
      - Fix websearch provider failover: proxy error from provider-specific proxy
        now continues to next provider instead of aborting the entire loop
      - Fix SMTP failure locking users out: send email first, then write cache
        and increment rate counter
      - Fix notify email cache key case sensitivity: normalize to lowercase
      - Add OriginalPrice validation to validatePlanPatch and validatePlanRequired
      - Add empty scope validation for channel pricing rules (group_ids/account_ids)
      - Add platform color to account search dropdown in channel pricing rules
      74f8a30f
    • erio's avatar
      fix: audit fixes for websearch, notifications, and channel pricing · b7fb2e43
      erio authored
      P0: fix wildcard matching test assertion (config order, not longest prefix)
      P0: add TotalRecharged to auth cache snapshot (v5) for percentage threshold
      P1: move pricing rules into per-platform sections in ChannelsView
      P1: populate account name cache when editing existing channel rules
      P1: sanitize email subject headers to prevent SMTP injection
      P1: make Redis INCR+EXPIRE idempotent for rate limiting
      P1: deep copy FeaturesConfig in Channel.Clone()
      P2: clean up stale email="" placeholder comments
      P2: replace log.Printf with slog in email_service.go
      b7fb2e43
    • erio's avatar
      fix: address audit findings across websearch, notify, and channel pricing · a68df457
      erio authored
      Backend fixes:
      - Fix balance notify ignoring percentage threshold type (was treating
        percentage value as fixed USD amount)
      - Remove dead code parseJSONStringArray
      - Add ImageOutputTokens to tryModelFilePricing calculation
      - Unify zero-value check: cost == 0 → cost <= 0 in calculateTokenStatsCost
      - Use MarshalNotifyEmails instead of json.Marshal for consistency
      - Rename quotaDim.oldUsed → currentUsed for clarity
      - Extract HTML email templates to const variables (function ≤30 lines)
      
      Test fixes:
      - Rewrite account_websearch_test.go for GetWebSearchEmulationMode tri-state
      - Add 6 tryModelFilePricing test cases
      
      Frontend fixes:
      - Replace hardcoded '未命名' with i18n key
      - Extract getBillingModeLabel/getBillingModeBadgeClass to shared utils
      - Replace inline type with imported NotifyEmailEntry
      - Pass platform to AccountStats pricing rules via inferRulePlatform()
      - Add billing mode constants (BILLING_MODE_TOKEN/PER_REQUEST/IMAGE)
      a68df457
    • erio's avatar
      feat: WebSearch tri-state, account stats pricing fix, quota cache fix, usage tooltip · 1262654d
      erio authored
      WebSearch tri-state switch:
      - Account-level web_search_emulation changed from bool to tri-state
        string: "default" (follow channel) / "enabled" / "disabled"
      - shouldEmulateWebSearch checks channel config when account is "default"
      - SQL migration converts old bool values
      - Frontend select replaces toggle in Edit/CreateAccountModal
      
      Account stats pricing:
      - resolveAccountStatsCost uses upstream model (post-mapping) for matching
      - Priority: custom rules → model pricing file (when toggle on) → default
      - Custom rules always configurable, independent of toggle
      - Account ID field changed to searchable selector filtered by platform
      - Description updated to reflect new behavior
      
      Quota notification cache fix:
      - CheckAccountQuotaAfterIncrement fetches real-time account from DB
      - Reconstructs pre-increment usage for accurate threshold crossing detection
      - New AccountQuotaReader interface (minimal: GetByID only)
      
      Usage tooltip:
      - Per-request/image billing shows per-request price instead of $0 token price
      - Token billing continues to show input/output price per million tokens
      1262654d
    • erio's avatar
      refactor(channels): move account stats pricing rules from basic to platform tabs · 80fa4844
      erio authored
      - Basic settings now only shows the global toggle
      - Custom pricing rules appear inside each platform tab when toggle is on
      - Group selector in rules scoped to the current platform's groups
      - Remove unused allFormGroupIds computed
      80fa4844
    • erio's avatar
      fix(websearch): improve settings UI and hide config when globally disabled · 889b5b4f
      erio authored
      - API Key show/copy buttons moved inside input field (inline icons)
      - Proxy selector and test button on same row to save vertical space
      - Test opens a dialog modal instead of inline display
      - Hide all websearch config in channels/accounts when global toggle is off
      889b5b4f
    • 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
  2. 09 Apr, 2026 1 commit
  3. 05 Apr, 2026 1 commit
  4. 04 Apr, 2026 25 commits