1. 28 Apr, 2026 1 commit
    • DaydreamCoding's avatar
      feat(openai): OpenAI Fast/Flex Policy 完整实现(HTTP + WebSocket + Admin) · 30f55a1f
      DaydreamCoding authored
      
      
      对称参照 Claude BetaPolicy 的 fast-mode 过滤实现,新增针对 OpenAI 上游
      service_tier 字段(priority / flex,含客户端 "fast" → "priority" 归一化)的
      pass / filter / block 三态策略,覆盖全部 OpenAI 入口 + admin 配置入口。
      
      后端核心
      - 新增 SettingKeyOpenAIFastPolicySettings、OpenAIFastPolicyRule、
        OpenAIFastPolicySettings 配置模型,含规则的 service_tier × action × scope
        × 模型白名单 × fallback action 维度。
      - SettingService.Get/SetOpenAIFastPolicySettings;缺失时返回内置默认策略
        (所有模型的 priority 走 filter,whitelist 为空,fallback=pass)。设计
        依据:service_tier=fast 是用户级开关,与 model 字段正交,默认锁定特定
        model slug 会留下"用 gpt-4 + fast 透传 priority 上游"的绕过路径。JSON
        解析失败不再静默 fallback,slog.Warn 记录脏数据,便于运维定位。
      - service_tier 归一化(trim + ToLower + fast→priority + 白名单 priority/flex)
        与策略评估(evaluateOpenAIFastPolicy)作为唯一真实来源,HTTP / WS 共用。
        抽出纯函数 evaluateOpenAIFastPolicyWithSettings,配合 ctx-bound settings
        快照(withOpenAIFastPolicyContext / openAIFastPolicySettingsFromContext),
        WS 长会话入口预取一次后所有帧复用,避免每帧打到 settingService。
      
      HTTP 入口(4 个)
      - Chat Completions、Anthropic 兼容(Messages,含 BetaFastMode→priority 二次
        命中)、原生 Responses、Passthrough Responses 全部接入
        applyOpenAIFastPolicyToBody,filter 走 sjson 顶层删除 service_tier,block
        返回 403 forbidden_error JSON。
      - 4 入口统一使用 upstream 视角的 model(GetMappedModel +
        normalizeOpenAIModelForUpstream + Codex OAuth normalize 后的 slug),
        避免 chat/messages/native /responses/passthrough 因为 model 维度不同
        造成 whitelist 命中差异。
      - 在 pass 路径也把客户端 "fast" 别名归一化为 "priority" 写回 body,
        否则 native /responses 与 passthrough 入口会把 "fast" 原样透传给上游
        导致 400/拒绝(chat-completions 入口的 normalizeResponsesBodyServiceTier
        此前已具备同等行为)。
      
      WebSocket 入口
      - 新增 applyOpenAIFastPolicyToWSResponseCreate:严格匹配
        type="response.create",仅处理顶层 service_tier;filter 用 sjson 删字段,
        block 返回 typed *OpenAIFastBlockedError。
      - ingress 路径在 parseClientPayload 内调用,block 命中先 Write Realtime
        风格 error event 再返回 OpenAIWSClientCloseError(StatusPolicyViolation
        =1008),依赖底层 WebSocket Conn.Write 的同步 flush 保证 error 先于
        close。
      - passthrough 路径在 RunEntry 前对 firstClientMessage 应用策略,并通过
        openAIWSPolicyEnforcingFrameConn 包装 ReadFrame 对每个 client→upstream
        帧执行策略;后续帧无 model 字段时回退到 capturedSessionModel。
        filter 闭包内同时侦测 session.update / session.created 帧的 session.model
        字段刷新 capturedSessionModel,封堵"首帧 model=gpt-4o(pass)→
        session.update 改为 gpt-5.5 → 不带 model 的 response.create fallback
        到 gpt-4o"的 mid-session 绕过路径。
      - passthrough billing:requestServiceTier 在策略 filter 之后再从
        firstClientMessage 提取,filter 命中时 OpenAIForwardResult.ServiceTier
        上报 nil(default tier),与 HTTP 入口(reqBody 来自 post-filter map)
        / WS ingress(payload 来自 post-filter bytes)的语义一致。
      - 错误事件 schema:{event_id: "evt_<32hex>", type: "error",
        error: {type: "forbidden_error", code: "policy_violation", message}},
        与 OpenAI codex 客户端 error event 解析兼容。
      
      Admin / Frontend
      - dto.SystemSettings / UpdateSettingsRequest 新增
        openai_fast_policy_settings 字段(omitempty),bulk GET/PUT 接入。
      - Settings 页 Gateway 页签新增 Fast/Flex Policy 表单卡片:
        service_tier × action × scope × 模型白名单 × fallback action 全字段配置。
      - 前端守门:openaiFastPolicyLoaded 标志仅在 GET 真带回字段时才允许回写,
        避免 rollout/错误把默认规则覆盖成空;saveSettings 回写循环 skip 该字段,
        由专用刷新逻辑处理;仅 action=block 时发送 error_message,匹配后端
        omitempty 行为。
      
      测试
      - HTTP 路径:openai_fast_policy_test.go 覆盖默认配置(whitelist=[],所有
        模型 priority filter)/ block 自定义错误 / scope 区分 / filter 删字段 /
        block 不改 body / block 短路上游 / Anthropic BetaFastMode 触发 OpenAI
        fast policy 等场景。
      - WebSocket 路径:openai_fast_policy_ws_test.go 覆盖
          helper 单元(filter / fast→priority 归一化 / flex 透传 / block typed
          error / 无 service_tier 字节不变 / 非 response.create 帧不动 / 空 type
          帧不动 / event_id+code 字段断言 / 非字符串 service_tier 容错)+
          pass 路径 fast 别名归一化回归 +
          ingress 端到端(filter 后上游不含 service_tier / block 后客户端先收
          error event 再收 close 1008 且上游 0 写)+
          passthrough capturedSessionModel fallback 用例(whitelist 策略下首帧
          建立、缺 model 命中 fallback、缺少 fallback 时的 leak 文档化)+
          passthrough session.update / session.created 旋转 capturedSessionModel
          的 mid-session 绕过回归 +
          passthrough billing post-filter ServiceTier 与 idempotent filter 回归。
      Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
      30f55a1f
  2. 27 Apr, 2026 3 commits
  3. 26 Apr, 2026 2 commits
    • shaw's avatar
      feat(affiliate): 完善邀请返利系统 · 9b6dcc57
      shaw authored
        - 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突
        - 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定
        - 前端 OAuth 注册页面传递 aff_code 参数
        - 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻)
        - 新增返利有效期:绑定后 N 天内有效,过期不再产生返利
        - 新增单人返利上限:超出上限部分精确截断
        - 增强返利流程 slog 结构化日志,便于排查问题
        - 已邀请用户列表增加返利明细列
      9b6dcc57
    • Oliver's avatar
      Add Vertex service account support · 6d11f9ed
      Oliver authored
      6d11f9ed
  4. 25 Apr, 2026 2 commits
    • shaw's avatar
      feat(affiliate): add feature toggle and per-user custom invite settings · 4e1bb2b4
      shaw authored
      - 在系统设置「功能开关」中新增邀请返利总开关,默认关闭;
        关闭态:菜单隐藏、注册忽略 aff、新充值不返利,但已有 quota 仍可转余额
      - 支持管理员为指定用户设置专属邀请码(覆盖随机码,全局唯一)
      - 支持管理员为指定用户设置专属返利比例(覆盖全局比例,可单条/批量调整)
      - 在系统设置邀请返利卡片内嵌入专属用户管理表格(搜索/编辑/批量/删除),
        删除采用项目通用 ConfirmDialog,会同时清除专属比例并把邀请码重置为系统随机码
      - /affiliate 用户页新增「我的返利比例」卡片与动态使用说明,让用户直观看到
        分享后能拿到多少(同源 resolveRebateRatePercent 计算,与实际充值一致)
      - 新增数据库迁移 132 添加 aff_rebate_rate_percent 与 aff_code_custom 列
      - 新增 admin 路由组 /api/v1/admin/affiliates/users/* 共 5 个端点
      - AffiliateService 改为只依赖 *SettingService,去除冗余的 SettingRepository
      - 邀请码格式校验放宽到 [A-Z0-9_-]{4,32},兼容旧 12 位系统码与新自定义码
      - 补充单元测试与集成测试覆盖新方法、冲突路径与边界值
      4e1bb2b4
    • shaw's avatar
      feat(openai): port /responses/compact account support flow (PR #1555) · 095f457c
      shaw authored
      将 vansour/sub2api#1555 的 OpenAI compact 能力建模手工移植到当前 main:账号
      级 compact 状态/auto-force_on-force_off 模式、compact-only 模型映射、调度器
      tier 分层(已支持 > 未知 > 已知不支持)、管理后台 compact 主动探测,以及对应
      i18n/状态徽章。普通 /responses 流量行为不变,无数据库迁移。
      095f457c
  5. 24 Apr, 2026 1 commit
  6. 23 Apr, 2026 3 commits
    • erio's avatar
      revert: remove fork-only changes from release sync · 67518a59
      erio authored
      Revert payment/wechat, sora/claude-max cleanup, fork-only migrations,
      and cosmetic changes that were brought in by the release sync commit.
      Keep only channel-monitor related improvements:
      - PublicSettingsInjectionPayload named struct with drift test
      - ChannelMonitorRunner graceful shutdown in wire
      - image_output_price in SupportedModelChip
      - Simplified buildSelfNavItems in AppSidebar
      - Gateway WARN logs for 503 branches
      67518a59
    • erio's avatar
      sync: bring over remaining release/custom-0.1.115 changes · 748a84d8
      erio authored
      - Extract PublicSettingsInjectionPayload named struct with drift test
      - Add channel_monitor_default_interval_seconds to SSR injection
      - Add image_output_price to SupportedModelChip
      - Simplify AppSidebar buildSelfNavItems (admins see available channels)
      - Add gateway WARN logs for 503 no-available-accounts branches
      - Wire ChannelMonitorRunner into provideCleanup for graceful shutdown
      - Add migrations 130/131 (CC template userid fix + mimicry field cleanup)
      - Clean up fork-only features (sora, claude max simulation, client affinity)
      - Remove ~320 obsolete i18n keys
      - Add codexUsage utility, WechatServiceButton, BulkEditAccountModal
      - Tidy go.sum
      748a84d8
    • james-6-23's avatar
      feat(rpm): RPM 限流模块优化 · dc5d42ad
      james-6-23 authored
      P0:
      - rpm_override 嵌入 Auth Cache Snapshot,消除每请求 DB 查询 (snapshot v6→v7)
      - 429 RPM 响应返回 Retry-After 头(当前分钟剩余秒数)
      
      P1:
      - ClearAll 按钮直连 DELETE API,带 loading 防重复
      - 新增 GET /admin/users/:id/rpm-status 管理员 RPM 用量查询端点
      
      优化:
      - checkRPM 从级联互斥改为并行取最严,user.rpm_limit 作为全局硬上限始终生效
      - Override/Group 变更后自动失效 auth cache
      - fail-open 语义不变,Redis 故障不阻塞业务
      dc5d42ad
  7. 22 Apr, 2026 5 commits
  8. 21 Apr, 2026 8 commits
    • IanShaw027's avatar
    • IanShaw027's avatar
      2cebb0dc
    • IanShaw027's avatar
    • IanShaw027's avatar
      d08757ce
    • erio's avatar
      feat(channels): gate available channels behind feature switch (backend) · 9ba42aa5
      erio authored
      Add a DB-backed soft switch "available_channels_enabled" controlling
      the user-facing /channels/available endpoint and sidebar entry. Default
      to false (opt-in) — the feature stays invisible until an admin enables
      it under Admin Settings > Features.
      
      - domain_constants: SettingKeyAvailableChannelsEnabled
      - settings_view: AllSettings/PublicSettings + AvailableChannelsEnabled
      - setting_service: public+all read/write, seed default "false",
        GetAvailableChannelsRuntime helper (fail-closed on read error)
      - admin setting_handler: UpdateSettingsRequest *bool + update branch
        + audit diff entry
      - public setting_handler: expose via GET /api/v1/settings
      - available_channel_handler: featureEnabled() guard — returns empty
        list after auth when disabled (401 precedes the feature check to
        preserve existing behavior)
      9ba42aa5
    • erio's avatar
      chore(channels): drop admin-side available channels view · 59290e39
      erio authored
      Remove the admin-side "Available Channels" aggregate view — admins
      already see full channel configuration (groups, pricing, model
      mappings) in the channel edit dialog, making a read-only admin
      aggregate view redundant. The user-side "可用渠道" remains.
      
      Backend:
      - Delete handler/admin/available_channel_handler.go (+ test)
      - Drop AdminHandlers.AvailableChannel field and wire injection
      - Remove /admin/channels/available route
      
      Frontend:
      - Delete views/admin/AvailableChannelsView.vue
      - Drop /admin/available-channels router entry
      - Strip AvailableChannel types + listAvailable from api/admin/channels.ts
      59290e39
    • IanShaw027's avatar
      feat: add payment order provider snapshots · 561405ab
      IanShaw027 authored
      561405ab
    • erio's avatar
      refactor(channels): centralize BillingModelSource normalization and exhaustive enum maps · 375aefa2
      erio authored
      - service: add normalizeBillingModelSource helper, apply in Create/GetByID/Update/List/ListAvailable outputs
      - handler: drop channelToResponse fallback now that service owns the default; add passthrough test
      - frontend: replace ternary status/billing-source lookups with Record<Enum, ...> maps so new union members fail the build
      - chip/table: drop local type aliases, reuse UserSupportedModel/UserPricingInterval directly
      - tests: assert short-circuit on ListAll error, wrap-prefix preservation, and Name-based default lookup
      375aefa2
  9. 20 Apr, 2026 4 commits
    • IanShaw027's avatar
      ebe75244
    • IanShaw027's avatar
      fix settings auth source default persistence · e12599c1
      IanShaw027 authored
      e12599c1
    • erio's avatar
      refactor(channels): consolidate pricing index, tighten types, polish DTOs · 365ef1fd
      erio authored
      Follow-up to the available-channels review pass. No behavior change for
      end users; tightens internals based on three independent code reviews.
      
      Backend
      - service/channel.go: collapse buildPricingLookup + pricedNamesFor
        into a single platformPricingIndex (byLower + originalCase + ordered
        names), built once per SupportedModels call. Fixes a casing-
        consistency bug where the same logical model appeared with mapping
        case in the exact branch but pricing case in the wildcard branch —
        pricing's original case now wins everywhere.
      - service/channel.go: doc that a mapping key of just "*" expands to
        every priced model on the platform (intentional "passthrough all").
      - service/channel_available.go: normalize empty BillingModelSource to
        channel_mapped at construction time, removing the same fallback
        duplicated in the admin DTO mapper and the admin Vue template.
      - handler/admin/available_channel_handler.go: unexport
        availableChannelToAdminResponse (same-package usage only); mapper
        is now a pure passthrough.
      - handler/available_channel_handler.go: drop the middleware2 alias
        (no name collision in this file).
      
      Frontend
      - utils/pricing.ts: extract formatScaled, used by SupportedModelChip
        and PricingRow.
      - api/admin/channels.ts: re-export BillingMode from constants/channel;
        tighten Channel.status / billing_model_source to ChannelStatus /
        BillingModelSource (and same for AvailableChannel).
      - components/channels/AvailableChannelsTable.vue: drop dead
        withDefaults wrapper (loading is required, both call sites pass it).
      - views/admin/AvailableChannelsView.vue: drop the redundant
        || BILLING_MODEL_SOURCE_CHANNEL_MAPPED fallback (now applied in
        service layer); remove unused import.
      - i18n zh + en: delete unused tierLabel and tokenRange keys from
        both availableChannels.pricing and admin.availableChannels.pricing.
      
      Tests
      - New: SupportedModels_ExactKeyUsesPricedCaseWhenAvailable locks the
        pricing-case-wins rule.
      - New: SupportedModels_AsteriskOnlyMappingExpandsAllPriced documents
        the "*" expansion rule.
      - Admin handler: existing tests adjusted to pass an explicit
        BillingModelSource (default-fill is now exercised by service tests).
      365ef1fd
    • erio's avatar
      feat(channels): add "Available Channels" aggregate view · 654cfb64
      erio authored
      Add a read-only aggregate view per channel: its linked groups and a
      deterministic wildcard-free supported-model list with pricing details.
      
      Backend
      - service.Channel.SupportedModels(): combine ModelMapping keys with
        same-platform ModelPricing.Models; trailing "*" keys expand via
        pricing prefix match; platforms without a mapping produce no
        entries (intentional "no mapping = not shown" rule).
      - Extract splitWildcardSuffix() shared with toModelEntry.
      - Build a per-call pricing lookup map (platform+lowerName -> *pricing)
        to avoid O(N*M) scans in SupportedModels.
      - ChannelService.ListAvailable() aggregates channels + active groups;
        filters out group IDs no longer active.
      - Admin route GET /api/v1/admin/channels/available returns the full
        DTO (id, status, billing_model_source, restrict_models, groups,
        supported_models).
      - User route GET /api/v1/channels/available applies three filters:
        Status==active, visible-group intersection, and platform filter
        on supported_models (prevents cross-platform leak when a channel
        links to both a user-accessible group and an inaccessible one on
        another platform). Response is a plain array (matches the
        /groups/available sibling shape). Field whitelist omits
        billing_model_source, restrict_models, ids, status, sort_order.
      
      Frontend
      - New /admin/available-channels and /available-channels views backed
        by a shared AvailableChannelsTable component (admin adds status +
        billing-source columns via slots).
      - PricingRow extracted to its own SFC; SupportedModelChip references
        shared billing-mode constants in constants/channel.ts.
      - Sidebar: new entry above "渠道管理" for admin; matching entry in
        user nav.
      - i18n: zh + en coverage for both namespaces.
      
      Tests
      - SupportedModels: wildcard-only pricing skipped, prefix-matches-
        nothing, cross-platform bleed, case-insensitive dedup, empty
        platform mapping.
      - ListAvailable: nil groupRepo, inactive-group-ID dropped, stable
        case-insensitive name sort.
      - User handler: 401 on unauthenticated, visible-group intersection,
        platform filter on supported_models, JSON whitelist.
      - Admin handler: full DTO including default BillingModelSource
        fallback.
      
      Refs: issue #1729
      654cfb64
  10. 21 Apr, 2026 2 commits
    • erio's avatar
      feat(channel-monitor): apply template via subset picker; CC 2.1.114 baseline doc · 6925ac25
      erio authored
      Apply flow:
      - POST /admin/channel-monitor-templates/:id/apply now requires monitor_ids
        (non-empty array). Service applies the template only to the selected
        subset, gated by AND template_id = :id (so users can't sneak in
        unrelated monitor IDs).
      - New GET /admin/channel-monitor-templates/:id/monitors returns the
        associated monitor briefs (id/name/provider/enabled) for the picker.
      - ApplyToMonitors signature gains monitorIDs []int64; empty list returns
        ErrChannelMonitorTemplateApplyEmpty.
      
      Frontend:
      - New MonitorTemplateApplyPickerDialog.vue: list of associated monitors
        with checkboxes (default all checked), 全选 / 全不选 shortcuts, live
        selected/total count. Submit calls apply(id, ids).
      - MonitorTemplateManagerDialog replaces the old ConfirmDialog flow with
        the picker; onApplied refetches the list to refresh associated counts.
      
      i18n: applyPicker* + common.selectAll keys.
      
      chore: bump version to 0.1.114.33
      
      The CC 2.1.114 (sdk-cli) UA / APIKeyBetaHeader / JSON metadata.user_id
      baseline (already verified working via the in-process apply on prod
      template id=1) is documented in internal/pkg/claude/constants.go and
      is what the seed template in the manager UI should follow.
      6925ac25
    • erio's avatar
      feat(channel-monitor): request templates with snapshot apply + headers/body override · a2964259
      erio authored
      Problem:
      Upstream channels can reject monitor probes based on client fingerprint
      (e.g. "only Claude Code clients allowed"). The monitor had no way to
      customize the outgoing request to bypass such restrictions.
      
      Solution:
      Introduce reusable request templates that carry extra_headers plus an
      optional body override; monitors reference a template and receive a
      snapshot copy on apply. Template edits do NOT auto-propagate — users
      must click "apply to associated monitors" to refresh snapshots, so a
      bad template edit cannot instantly break all production monitors.
      
      Data model (migration 112):
      - channel_monitor_request_templates: id, name, provider, description,
        extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
        body_override jsonb. Unique (provider, name).
      - channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
        +body_override_mode, +body_override (the three runtime snapshot fields).
      
      Checker (channel_monitor_checker.go):
      - callProvider + runCheckForModel accept a CheckOptions carrying the
        snapshot fields. mergeHeaders applies user headers on top of adapter
        defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
        Connection / Content-Encoding).
      - buildRequestBody:
          off     -> adapter default body
          merge   -> shallow-merge over default; per-provider deny list
                     (model/messages/contents) protects the challenge contract
          replace -> user body verbatim
      - Replace mode skips challenge validation; instead HTTP 2xx + non-empty
        extracted response text = operational, empty = failed.
      - 4 new unit tests cover all three modes + replace/empty-response case.
      
      Admin API:
      - /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
        on all template_id=id monitors, returns affected count).
      - channel_monitor request/response DTOs gain the 4 new fields.
      
      Frontend:
      - channelMonitorTemplate.ts API client.
      - MonitorAdvancedRequestConfig.vue shared component for headers textarea
        + body mode radio + body JSON editor; used by both template and monitor
        forms.
      - MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
        delete/apply, live "associated monitors" count per row.
      - MonitorFiltersBar: new 模板管理 button next to 新增监控.
      - MonitorFormDialog: collapsible 高级 section with template dropdown
        (filtered by form.provider, clears on provider change) + embedded
        AdvancedRequestConfig. Picking a template copies its fields into the
        form (snapshot semantics mirrored on the client).
      - i18n zh/en entries for all new copy.
      
      chore: bump version to 0.1.114.32
      a2964259
  11. 20 Apr, 2026 7 commits
    • erio's avatar
      feat(channel-monitor): add feature switch settings + fix extra_models save · 7da51240
      erio authored
      Settings:
      - New "功能开关" tab between 通用设置 and 安全与认证
      - ChannelMonitorEnabled toggle: runner skips scheduling when false,
        user-facing list returns empty
      - ChannelMonitorDefaultIntervalSeconds (15-3600): pre-fills interval
        when creating a new monitor; each monitor can still override
      
      Bug fix:
      - ModelTagInput now commits pending input on blur, not just Enter/Tab.
        Previously clicking "save" with an un-Enter'd extra model would drop
        the value (DB stored extra_models=[] even when user typed entries).
      
      Backend:
      - domain_constants: SettingKeyChannelMonitor{Enabled,DefaultIntervalSeconds}
      - SettingService.GetChannelMonitorRuntime: lightweight getter used by
        runner tick + user handler per-request (fail-open on DB error)
      - Runner tickDueChecks: bails early when feature disabled
      - ChannelMonitorUserHandler: checks feature flag before serving
      - Comment on runner doc: scheduler state is implicit (every tick re-reads
        ListEnabled from DB), so CRUD ops on monitors self-maintain the schedule
      
      Bump VERSION to 0.1.114.25
      7da51240
    • IanShaw027's avatar
    • IanShaw027's avatar
      feat: resolve auth identity migration reports · 724f8e89
      IanShaw027 authored
      724f8e89
    • IanShaw027's avatar
      feat: add admin auth identity repair binding · 452e55a5
      IanShaw027 authored
      452e55a5
    • IanShaw027's avatar
      feat: expose auth identity migration reports · 3bd30272
      IanShaw027 authored
      3bd30272
    • erio's avatar
      feat(monitor): admin channel monitor MVP with SSRF protection and batch aggregation · 20a4e418
      erio authored
      新增 admin「渠道监控」模块(参考 BingZi-233/check-cx),独立于现有 Channel 体系。
      admin 配置 + 后台定时调用上游 LLM chat completions 健康检查 + 所有登录用户只读可见。
      
      后端:
      - ent: channel_monitor + channel_monitor_history(AES-256-GCM 加密 api_key)
      - service 按职责拆分:service/aggregator/validate/checker/runner/ssrf
      - provider strategy map 替代 switch(openai/anthropic/gemini)
      - repository batch 聚合(ListLatestForMonitorIDs + ComputeAvailabilityForMonitors)消除 N+1
      - runner: ticker(5s) + pond worker pool(5) + inFlight 防并发 + TrySubmit 防雪崩
        + 凌晨 3 点 cron 清理 30 天历史
      - SSRF 防护:强制 https + 私网/loopback/云元数据 IP 拒绝(127/8、10/8、172.16/12、
        192.168/16、169.254/16、100.64/10、::1、fc00::/7、fe80::/10)+ DialContext
        在 socket 层防 DNS rebinding
      - API key sanitize:擦除 url.Error 与上游响应 body 中的 sk-/sk-ant-/AIza/JWT 模式
      - APIKeyDecryptFailed 标志位 + 单 monitor 路径检测,避免空 key 调用上游
      
      handler:
      - admin: CRUD + 手动触发 + 历史接口(api_key 脱敏)
      - user: 只读列表 + 状态详情(去除 api_key/endpoint)
      - ParseChannelMonitorID 共用 + dto.ChannelMonitorExtraModelStatus 共用
      
      前端:
      - 路由 /admin/channels/{pricing,monitor} + /monitor(用户只读)
      - AppSidebar 父项 expandOnly 支持
      - ChannelMonitorView 拆为 8 个子组件 + ChannelStatusView 拆出 detail dialog
      - composables/useChannelMonitorFormat + constants/channelMonitor 共享
      - i18n monitorCommon namespace 消除 admin/user 两 view 重复
      
      合规:所有文件符合 CLAUDE.md(Go ≤ 500 行 / Vue ≤ 300 行 / 函数 ≤ 30 行)
      CI: go build / gofmt / golangci-lint(0 issues) / make test-unit / pnpm build 全绿
      20a4e418
    • IanShaw027's avatar
      feat: rebuild auth identity foundation flow · e9de839d
      IanShaw027 authored
      e9de839d
  12. 14 Apr, 2026 2 commits
    • 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
      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