1. 21 Apr, 2026 4 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
    • erio's avatar
      refactor(channel-status): drop breadcrumb + subtitle from MonitorHero · 0c48f08f
      erio authored
      The "CHANNEL · STATUS" breadcrumb and the zh/en subtitles above the
      window-picker were redundant with the existing "渠道状态" page title
      shown in the layout header. Remove the left column and right-align the
      7d/15d/30d tabs + overall chip.
      
      Also drop the now-unreferenced channelStatus.hero.* i18n keys from both
      locales (grep confirms no remaining usage).
      
      chore: bump version to 0.1.114.31
      0c48f08f
    • erio's avatar
      feat(channel-monitor): preserve upstream error body · b363bff1
      erio authored
      Monitor:
      - callProvider now returns both textPath-extracted text and raw body;
        runCheckForModel uses rawBody on non-2xx so history.message stops being
        "upstream HTTP 503: " with empty body (gjson textPath produces "" for
        error responses like {"error":{"message":"No available accounts..."}})
      - truncateForErrorBody collapses whitespace then caps at 300 bytes
        (monitorErrorBodySnippetMaxBytes); final truncateMessage still enforces
        the 500-byte DB column cap
      
      Frontend:
      - MonitorFormDialog: primary_model input text color and ModelTagInput tags
        now both track form.provider (via new getPlatformTextClass + existing
        getPlatformTagClass with platform prop).
      
      (cherry-picked from 1d3b0418; dropped gateway_handler logging改动,不在本 PR 范围)
      b363bff1
  2. 23 Apr, 2026 1 commit
    • erio's avatar
      fix(channel-monitor): drop soft delete, refactor feature flag to declarative form · ef6ec8a1
      erio authored
      ### 后端修复:日志表不该用软删除
      
      channel_monitor_histories / channel_monitor_daily_rollups 都是日志/聚合表,
      没有恢复需求。110 里加的 SoftDeleteMixin 会让 DELETE 自动变成 UPDATE deleted_at,
      导致行和索引只增不减,徒增磁盘占用和查询成本。
      
      改回分批物理删(参考 OpsCleanupService.deleteOldRowsByID 模板):
      
      - ent schema 移除 SoftDeleteMixin,重新 go generate
      - repo 新增 deleteChannelMonitorBatched 辅助 + 两条 prune SQL 常量
        (WITH batch AS SELECT id LIMIT 5000 → DELETE IN batch)
      - DeleteHistoryBefore / DeleteRollupsBefore 改调分批 raw SQL
      - 移除 ComputeAvailability / ComputeAvailabilityForMonitors / UpsertDailyRollupsFor /
        ListLatestPerModel / ListLatestForMonitorIDs / ListRecentHistoryForMonitors 等
        raw SQL 中的 deleted_at IS NULL 过滤
      - UpsertDailyRollupsFor 的 ON CONFLICT 去掉 deleted_at = NULL 重置
      - migration 111 DROP COLUMN deleted_at + 对应索引(110 已部署但 maintenance
        首跑在次日 02:00,此时尚无业务数据在依赖软删除)
      
      ### 前端重构:feature flag 声明式 + 复用
      
      AppSidebar.vue 里 7 处 `...(flag ? [item] : [])` 样板代码删光,改为 NavItem 加
      featureFlag?: () => boolean | undefined 字段,加一个 applyFeatureFlags 递归
      过滤(含 children)。语义统一为 `!== false`(宽容策略,undefined 时默认显示,
      避免 public settings 未加载完成时菜单闪烁消失 — 对应用户反馈"刷新后菜单消失
      要去保存设置才回来")。
      
      - 集中声明 4 个 flag getter:flagChannelMonitor / flagPayment /
        flagOpsMonitoring / flagAdminPayment
      - 提取 buildSelfNavItems 复用用户端主菜单和管理员"我的账户"子菜单
      - 未来新增开关:在统一位置加一个 flag getter + 给对应 NavItem 加字段
        (不用再动渲染逻辑)
      
      bump 0.1.114.29
      ef6ec8a1
  3. 21 Apr, 2026 1 commit
    • erio's avatar
      feat(channel-monitor): aggregate history to daily rollups + soft delete · 8cf83c98
      erio authored
      明细只保留 1 天,超过 1 天聚合到新表 channel_monitor_daily_rollups(按
      monitor_id/model/bucket_date 维度),聚合保留 30 天。两张表都用 SoftDeleteMixin
      软删除(DELETE 自动改为 UPDATE deleted_at = NOW())。
      
      聚合 + 清理任务由 OpsCleanupService 的 cron 统一调度,与运维监控的清理共享
      schedule(默认 0 2 * * *)和 leader lock。ChannelMonitorRunner 的 cleanupLoop
      被移除,只保留 dueCheckLoop。
      
      读取路径 ComputeAvailability* 改为 UNION 明细(今天 deleted_at IS NULL)+
      聚合(过去 windowDays 天 deleted_at IS NULL),SUM(ok)/SUM(total) 自然加权
      计算可用率,AVG latency 用 SUM(sum_latency_ms)/SUM(count_latency)。
      
      watermark 表 channel_monitor_aggregation_watermark 单行(id=1),记录
      last_aggregated_date,重启后从该日期 +1 继续聚合,首次为 nil 则从
      today - 30d 开始回填,单次最多 35 天上限避免长事务。
      
      raw SQL 的 ListLatestPerModel / ListLatestForMonitorIDs / ListRecentHistoryForMonitors
      都补上 deleted_at IS NULL 过滤(SoftDeleteMixin interceptor 只对 ent query 生效)。
      
      bump version to 0.1.114.28
      
      GroupBadge 在 MonitorKeyPickerDialog 中复用平台主题色 + 倍率/专属倍率
      (顺手优化)。
      8cf83c98
  4. 20 Apr, 2026 5 commits
    • erio's avatar
      feat(channel-monitor): gate UI by feature switch + polish form UX · ba98243c
      erio authored
      - AppSidebar 三处菜单项(管理端渠道监控、用户端/个人页渠道状态)按
        channel_monitor_enabled 条件展开,关闭时隐藏
      - ChannelStatusView setInterval 随开关启停:关闭 clearInterval,
        开启/未知态自动启动,避免禁用功能后仍在轮询
      - MonitorFormDialog provider Select 改为 3 色单选按钮
        (openai=emerald / anthropic=orange / gemini=sky),i18n 文案
        供应商 → 平台 / Provider → Platform
      - MonitorKeyPickerDialog 按钮列表改为 name/key/group 三列表格 +
        搜索框,按 key.group.platform === provider 过滤,避免跨平台误选
      - form.provider 变化时清空 api_key,修复切换平台仍保留旧 key 的
        错配 bug
      - providerPickerClass 抽取到 useChannelMonitorFormat composable,
        统一 emerald/orange/sky 颜色语义,消除硬编码 Tailwind class 重复
      - maskApiKey 工具函数统一(utils/maskApiKey.ts),KeysView 与
        MonitorKeyPickerDialog 共用 slice(0,6)...slice(-4) 策略
      - bump version to 0.1.114.27
      ba98243c
    • erio's avatar
      refactor(channel-monitor): remove INTELLIGENCE MONITOR hero title · 0d01bd90
      erio authored
      Subtitle + breadcrumb already convey context; the giant h1 was visual
      noise. Drops orphan i18n key `channelStatus.hero.title` and shrinks
      hero section vertical padding accordingly.
      
      Bump VERSION to 0.1.114.26
      0d01bd90
    • 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
    • erio's avatar
      feat(channel-monitor): redesign user dashboard as card grid · a1425b45
      erio authored
      Reference check-cx UI: INTELLIGENCE MONITOR hero + 3-column card grid
      with 60-point timeline bars.
      
      Backend:
      - Add PrimaryPingLatencyMs + Timeline[60] to UserMonitorView
      - ListRecentHistoryForMonitors: batch CTE + ROW_NUMBER() window query
      - indexLatestByModel / indexAvailabilityByModel helpers
      
      Frontend:
      - 7 new components: ProviderIcon, MonitorMetricPair, MonitorAvailabilityRow,
        MonitorTimeline, MonitorHero, MonitorCard, MonitorCardGrid
      - ChannelStatusView 381→~180 lines (delegated to subcomponents)
      - AbortController reload concurrency protection
      - HSL 0-120° availability color mapping
      - Replace emoji with Icon component (bolt / globe)
      - i18n: monitorCommon.* shared namespace, channelStatus.hero.*
      
      Bump VERSION to 0.1.114.24
      a1425b45
    • 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
  5. 22 Apr, 2026 14 commits
  6. 21 Apr, 2026 15 commits