1. 05 Feb, 2026 1 commit
    • JIA-ss's avatar
      feat(gateway): filter /v1/usage stats by API Key instead of UserID · fa3ea5ee
      JIA-ss authored
      
      
      Previously the /v1/usage endpoint aggregated usage stats (today/total
      tokens, cost, RPM/TPM) across all API Keys belonging to the user.
      This made it impossible to distinguish usage from different API Keys
      (e.g. balance vs subscription keys).
      
      Now the usage stats are filtered by the current request's API Key ID,
      so each key only sees its own usage data. The balance/remaining fields
      are unaffected and still reflect the user-level wallet balance.
      
      Changes:
      - Add GetAPIKeyDashboardStats to repository interface and implementation
      - Add getPerformanceStatsByAPIKey helper (also fixes TPM to include
        cache_creation_tokens and cache_read_tokens)
      - Add GetAPIKeyDashboardStats to UsageService
      - Update Usage handler to call GetAPIKeyDashboardStats(apiKey.ID)
      Co-Authored-By: default avatarClaude Opus 4.5 <noreply@anthropic.com>
      fa3ea5ee
  2. 03 Feb, 2026 9 commits
  3. 02 Feb, 2026 9 commits
    • bayma888's avatar
      feat(admin): add user balance/concurrency history modal · 606e29d3
      bayma888 authored
      - Add new API endpoint GET /admin/users/:id/balance-history with pagination and type filter
      - Add SumPositiveBalanceByUser for calculating total recharged amount
      - Create UserBalanceHistoryModal component with:
        - User info header (email, username, created_at, current balance, notes, total recharged)
        - Type filter dropdown (all/balance/admin_balance/concurrency/admin_concurrency/subscription)
        - Quick deposit/withdraw buttons
        - Paginated history list with icons and colored values
      - Add instant tooltip on balance column for better UX
      - Add z-index prop to BaseDialog for modal stacking control
      - Update i18n translations (zh/en)
      606e29d3
    • song's avatar
      chore: apply stashed changes · 3ecadf4a
      song authored
      3ecadf4a
    • song's avatar
      merge upstream main · 0170d19f
      song authored
      0170d19f
    • liuxiongfeng's avatar
      feat(groups): 添加从其他分组复制账号功能 · e1a4a7b8
      liuxiongfeng authored
      - 创建分组时可选择从已有分组复制账号
      - 编辑分组时支持同步账号(全量替换操作)
      - 仅允许选择相同平台的源分组
      - 添加完整的数据校验:去重、自引用检查、平台一致性检查
      - 前端支持多选源分组,带提示说明操作行为
      e1a4a7b8
    • liuxiongfeng's avatar
      feat(billing): 添加 Gemini 200K 长上下文双倍计费功能 · 45e1429a
      liuxiongfeng authored
      - 新增 CalculateCostWithLongContext 方法支持阈值双倍计费
      - 新增 RecordUsageWithLongContext 方法专用于 Gemini 计费
      - Gemini 超过 200K token 的部分按 2 倍费率计算
      - 其他平台(Claude/OpenAI)完全不受影响
      45e1429a
    • Zero Clover's avatar
      feat(ops): 支持过滤无效 API Key 错误,不写入错误日志 · ad1cdba3
      Zero Clover authored
      新增 IgnoreInvalidApiKeyErrors 开关,启用后 INVALID_API_KEY 和
      API_KEY_REQUIRED 错误将被完全跳过,不写入 Ops 错误日志。
      这些错误由用户错误配置导致,与服务质量无关。
      ad1cdba3
    • Zero Clover's avatar
      feat(ops): 将 USER_INACTIVE 错误排除在 SLA 统计之外 · 673caf41
      Zero Clover authored
      将账户停用 (USER_INACTIVE) 导致的请求失败视为业务限制类错误,不计入 SLA 和错误率统计。
      
      账户停用是预期内的业务结果,不应被视为系统错误或服务质量问题。此改动使错误分类更加准确,避免将预期的业务限制误报为系统故障。
      
      修改内容:
      - 在 classifyOpsIsBusinessLimited 函数中添加 USER_INACTIVE 错误码
      - 该类错误不再触发错误率告警
      
      Fixes Wei-Shaw/sub2api#453
      673caf41
    • JIA-ss's avatar
      feat(gateway): 增强 /v1/usage 端点返回完整用量统计 · c441638f
      JIA-ss authored
      
      
      为 CC Switch 集成增强 /v1/usage 网关端点,在保持原有 4 字段
      (isValid, planName, remaining, unit) 向后兼容的基础上,新增:
      
      - usage 对象:今日/累计的请求数、token 用量、费用,以及 RPM/TPM
      - subscription 对象(订阅模式):日/周/月用量和限额、过期时间
      - balance 字段(余额模式):当前钱包余额
      
      用量数据获取采用 best-effort 策略,失败不影响基础响应。
      Co-Authored-By: default avatarClaude Opus 4.5 <noreply@anthropic.com>
      c441638f
    • 小北's avatar
      feat: 向用户显示管理员调整余额的备注 · ae18397c
      小北 authored
      - 为RedeemCode DTO添加notes字段(仅用于admin_balance/admin_concurrency类型)
      - 更新mapper使其有条件地包含备注信息
      - 在用户兑换历史UI中显示备注
      - 备注以斜体显示,悬停时显示完整内容
      
      用户现在可以看到管理员调整其余额的原因说明。
      
      Changes:
      - backend/internal/handler/dto/types.go: RedeemCode添加notes字段
      - backend/internal/handler/dto/mappers.go: 条件性填充notes
      - frontend/src/api/redeem.ts: TypeScript接口添加notes
      - frontend/src/views/user/RedeemView.vue: UI显示备注信息
      ae18397c
  4. 30 Jan, 2026 2 commits
  5. 28 Jan, 2026 1 commit
  6. 27 Jan, 2026 3 commits
  7. 26 Jan, 2026 1 commit
    • shaw's avatar
      feat(auth): 实现 TOTP 双因素认证功能 · 1245f07a
      shaw authored
      新增功能:
      - 支持 Google Authenticator 等应用进行 TOTP 二次验证
      - 用户可在个人设置中启用/禁用 2FA
      - 登录时支持 TOTP 验证流程
      - 管理后台可全局开关 TOTP 功能
      
      安全增强:
      - TOTP 密钥使用 AES-256-GCM 加密存储
      - 添加 TOTP_ENCRYPTION_KEY 配置项,必须手动配置才能启用功能
      - 防止服务重启导致加密密钥变更使用户无法登录
      - 验证失败次数限制,防止暴力破解
      
      配置说明:
      - Docker 部署:在 .env 中设置 TOTP_ENCRYPTION_KEY
      - 非 Docker 部署:在 config.yaml 中设置 totp.encryption_key
      - 生成密钥命令:openssl rand -hex 32
      1245f07a
  8. 25 Jan, 2026 1 commit
    • ianshaw's avatar
      feat(gemini): 支持 Gemini CLI 粘性会话与跨账号 thoughtSignature 清理 · 839975b0
      ianshaw authored
      ## 问题背景
      
      1. Gemini CLI 没有明确的会话标识(如 Claude Code 的 metadata.user_id)
      2. thoughtSignature 与具体上游账号强绑定,跨账号使用会导致 400 错误
      3. 粘性会话切换账号或 cache 丢失时,旧签名会导致请求失败
      
      ## 解决方案
      
      ### 1. Gemini CLI 会话标识提取
      
      - 从 `x-gemini-api-privileged-user-id` header 和请求体中的 tmp 目录哈希生成会话标识
      - 组合策略:SHA256(privileged-user-id + ":" + tmp_dir_hash)
      - 正则提取:`/\.gemini/tmp/([A-Fa-f0-9]{64})`
      
      ### 2. 跨账号 thoughtSignature 清理
      
      实现三种场景的智能清理:
      
      1. **Cache 命中 + 账号切换**
         - 粘性会话绑定的账号与当前选择的账号不同时清理
      
      2. **同一请求内 failover 切换**
         - 通过 sessionBoundAccountID 跟踪,检测重试时的账号切换
      
      3. **Gemini CLI + Cache 未命中 + 含签名**
         - 预防性清理,避免 cache 丢失后首次转发就 400
         - 仅对 Gemini CLI 请求且请求体包含 thoughtSignature 时触发
      
      ## 修改内容
      
      ### backend/internal/handler/gemini_v1beta_handler.go
      - 添加 `extractGeminiCLISessionHash` 函数提取 Gemini CLI 会话标识
      - 添加 `isGeminiCLIRequest` 函数识别 Gemini CLI 请求
      - 实现账号切换检测与 thoughtSignature 清理逻辑
      - 添加 `geminiCLITmpDirRegex` 正则表达式
      
      ### backend/internal/service/gateway_service.go
      - 添加 `GetCachedSessionAccountID` 方法查询粘性会话绑定的账号 ID
      
      ### backend/internal/service/gemini_native_signature_cleaner.go (新增)
      - 实现 `CleanGeminiNativeThoughtSignatures` 函数
      - 递归清理 JSON 中的所有 thoughtSignature 字段
      - 支持任意 JSON 顶层类型(object/array)
      
      ### backend/internal/handler/gemini_cli_session_test.go (新增)
      - 测试 Gemini CLI 会话哈希提取逻辑
      - 测试 tmp 目录正则匹配
      - 覆盖有/无 privileged-user-id 的场景
      
      ## 影响范围
      
      - 修复 Gemini CLI 多轮对话时账号切换导致的 400 错误
      - 提高粘性会话的稳定性和容错能力
      - 不影响其他客户端(Claude Code 等)的会话标识生成
      
      ## 测试
      
      - 单元测试:go test -tags=unit ./internal/handler -run TestExtractGeminiCLISessionHash
      - 单元测试:go test -tags=unit ./internal/handler -run TestGeminiCLITmpDirRegex
      - 编译验证:go build ./cmd/server
      839975b0
  9. 24 Jan, 2026 3 commits
    • Ubuntu's avatar
      fix(oauth): 彻底修复 project_id 丢失问题 · 716272a1
      Ubuntu authored
      
      
      根本原因:
      - BuildAccountCredentials 只在 project_id 非空时才添加该字段
      - LoadCodeAssist 失败时返回空字符串 → 新 credentials 不包含 project_id 键
      - 普通合并逻辑只保留新 credentials 中不存在的键,无法覆盖空值
      
      解决方案:
      1. 在合并后特殊处理 project_id:如果新值为空但旧值非空,保留旧值
      2. LoadCodeAssist 失败不再返回错误,只记录警告
      3. Token 刷新成功(access_token 已更新)就不应标记账户为 error
      
      改进效果:
      - 即使 LoadCodeAssist 连续失败,已有的 project_id 也不会丢失
      - 避免因临时网络问题将账户误标记为不可用
      - 允许在下次刷新时自动重试获取 project_id
      Co-Authored-By: default avatarClaude Sonnet 4.5 <noreply@anthropic.com>
      716272a1
    • shaw's avatar
      feat(auth): 密码重置邮件队列化与限流优化 · 9cc83525
      shaw authored
      - 邮件发送改为异步队列处理,避免并发导致发送失败
      - 新增 Email 维度限流(30秒冷却期),防止邮件轰炸
      - Token 验证使用常量时间比较,防止时序攻击
      - 重构代码消除冗余,提取公共验证逻辑
      9cc83525
    • shaw's avatar
      feat(subscription): 订阅过期状态自动更新与服务端排序 · b0aa2354
      shaw authored
      - 新增 SubscriptionExpiryService 定时任务,每分钟更新过期订阅状态
      - 订阅列表支持服务端排序(按过期时间、状态、创建时间)
      - 实时显示正确的过期状态,无需等待定时任务
      - 允许对已过期订阅进行续期操作
      - DataTable 组件支持 serverSideSort 模式
      b0aa2354
  10. 23 Jan, 2026 2 commits
    • song's avatar
      Add invalid-request fallback routing · fd0370c0
      song authored
      fd0370c0
    • shaw's avatar
      feat(gateway): 增加 SUGGESTION MODE 请求拦截 · 4a0fe3b1
      shaw authored
      扩展现有的预热请求拦截功能,新增对 SUGGESTION MODE 请求的拦截:
      - 检测 messages 最后一条 user 消息是否以 [SUGGESTION MODE: 开头
      - 拦截后返回空内容响应,节省 token 消耗
      - 重构检测逻辑,合并为单一函数,只解析一次 JSON
      4a0fe3b1
  11. 22 Jan, 2026 1 commit
  12. 20 Jan, 2026 3 commits
  13. 19 Jan, 2026 4 commits