1. 21 Apr, 2026 2 commits
    • 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. 23 Apr, 2026 4 commits
  6. 22 Apr, 2026 27 commits