1. 27 Apr, 2026 4 commits
    • shaw's avatar
      feat(affiliate): 完善邀请返利系统 · 11c06803
      shaw authored and 陈曦's avatar 陈曦 committed
        - 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突
        - 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定
        - 前端 OAuth 注册页面传递 aff_code 参数
        - 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻)
        - 新增返利有效期:绑定后 N 天内有效,过期不再产生返利
        - 新增单人返利上限:超出上限部分精确截断
        - 增强返利流程 slog 结构化日志,便于排查问题
        - 已邀请用户列表增加返利明细列
      11c06803
    • shaw's avatar
      feat(affiliate): add feature toggle and per-user custom invite settings · ae317432
      shaw authored and 陈曦's avatar 陈曦 committed
      - 在系统设置「功能开关」中新增邀请返利总开关,默认关闭;
        关闭态:菜单隐藏、注册忽略 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 位系统码与新自定义码
      - 补充单元测试与集成测试覆盖新方法、冲突路径与边界值
      ae317432
    • shaw's avatar
      fix(payment): 同时启用易支付和 Stripe 时显示 Stripe 按钮 · 631b734e
      shaw authored and 陈曦's avatar 陈曦 committed
      VISIBLE_METHOD_ALIASES 漏了 stripe,导致 getVisibleMethods 把后端返回
      的 stripe 过滤掉。点 Stripe 按钮时省略 method 查询参数,让落地页渲染
      完整的 Payment Element。
      631b734e
    • VpSanta33's avatar
      feat: add affiliate invite rebate flow and admin rebate-rate setting · 2b67e632
      VpSanta33 authored and 陈曦's avatar 陈曦 committed
      2b67e632
  2. 23 Apr, 2026 5 commits
    • erio's avatar
    • erio's avatar
    • erio's avatar
      feat(monitor): proportion-based overall status + reusable auto-refresh · 0dcc0e05
      erio authored
      - Change overall status logic: >50% failed = UNAVAILABLE, any failed
        or degraded = DEGRADED, all ok = OPERATIONAL
      - Extract useAutoRefresh composable with localStorage persistence
      - Create AutoRefreshButton dropdown component (reusable)
      - Integrate auto-refresh into channel status page via MonitorHero
      0dcc0e05
    • 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
  3. 22 Apr, 2026 5 commits
  4. 21 Apr, 2026 6 commits
  5. 22 Apr, 2026 1 commit
    • erio's avatar
      fix(available-channels): description as own column, fixed table layout · 25a50355
      erio authored
      - 描述独立成列:渠道名与描述各占一列,均用 rowspan 纵向合并
      - 渠道名单元格 text-center + align-middle,合并后视觉居中
      - table-fixed:给 name/description/platform 显式宽度,groups 和
        supported_models 在剩余空间均分。支持模型列此前在 table-auto 下
        不会换行导致横向溢出遮挡(反馈截图),加 table-fixed 后天然 flex-wrap
      - i18n 增加 availableChannels.columns.description(zh/en)
      25a50355
  6. 21 Apr, 2026 5 commits
    • erio's avatar
      feat(channels): themed model popover + group-badge with rate, subscription & exclusivity · ff4ef1b5
      erio authored
      Pricing popover:
      - Teleport to body + fixed-position re-measuring on hover/scroll/resize so it
        escapes the card's overflow-hidden clip that was chopping off the lower
        half of the panel.
      - Header + border adopt the platform theme via platformBorderClass /
        platformBadgeLightClass so each model card reads at a glance.
      
      Accessible groups:
      - Backend AvailableGroupRef / user DTO now expose subscription_type,
        rate_multiplier and is_exclusive. User-specific rates continue to come
        from /groups/rates (same pattern as the API keys page).
      - Table renders groups through the shared GroupBadge, which already deepens
        subscription-type colors and shows default vs custom rate as
        <s>default</s> <b>custom</b>.
      - Exclusive groups are labelled with a purple shield row, public groups
        with a grey globe row so admins and users can tell at a glance which
        groups they got via explicit grant vs. public access.
      
      i18n keys for exclusive / public / their tooltips added to zh + en.
      ff4ef1b5
    • erio's avatar
      feat(channels): aggregate by channel with platform sections + rowspan table · 3cdd5754
      erio authored
      Switch the user-facing 'Available Channels' view from "one row per
      platform" to "one channel row-group with N platform sections".
      
      Backend: userAvailableChannel now holds Platforms []section instead
      of being exploded. buildPlatformSections replaces
      explodeChannelByPlatform with the same per-platform grouping logic.
      
      Frontend: drop the DataTable wrapper for this view and write a
      four-column grid table (渠道名 / 平台 / 分组 / 支持模型) where the
      channel name only renders on the first platform row of each channel —
      visual rowspan without hacking DataTable.
      
      - api/channels.ts: UserChannelPlatformSection + platforms[]
      - AvailableChannelsTable: rewritten as native grid (header + per-
        channel section with hover row highlight)
      - AvailableChannelsView: search now filters platforms sub-array;
        channel-name / description hits still keep the whole channel
      - i18n: add availableChannels.columns.platform (zh/en)
      3cdd5754
    • IanShaw027's avatar
      7309c02f
    • IanShaw027's avatar
      9742796e
    • IanShaw027's avatar
      09351e94
  7. 20 Apr, 2026 14 commits
    • IanShaw027's avatar
      fix payment resume result consistency · a27a7add
      IanShaw027 authored
      a27a7add
    • IanShaw027's avatar
      fix frontend wechat oauth capability recovery · cd0338fb
      IanShaw027 authored
      cd0338fb
    • IanShaw027's avatar
      c297d011
    • IanShaw027's avatar
      067eb23d
    • IanShaw027's avatar
      Tighten WeChat payment resume flow · 55e8dd55
      IanShaw027 authored
      55e8dd55
    • 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
    • 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
    • IanShaw027's avatar
      16be82b9
    • IanShaw027's avatar
      Refine payment UX for wallet flows · f83fd59d
      IanShaw027 authored
      f83fd59d
    • 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
    • IanShaw027's avatar
      7ef7fd19
    • IanShaw027's avatar
      feat: resolve payment results by resume token · 9bebf1c1
      IanShaw027 authored
      9bebf1c1
    • erio's avatar
      feat(payment): i18n payment error codes and label localization · 40d4e167
      erio authored
      Pairs with the backend structured payment errors (reason + metadata). The
      frontend now maps reason codes to localized messages with metadata as
      interpolation variables, and automatically localizes raw config-field names
      (e.g. "certSerial" → "证书序列号") using the existing UI-label i18n
      namespace.
      
      - frontend/src/utils/apiError.ts
        - extractApiErrorCode now prefers the string `reason` over the numeric HTTP
          `code`; reason is granular enough to drive i18n lookup, HTTP code is not.
        - New extractApiErrorMetadata to pull interpolation params off the error.
        - New extractI18nErrorMessage(err, t, namespace, fallback): looks up
          `<namespace>.<REASON>` in i18n and substitutes metadata. Before
          substitution, `metadata.key` and `metadata.keys` (slash-joined) are
          re-translated through `admin.settings.payment.field_<key>` so users see
          "缺少必填项:证书序列号" instead of "缺少必填项:certSerial".
      
      - frontend/src/i18n/locales/{zh,en}.ts
        - Add payment.errors entries for every structured reason code returned by
          the backend (PAYMENT_DISABLED, INVALID_AMOUNT, TOO_MANY_PENDING,
          DAILY_LIMIT_EXCEEDED, NO_AVAILABLE_INSTANCE, PAYMENT_PROVIDER_MISCONFIGURED,
          WXPAY_CONFIG_MISSING_KEY / INVALID_KEY_LENGTH / INVALID_KEY, NOT_FOUND,
          FORBIDDEN, CONFLICT, INVALID_ORDER_TYPE, INVALID_STATUS,
          BALANCE_NOT_ENOUGH, REFUND_AMOUNT_EXCEEDED, REFUND_FAILED, and more),
          with placeholders for template variables.
      
      - 13 payment-related Vue files
        - Migrate catch-block error reporting from extractApiErrorMessage to
          extractI18nErrorMessage(err, t, 'payment.errors', fallback).
        - Remove the ad-hoc paymentErrorMap computed in SettingsView.vue, which the
          new helper supersedes (it reads i18n directly via t).
      
      - frontend/src/components/payment/providerConfig.ts
        - wxpay: publicKey and publicKeyId are now required (was optional), matching
          the pubkey-only verifier direction; certSerial is already required.
      
      This PR is drop-in safe: reason-preferring extractApiErrorCode is backward
      compatible with callers that pass their own i18nMap, and error codes missing
      from i18n fall back to the existing message-based path.
      40d4e167
    • IanShaw027's avatar
      test: harden payment result resume flow · 58b2cc38
      IanShaw027 authored
      58b2cc38