1. 29 Dec, 2025 11 commits
  2. 28 Dec, 2025 11 commits
    • IanShaw027's avatar
      test: 修复分组测试以适配默认分组 · 9e9811cb
      IanShaw027 authored
      由于简易模式会自动创建3个默认分组(anthropic-default, openai-default, gemini-default),
      需要更新测试用例的预期数量:
      - TestList: 期望5个分组(3个默认 + 2个测试)
      - TestListActive: 期望4个活跃分组(3个默认 + 1个测试)
      - TestListActiveByPlatform: 期望2个Anthropic分组(1个默认 + 1个测试)
      - TestListWithFilters_Platform: 期望2个OpenAI分组(1个默认 + 1个测试)
      9e9811cb
    • IanShaw027's avatar
      feat(全栈): 实现简易模式核心功能 · ecfad788
      IanShaw027 authored
      **功能概述**:
      实现简易模式(Simple Mode),为个人用户和小团队提供简化的使用体验,隐藏复杂的分组、订阅、配额等概念。
      
      **后端改动**:
      1. 配置系统
         - 新增 run_mode 配置项(standard/simple)
         - 支持环境变量 RUN_MODE
         - 默认值为 standard
      
      2. 数据库初始化
         - 自动创建3个默认分组:anthropic-default、openai-default、gemini-default
         - 默认分组配置:无并发限制、active状态、非独占
         - 幂等性保证:重复启动不会重复创建
      
      3. 账号管理
         - 创建账号时自动绑定对应平台的默认分组
         - 如果未指定分组,自动查找并绑定默认分组
      
      **前端改动**:
      1. 状态管理
         - authStore 新增 isSimpleMode 计算属性
         - 从后端API获取并同步运行模式
      
      2. UI隐藏
         - 侧边栏:隐藏分组管理、订阅管理、兑换码菜单
         - 账号管理页面:隐藏分组列
         - 创建/编辑账号对话框:隐藏分组选择器
      
      3. 路由守卫
         - 限制访问分组、订阅、兑换码相关页面
         - 访问受限页面时自动重定向到仪表板
      
      **配置示例**:
      ```yaml
      run_mode: simple
      
      run_mode: standard
      ```
      
      **影响范围**:
      - 后端:配置、数据库迁移、账号服务
      - 前端:认证状态、路由、UI组件
      - 部署:配置文件示例
      
      **兼容性**:
      - 简易模式和标准模式可无缝切换
      - 不需要数据迁移
      - 现有数据不受影响
      ecfad788
    • shaw's avatar
      fix(usage): 分离 API 响应和窗口统计缓存,修复 5h 窗口未激活时的显示 bug · 30b95cf5
      shaw authored
      问题:
      1. WindowStats 与 API 响应一起缓存 10 分钟,导致费用数据更新延迟
      2. 当 5h 窗口未激活(ResetsAt 为空)时,FiveHour 为 nil,导致所有窗口的 WindowStats 都无法显示
      
      修复:
      - 分离缓存:API 响应缓存 10 分钟,窗口统计独立缓存 1 分钟
      - RemainingSeconds 每次请求时实时计算
      - FiveHour 对象始终创建(即使 ResetsAt 为空)
      - addWindowStats 增强防护,支持 FiveHour 为 nil 时仍处理其他窗口
      30b95cf5
    • shaw's avatar
      fix(test): 测试用例添加 simple_mode 字段 · 25b8a226
      shaw authored
      API 响应新增 simple_mode 字段,同步更新测试期望值
      25b8a226
    • shaw's avatar
      fix: 修复 NewSettingHandler 参数不足导致的编译错误 · 0084da9c
      shaw authored
      - 测试文件添加第三个参数 userService(nil)
      - Handler 添加 userService 空指针检查,防止测试环境 panic
      0084da9c
    • shaw's avatar
      feat: 添加简单模式功能 · 7d4b7dee
      shaw authored
      新增简单模式设置,适合个人使用场景:
      - 隐藏多用户管理相关菜单(用户管理、兑换码等)
      - 自动关闭用户注册功能
      - 管理员并发数自动设为无限制(99999)
      - 侧边栏根据模式动态调整菜单项
      
      同时优化分组页面的"专属分组"功能,添加帮助提示说明使用场景
      7d4b7dee
    • IanShaw027's avatar
      refactor(frontend): UI/UX改进和组件优化 · 506cb21c
      IanShaw027 authored
      - DataTable组件操作列自适应
      - 优化各种Modal弹窗
      - 统一API调用方式(AbortSignal)
      - 添加全局订阅状态管理
      - 优化各管理视图的交互和布局
      - 修复国际化翻译问题
      506cb21c
    • yangjianbo's avatar
      fix: 代码的核心问题是判错条件用错了层级: · fd51ff69
      yangjianbo authored
        - apiKeyService.GetByKey(...) 返回的“找不到 API key”在这个项目里通常会被翻译成业务错误(比如
          service.ErrApiKeyNotFound 这类 ApplicationError),而不是直接把 gorm.ErrRecordNotFound 透传到中
          间件层。
        - 因此你在中间件里用 errors.Is(err, gorm.ErrRecordNotFound) 去判断“无效 key”,很容易匹配不到(尤其
          是:后面加 Redis 缓存、换存储实现、或测试里用 stub repo 时,根本不会出现 gorm 的错误)。
        - 匹配不到时就会走到 500 Failed to validate API key,导致无效 API key 被错误地当成服务端故障返回
          500(应该是 401)。
      
        修复思路:中间件不要依赖 gorm 的错误,改成判断业务层错误,例如:
      
        if errors.Is(err, service.ErrApiKeyNotFound) {
            abortWithGoogleError(c, 401, "Invalid API key")
            return
        }
      
        如果你把 GetByKey 的“not found”统一封装成业务错误,这样才不会被底层实现(gorm/redis/mock)影响。
      fd51ff69
    • shaw's avatar
      fix: 防止订阅过期时间超出 JSON 序列化范围 · fbdff4f3
      shaw authored
      问题:当分配订阅天数过大时,expires_at 年份可能超过 9999,
      导致 time.Time JSON 序列化失败(RFC 3339 要求年份 <= 9999),
      使后台无法显示和删除异常数据。
      
      修复:
      - handler 层添加 validity_days 最大值验证(max=36500,即100年)
      - service 层添加 MaxValidityDays 和 MaxExpiresAt 双重保护
      - 启动时自动修复已存在的异常数据(expires_at > 2099年)
      fbdff4f3
    • shaw's avatar
      cd9d31f5
    • noreply's avatar
      feat: Schedule batch update for account last_used_at · cbfce49a
      noreply authored
      Implement deferred batch update mechanism to reduce database load:
      
      - Add DeferredService for batching account last_used_at updates
      - Add TimingWheelService for efficient recurring task scheduling
      - Integrate with GatewayService and OpenAIGatewayService
      - Implement BatchUpdateLastUsed repository method using CASE...WHEN SQL
      - Fix golangci-lint error: Replace interface{} with any
      
      Benefits:
      - Reduces database writes by batching updates (10-second intervals)
      - Improves request throughput by deferring non-critical updates
      - Maintains accurate account usage tracking for scheduling
      cbfce49a
  3. 27 Dec, 2025 14 commits
    • shaw's avatar
      0b9c4ae6
    • shaw's avatar
      fix: 修复claude token刷新失效的问题 · 0d5a8a95
      shaw authored
      0d5a8a95
    • IanShaw027's avatar
      fix(test): 实现GetUserStatsAggregated方法以支持新的统计查询 · 96bec5c9
      IanShaw027 authored
      - 在stubUsageLogRepo中实现GetUserStatsAggregated方法
      - 根据userLogs计算统计数据而不是返回错误
      - 修复类型转换问题(int转int64)
      96bec5c9
    • IanShaw027's avatar
      cfeb6b8b
    • IanShaw027's avatar
      fix(test): 修复CI测试失败 · 481310de
      IanShaw027 authored
      - 修复gofmt格式问题
      - 为stubUsageLogRepo添加缺失的GetApiKeyStatsAggregated方法
      481310de
    • IanShaw027's avatar
      feat(backend): 增强使用统计和API密钥功能 · 227d506c
      IanShaw027 authored
      - 优化使用统计处理逻辑
      - 增强API密钥仓储层功能
      - 改进账户使用服务
      - 完善API契约测试覆盖
      227d506c
    • IanShaw027's avatar
      perf(backend): 优化数据库查询性能 · 36a86e9a
      IanShaw027 authored
      - 合并多个独立查询为单个SQL查询
      - 减少数据库往返次数
      - 提升仪表板统计数据获取效率
      36a86e9a
    • shaw's avatar
      fix: 修复账号更新时分组绑定操作顺序导致的数据不一致问题 · f1e47291
      shaw authored
      原逻辑先执行 Update 再验证 GroupIDs,如果验证失败会导致账号已更新但返回错误。
      现改为先验证分组是否存在,再执行 Update 和 BindGroups。
      f1e47291
    • IanShaw's avatar
      3f92a431
    • shaw's avatar
      2101f1d1
    • daodao97's avatar
      feat: cc/codex/gemini 增加账号重试 · f0f920e4
      daodao97 authored
      f0f920e4
    • daodao97's avatar
      feat: cc/codex support account retry · 95583fce
      daodao97 authored
      95583fce
    • IanShaw's avatar
      feat(frontend): 前端界面优化与使用统计功能增强 (#46) · 254f1254
      IanShaw authored
      * feat(frontend): 前端界面优化与使用统计功能增强
      
      主要改动:
      
      1. 表格布局统一优化
         - 新增 TablePageLayout 通用布局组件
         - 统一所有管理页面的表格样式和交互
         - 优化 DataTable、Pagination、Select 等通用组件
      
      2. 使用统计功能增强
         - 管理端: 添加完整的筛选和显示功能
         - 用户端: 完善 API Key 列显示
         - 后端: 优化使用统计数据结构和查询
      
      3. 账户组件优化
         - 优化 AccountStatsModal、AccountUsageCell 等组件
         - 统一进度条和统计显示样式
      
      4. 其他改进
         - 完善中英文国际化
         - 统一页面样式和交互体验
         - 优化各视图页面的响应式布局
      
      * fix(test): 修复 stubUsageLogRepo.ListWithFilters 测试 stub
      
      测试用例 GET /api/v1/usage 返回 500 是因为 stub 方法未实现,
      现在正确返回基于 UserID 过滤的日志数据。
      
      * feat(frontend): 统一日期时间显示格式
      
      **主要改动**:
      1. 增强 utils/format.ts:
         - 新增 formatDateOnly() - 格式: YYYY-MM-DD
         - 新增 formatDateTime() - 格式: YYYY-MM-DD HH:mm:ss
      
      2. 全局替换视图中的格式化函数:
         - 移除各视图中的自定义 formatDate 函数
         - 统一导入使用 @/utils/format 中的函数
         - created_at/updated_at 使用 formatDateTime
         - expires_at 使用 formatDateOnly
      
      3. 受影响的视图 (8个):
         - frontend/src/views/user/KeysView.vue
         - frontend/src/views/user/DashboardView.vue
         - frontend/src/views/user/UsageView.vue
         - frontend/src/views/user/RedeemView.vue
         - frontend/src/views/admin/UsersView.vue
         - frontend/src/views/admin/UsageView.vue
         - frontend/src/views/admin/RedeemView.vue
         - frontend/src/views/admin/SubscriptionsView.vue
      
      **效果**:
      - 日期统一显示为 YYYY-MM-DD
      - 时间统一显示为 YYYY-MM-DD HH:mm:ss
      - 提升可维护性,避免格式不一致
      
      * fix(frontend): 补充遗漏的时间格式化统一
      
      **补充修复**(基于 code review 发现的遗漏):
      
      1. 增强 utils/format.ts:
         - 新增 formatTime() - 格式: HH:mm
      
      2. 修复 4 个遗漏的文件:
         - src/views/admin/UsersView.vue
           * 删除 formatExpiresAt(),改用 formatDateTime()
           * 修复订阅过期时间 tooltip 显示格式不一致问题
      
         - src/views/user/ProfileView.vue
           * 删除 formatMemberSince(),改用 formatDate(date, 'YYYY-MM')
           * 统一会员起始时间显示格式
      
         - src/views/user/SubscriptionsView.vue
           * 修改 formatExpirationDate() 使用 formatDateOnly()
           * 保留天数计算逻辑
      
         - src/components/account/AccountStatusIndicator.vue
           * 删除本地 formatTime(),改用 utils/format 中的统一函数
           * 修复 rate limit 和 overload 重置时间显示
      
      **验证**:
      - TypeScript 类型检查通过 ✓
      - 前端构建成功 ✓
      - 所有剩余的 toLocaleString() 都是数字格式化,属于正确用法 ✓
      
      **效果**:
      - 订阅过期时间统一为 YYYY-MM-DD HH:mm:ss
      - 会员起始时间统一为 YYYY-MM
      - 重置时间统一为 HH:mm
      - 消除所有不规范的原生 locale 方法调用
      254f1254
    • IanShaw's avatar
      fix: 修复 Gemini API 认证和 /responses 端点路由问题 (#45) · cf8a6452
      IanShaw authored
      * fix(middleware): 修复 Gemini API Key 认证中间件用户上下文类型错误
      
      修复了 ApiKeyAuthWithSubscriptionGoogle 中间件中设置用户上下文时的类型错误。
      
      **问题:**
      - 中间件直接设置 `apiKey.User` 对象到上下文
      - 导致 handler 中获取 `AuthSubject` 时类型断言失败
      - 所有 Gemini v1beta 端点返回 500 "User context not found"
      
      **修复:**
      - 改为设置 `AuthSubject` 结构体,与 `api_key_auth.go` 保持一致
      - 添加 `ContextKeyUserRole` 设置以完整支持角色检查
      
      **影响范围:**
      - Gemini v1beta API 端点 (generateContent, streamGenerateContent)
      - 使用 Google API Key 认证的所有请求
      
      **测试:**
      - 验证 Gemini CLI 调用成功返回 200
      - 确认用户上下文正确传递到 handler
      
      * fix(web): 修复 /responses 端点被前端中间件拦截的问题
      
      - 将 /responses 路径添加到 API 白名单,防止其被当作前端路由处理
      - 修复 /responses 端点返回 HTML 而非 API 响应的 BUG
      - 解决 codex CLI stream 在远程服务器上断开连接的问题
      
      根本原因:
      在 6c469b42 提交中添加了 /responses 路由,但未同步更新前端嵌入中间件
      的 API 白名单,导致该路由被拦截并返回 index.html 而非 API 响应。
      cf8a6452
  4. 26 Dec, 2025 4 commits
    • IanShaw027's avatar
      feat(test): 添加 Gemini 双响应格式支持 · 2714be99
      IanShaw027 authored
      添加对两种 Gemini 响应格式的支持:
      - AI Studio: `{"candidates": [...]}`
      - Gemini CLI: `{"response": {"candidates": [...]}}`
      
      通过 unwrap 逻辑自动检测并适配两种格式,确保账号测试功能
      对所有 Gemini 账号类型都能正常工作。
      
      合并 PR #43 的剩余功能到 PR #37
      2714be99
    • IanShaw027's avatar
      fix(lint): 修复 gofmt 格式问题 · d8518180
      IanShaw027 authored
      修复 golangci-lint 检查失败的问题:
      - gemini_token_provider.go: 删除 import 后多余空行
      - gemini_token_refresher.go: 删除 import 后多余空行
      
      Fixes CI golangci-lint check for PR #37
      d8518180
    • IanShaw027's avatar
      refactor: 统一使用 mergeMap 函数提升代码一致性 · 576bf463
      IanShaw027 authored
      根据 Gemini CLI 代码审查建议:
      
      ## 修改内容
      - 将 Gemini OAuth 同步中的 `mergeJSONB` 调用替换为 `mergeMap`
      - 删除不再使用的 `mergeJSONB` 函数定义
      
      ## 原因
      - 其他平台(OpenAI、Anthropic)的账户同步都使用 `mergeMap`
      - `mergeJSONB` 是为旧的 `model.JSONB` 类型设计,与重构后的架构不一致
      - 统一函数命名提高代码可读性和可维护性
      
      ## 影响范围
      - backend/internal/service/crs_sync_service.go (4处替换)
      - backend/internal/service/account.go (删除 mergeJSONB 函数)
      
      ## 验证
      ✓ 编译通过
      ✓ 功能逻辑无变化(mergeMap 和 mergeJSONB 实现相同)
      576bf463
    • IanShaw027's avatar
      fix(backend): 适配重构后的架构修复 Gemini OAuth 集成 · 9db52838
      IanShaw027 authored
      ## 主要修改
      
      1. **移除 model 包引用**
         - 删除所有 `internal/model` 包的 import
         - 使用 service 包中的类型定义(Account, Platform常量等)
      
      2. **修复类型转换**
         - JSONB → map[string]any
         - 添加 mergeJSONB 辅助函数
         - 添加 Account.IsGemini() 方法
      
      3. **更新中间件调用**
         - GetUserFromContext → GetAuthSubjectFromContext
         - 适配新的并发控制签名(传递 ID 和 Concurrency 而不是完整对象)
      
      4. **修复 handler 层**
         - 更新 gemini_v1beta_handler.go
         - 修正 billing 检查和 usage 记录
      
      ## 影响范围
      - backend/internal/service/gemini_*.go
      - backend/internal/service/account_test_service.go
      - backend/internal/service/crs_sync_service.go
      - backend/internal/handler/gemini_v1beta_handler.go
      - backend/internal/handler/gateway_handler.go
      - backend/internal/handler/admin/account_handler.go
      9db52838