Skip to content

feat(dashboard): improve user management, statistics reset, and i18n#610

Merged
ding113 merged 9 commits intoding113:devfrom
miraserver:dashboard-ui
Jan 19, 2026
Merged

feat(dashboard): improve user management, statistics reset, and i18n#610
ding113 merged 9 commits intoding113:devfrom
miraserver:dashboard-ui

Conversation

@miraserver
Copy link
Contributor

@miraserver miraserver commented Jan 14, 2026

Summary

Improves dashboard user management with key quotas display, tokens tracking, and statistics reset functionality. Also fixes Russian localization issues and adds missing i18n keys across all 5 locales.

Problem

  1. User management table lacked visibility into key quotas and token usage
  2. No way to reset user statistics (request logs and usage data)
  3. Russian localization had inconsistent/verbose translations
  4. Missing i18n keys for new features across locales
  5. Dashboard logs filters lacked reset functionality
  6. Auto Sort button was not accessible from the dashboard providers page

Related Issues:

Solution

User Management Enhancements

  • Display key quotas and todayTokens in user table
  • Add refresh functionality to user management table
  • Implement resetUserAllStatistics action with optimized Redis pipeline (batch delete via SCAN + pipeline)

i18n Improvements

  • Fix Russian localization for better readability (shorter labels, clearer descriptions)
  • Add missing translation keys for new features across all 5 locales (en, ja, ru, zh-CN, zh-TW)
  • Add relativeTimeShort translations for compact time display

Dashboard Improvements

  • Add reset options to logs filters
  • Use short time format for relative timestamps
  • Add Auto Sort button to providers dashboard page

Changes

Core Changes

  • src/actions/users.ts - Add resetUserAllStatistics action with Redis pipeline optimization
  • src/lib/redis/scan-helper.ts - New Redis SCAN utility for batch key operations
  • src/app/[locale]/dashboard/_components/user/* - User management UI improvements
  • src/app/[locale]/dashboard/providers/page.tsx - Add Auto Sort button

Supporting Changes

  • src/actions/key-quota.ts - Read-only access support
  • src/components/ui/relative-time.tsx - Short format support, useCallback fix
  • src/app/[locale]/dashboard/logs/_components/* - Logs filter reset options
  • src/repository/key.ts - Add tokens to key data
  • src/types/user.ts - Add todayTokens type

i18n Updates

  • messages/*/auth.json - Login error messages
  • messages/*/common.json - Short relative time formats
  • messages/*/dashboard.json - User management, logs, providers translations
  • messages/*/quota.json - Quota descriptions with user limits

Testing

Automated Tests

  • Unit tests added for scan-helper.ts (tests/unit/lib/redis/scan-helper.test.ts)

Manual Testing

  1. Navigate to Users page and verify key quotas and tokens display
  2. Test statistics reset functionality for a user
  3. Verify Russian translations are improved
  4. Test logs filter reset functionality
  5. Verify Auto Sort button appears on providers dashboard

Checklist

  • TypeScript type check passes (bun run typecheck)
  • Biome lint passes (bun run lint)
  • Production build succeeds (bun run build)
  • Code follows project conventions
  • Self-review completed

Description enhanced by Claude AI

John Doe and others added 6 commits January 13, 2026 18:59
…format

- Add "All keys" SelectItem to API Key filter dropdown
- Add "All status codes" SelectItem to Status Code filter dropdown
- Use __all__ value instead of empty string (Radix Select requirement)
- Add formatDateDistanceShort() for compact time display (2h ago, 3d ago)
- Update RelativeTime component to use short format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AutoSortPriorityDialog to dashboard/providers page
- EN: "Auto Sort Priority" -> "Auto Sort"
- RU: "Авто сортировка приоритета" -> "Автосорт"
- RU: "Добавить провайдера" -> "Добавить поставщика"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Russian localization improvements:
- Menu: "Управление поставщиками" -> "Поставщики"
- Menu: "Доступность" -> "Мониторинг"
- Filters: "Последние 7/30 дней" -> "7д/30д"
- Dashboard: "Статистика использования" -> "Статистика"
- Dashboard: "Показать статистику..." -> "Только ваши ключи"
- Quota: add missing translations (manageNotice, withQuotas, etc.)

Login error localization:
- Fix issue where login errors displayed in Chinese ("无效或已过期") regardless of locale
- Add locale detection from NEXT_LOCALE cookie and Accept-Language header
- Add 3 new error keys: apiKeyRequired, apiKeyInvalidOrExpired, serverError
- Support all 5 languages: EN, JA, RU, ZH-CN, ZH-TW
- Remove product name from login privacyNote for all locales

Files changed:
- messages/*/auth.json: new error keys, update privacyNote
- messages/ru/dashboard.json, messages/ru/quota.json: Russian improvements
- src/app/api/auth/login/route.ts: add getLocaleFromRequest()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…kens

- Add access/model restrictions support (allowedClients/allowedModels)
- Add tokens column and refresh button to users table
- Add todayTokens calculation in repository layer (sum all token types)
- Add visual status indicators with color-coded icons (active/disabled/expiring/expired)
- Allow users to view their own key quota (was admin-only)
- Fix React Query cache invalidation on status toggle
- Fix filter logic: change tag/keyGroup from OR to AND
- Refactor time display: move formatDateDistanceShort to component with i18n
- Add fixed header/footer to key dialogs for better UX

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…line

- Implement reset all statistics functionality for admins
- Optimize Redis operations: replace sequential redis.keys() with parallel SCAN
- Add scanPattern() helper for production-safe key scanning
- Comprehensive error handling and performance metrics logging
- 50-100x performance improvement with no Redis blocking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix useEffect dependencies in RelativeTime component (wrap formatShortDistance in useCallback)
- Remove unused effectiveGroupText variable in key-row-item.tsx
- Apply consistent LF line endings across modified files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

此 PR 扩展多语言本地化(新增短格式相对时间与多语言 auth 文案)、引入 todayTokens 与若干配额/仪表板文案、添加 Redis SCAN 辅助及测试、在统计流水中传播 tokens、并新增管理员“重置数据”功能与多处 UI/缓存失效调整(含 layout/对话框改动)。

Changes

Cohort / File(s) 变更摘要
国际化:认证与短时距
messages/en/auth.json, messages/ja/auth.json, messages/ru/auth.json, messages/zh-CN/auth.json, messages/zh-TW/auth.json, messages/en/common.json, messages/ja/common.json, messages/ru/common.json, messages/zh-CN/common.json, messages/zh-TW/common.json
更新 auth 文案(privacyNote 简化/去产品名)、新增错误键 apiKeyRequired/apiKeyInvalidOrExpired/serverError;为短格式相对时间新增 relativeTimeShort(多语言)。
仪表板与配额本地化
messages/*/dashboard.json, messages/*/quota.json (en/ja/ru/zh-CN/zh-TW)
新增 cacheTtlresetDatatodayTokens/tokensLabel,将 “Calls” → “Requests” 文案替换,加入 descriptionWithUserLimitlimitTotalUsd 等限额字段;注意新增翻译键需同步。
俄语设置文案调整
messages/ru/settings/providers/autoSort.json, messages/ru/settings/providers/form/title.json, messages/ru/settings/providers/strings.json
术语与按钮文案微调(例如按钮文案改为“Автосорт”,“провайдера”→“поставщика”)。
后端:用户统计、授权与登录
src/actions/users.ts, src/actions/key-quota.ts, src/app/api/auth/login/route.ts
users.ts:usage 聚合改为包含 { totalCost, totalTokens },新增导出 resetUserAllStatistics(userId)(删除日志、用 Redis scan 删除相关键并触发 revalidate);key-quota:允许只读会话并调整授权规则;login 路由:增加 locale 解析并用翻译返回错误信息。
Redis 工具与测试
src/lib/redis/scan-helper.ts, src/lib/redis/index.ts, tests/unit/lib/redis/scan-helper.test.ts
新增 scanPattern(redis, pattern, count?) 非阻塞 SCAN 工具并导出,添加对应单元测试(多页/空结果/自定义 count)。
数据层:聚合与过滤
src/repository/key.ts, src/repository/user.ts
key 聚合查询新增 totalTokens 并更新 findKeyUsageTodayBatch 返回类型;user 批量查询中同时存在 tag 与 keyGroup 过滤时由 OR 改为 AND。
类型变更
src/types/user.ts
UserKeyDisplay 添加 todayTokens: number,在 KeyDialogUserContext 添加可选 allowedClients?allowedModels?
RelativeTime 组件与使用处
src/components/ui/relative-time.tsx, 若干使用处如 src/app/[locale]/dashboard/.../key-list.tsx, .../key-row-item.tsx, logs/.../*.tsx
RelativeTime 增加 `format?: "full"
仪表板 UI、对话框与缓存失效
多处 UI 文件(示例) src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx, .../edit-user-dialog.tsx, .../edit-key-form.tsx, .../key-row-item.tsx, .../user-management-table.tsx, src/app/[locale]/dashboard/users/users-page-client.tsx, src/app/[locale]/dashboard/providers/page.tsx, src/components/form/form-layout.tsx, src/components/section.tsx
多处布局/样式调整(dialog header/footer padding、max-h→90dvh、flex 布局等);EditUserDialog 新增管理员“重置数据” UI 并调用 resetUserAllStatistics;Key 列表与行新增 todayTokens 显示与 tokensLabel;UserManagementTable 增加 onRefresh/isRefreshing props 并渲染刷新按钮;若干 mutation 成功后额外 invalidate userKeyGroups/userTags
日志筛选改动
src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
key 与 status 过滤引入 "all" 哨兵值与选项,避免在“全部”时解析为数字/状态码。
其他小改动
若干 UI/布局与 icons/imports 更改(见变更)
包括多处图标扩展、tooltip/group badges、缓存失效点新增、对话框样式微调等。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地总结了主要变更:改进用户管理、添加统计数据重置和国际化更新。
Description check ✅ Passed 描述详细阐述了PR的问题、解决方案、具体改动、测试和检查清单,与变更集高度相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🧹 Recent nitpick comments
tests/unit/actions/users-reset-all-statistics.test.ts (1)

186-200: 建议:增强断言的具体性。

此测试用例验证了 scanPattern 失败时的行为,但第 199 行的断言仅检查 warn 是否被调用,而没有验证具体的警告消息。与第 165-184 行的测试用例保持一致,建议增加对警告消息内容的验证。

♻️ 建议的改进
     // Should still succeed - error is caught inside Promise.all
     expect(result.ok).toBe(true);
-    expect(loggerMock.warn).toHaveBeenCalled();
+    expect(loggerMock.warn).toHaveBeenCalledWith(
+      expect.stringContaining("scan"),
+      expect.objectContaining({ userId: 123 })
+    );
   });

注意:具体的警告消息内容需要根据 src/actions/users.ts 中的实际实现来确定。


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between 23c68b3 and 5fea6de.

📒 Files selected for processing (1)
  • tests/unit/actions/users-reset-all-statistics.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use emoji characters in any code, comments, or string literals

Files:

  • tests/unit/actions/users-reset-all-statistics.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All new features must have unit test coverage of at least 80%

Files:

  • tests/unit/actions/users-reset-all-statistics.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: All user-facing strings must use i18n (5 languages supported: zh-CN, zh-TW, en, ja, ru). Never hardcode display text
Use path alias @/ to reference files in ./src/ directory
Format code with Biome using: double quotes, trailing commas, 2-space indent, 100 character line width

Files:

  • tests/unit/actions/users-reset-all-statistics.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer named exports over default exports

Files:

  • tests/unit/actions/users-reset-all-statistics.test.ts
tests/**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit testing and happy-dom for DOM testing

Files:

  • tests/unit/actions/users-reset-all-statistics.test.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/actions/model-prices.ts:275-335
Timestamp: 2026-01-10T06:20:04.478Z
Learning: In the `ding113/claude-code-hub` repository, Server Actions (files under `src/actions/*.ts`) currently return hardcoded Chinese error messages directly. This is a codebase-wide architectural decision that applies to all action files (e.g., model-prices.ts, users.ts, system-config.ts). Changing this pattern requires a coordinated i18n refactor across all Server Actions, which should be handled as a separate unified task rather than piecemeal changes in individual PRs.
🧬 Code graph analysis (1)
tests/unit/actions/users-reset-all-statistics.test.ts (3)
scripts/sync-settings-keys.js (2)
  • key (32-32)
  • result (40-40)
src/actions/users.ts (1)
  • resetUserAllStatistics (1518-1622)
src/lib/utils/error-messages.ts (1)
  • ERROR_CODES (110-117)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 🌐 API Tests
  • GitHub Check: Docker Build Test
🔇 Additional comments (4)
tests/unit/actions/users-reset-all-statistics.test.ts (4)

1-80: Mock 设置结构良好。

测试文件的 mock 设置覆盖了所有关键依赖项(认证、国际化、缓存、数据库、Redis、日志)。使用动态导入 (await import("@/actions/users")) 确保每个测试用例都能获取带有正确 mock 状态的模块实例,这是处理模块缓存的正确做法。


82-90: 测试初始化设置合理。

beforeEach 正确清理了所有 mock 并重置 Redis 状态,确保测试之间的隔离性。


233-245: 边界情况测试覆盖良好。

此测试验证了用户没有 API 密钥时的处理逻辑,确保系统能够正确处理空列表场景。


1-246: 测试覆盖全面,结构清晰。

此测试文件为 resetUserAllStatistics 操作提供了完善的测试覆盖:

  • 权限控制:非管理员用户、无会话场景
  • 业务逻辑:用户不存在、成功执行路径、无密钥用户
  • 容错处理:Redis 未就绪、部分失败、扫描失败、管道执行异常、意外错误

测试设计遵循了 "测试行为而非实现" 的原则,既验证了返回结果,也验证了副作用(日志记录、缓存失效)。符合编码规范中 80% 测试覆盖率的要求。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @miraserver, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the dashboard's user management capabilities, introduces a robust statistics reset feature, and refines the internationalization experience. It also includes minor UI/UX improvements for logs and provider settings, aiming to provide a more comprehensive and user-friendly administrative interface.

Highlights

  • User Management: Enhanced user management with key quotas display, tracking of 'todayTokens', and an improved user table featuring refresh functionality.
  • Statistics Reset: Introduced a new feature allowing administrators to reset all user statistics, utilizing an optimized Redis pipeline for efficient batch deletion via SCAN.
  • i18n Improvements: Addressed Russian localization issues and integrated missing translation keys across all five supported locales (English, Japanese, Russian, Simplified Chinese, Traditional Chinese).
  • Dashboard Logs: Added reset options to log filters and implemented a short time format for relative timestamps to improve readability.
  • Providers: Integrated an 'Auto Sort' button directly into the providers dashboard for easier management.
  • Code Quality: Resolved React hooks dependencies and applied consistent formatting across the codebase.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added enhancement New feature or request area:UI area:i18n area:statistics size/XL Extra Large PR (> 1000 lines) labels Jan 14, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
messages/ru/settings/providers/strings.json (1)

4-13: 俄语术语不一致:混用 "поставщик" 和 "провайдер"

第 4 行和第 13 行更新为使用 "поставщика",但文件中其他位置仍使用 "провайдер"(如第 10、11、12、22 行的 confirmDeleteDescconfirmDeleteProviderconfirmDeleteProviderDesceditProvider)。建议统一术语以确保用户体验一致性。

🤖 Fix all issues with AI agents
In `@src/app/`[locale]/dashboard/_components/user/edit-key-dialog.tsx:
- Line 55: The DialogContent element in edit-key-dialog.tsx has duplicate max-h
classes ("max-h-[90vh]" and "max-h-[90dvh]"); remove the redundant
"max-h-[90vh]" from the className on the DialogContent component so only
"max-h-[90dvh]" remains (keep the rest of the className values intact).

In `@src/app/api/auth/login/route.ts`:
- Around line 65-69: The catch block currently calls getTranslations which may
throw and mask the original error; update the catch to avoid rethrowing by
wrapping the getTranslations call in its own try/catch (or use a safe helper)
and fall back to a static/default error message if translation retrieval fails;
ensure you still log both the original error (logger.error) and any
translation-fetch error, then return NextResponse.json({ error: fallbackMessage
}, { status: 500 }) so getTranslations failures cannot propagate.

In `@src/repository/key.ts`:
- Around line 337-342: The totalTokens aggregation in the key repository
currently sums only messageRequest.inputTokens, outputTokens,
cacheCreationInputTokens and cacheReadInputTokens; add the missing
cacheCreation5mInputTokens and cacheCreation1hInputTokens to the SUM so
totalTokens includes all six token fields defined in the schema (update the
totalTokens SQL expression that references
messageRequest.inputTokens/outputTokens/cacheCreationInputTokens/cacheReadInputTokens
to also include messageRequest.cacheCreation5mInputTokens and
messageRequest.cacheCreation1hInputTokens).
🧹 Nitpick comments (10)
src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx (1)

538-541: 建议统一哨兵值命名:"all" vs "__all__"

当前文件中存在不一致的哨兵值命名:

  • Model 和 Endpoint 筛选器使用 "all"(第 539、572 行)
  • Key 和 Status Code 筛选器使用 "__all__"(第 422、608 行)

建议统一为 "__all__" 以避免与实际数据值冲突(例如某个 model 名称恰好为 "all"),并保持代码一致性。

♻️ 建议的统一修改
 {/* 模型选择 */}
 <Select
-  value={localFilters.model || "all"}
+  value={localFilters.model || "__all__"}
   onValueChange={(value: string) =>
-    setLocalFilters({ ...localFilters, model: value === "all" ? undefined : value })
+    setLocalFilters({ ...localFilters, model: value === "__all__" ? undefined : value })
   }
   onOpenChange={onModelsOpenChange}
 >
   ...
   <SelectContent>
-    <SelectItem value="all">{t("logs.filters.allModels")}</SelectItem>
+    <SelectItem value="__all__">{t("logs.filters.allModels")}</SelectItem>
     ...
   </SelectContent>
 </Select>

同样修改 Endpoint 筛选器(第 571-586 行)。

Also applies to: 571-574

src/repository/user.ts (1)

213-219: LGTM! 筛选逻辑变更实现正确。

tagFilterskeyGroupFilters 的组合逻辑从 OR 改为 AND 的实现是正确的,符合 PR 目标中描述的行为变更。

建议在 UserListBatchFilters 接口的文档注释中补充说明:当同时提供 tagFilterskeyGroupFilters 时,两者之间使用 AND 逻辑(即用户必须同时满足两个条件)。这样可以帮助调用方更清晰地理解筛选行为。

建议的文档改进
 export interface UserListBatchFilters {
   /** Offset pagination cursor */
   cursor?: number;
   /** Page size */
   limit?: number;
   /** Search in username / note */
   searchTerm?: string;
-  /** Filter by multiple tags (OR logic: users with ANY selected tag) */
+  /** Filter by multiple tags (OR logic: users with ANY selected tag).
+   *  When combined with keyGroupFilters, both conditions must be met (AND logic). */
   tagFilters?: string[];
-  /** Filter by provider group (derived from keys) */
+  /** Filter by provider group (OR logic: users with ANY selected group).
+   *  When combined with tagFilters, both conditions must be met (AND logic). */
   keyGroupFilters?: string[];
messages/zh-TW/auth.json (1)

33-33: 建议统一「登入」与「登錄」用词

新增内容中存在用词不一致:

  • Line 33 privacyNote: 使用「登入」
  • Line 41 unauthorized: 使用「登錄」
  • Line 44 serverError: 使用「登錄」

在繁体中文(台湾)中,「登入」更为常用。建议统一使用「登入」以提升一致性:

🔧 建议修改
-    "unauthorized": "未授權,請先登錄",
+    "unauthorized": "未授權,請先登入",
     "apiKeyRequired": "請輸入 API Key",
     "apiKeyInvalidOrExpired": "API Key 無效或已過期",
-    "serverError": "登錄失敗,請稍後重試"
+    "serverError": "登入失敗,請稍後重試"

Also applies to: 41-44

src/app/api/auth/login/route.ts (1)

21-28: Accept-Language 解析可优化。

当前实现使用简单的 includes() 检查,未处理 Accept-Language 标准格式(如 en-US,en;q=0.9,zh-CN;q=0.8)中的质量值权重。建议使用更健壮的解析方式。

♻️ 建议的改进
  // 2. Check Accept-Language header
  const acceptLanguage = request.headers.get("accept-language");
  if (acceptLanguage) {
-   for (const locale of locales) {
-     if (acceptLanguage.toLowerCase().includes(locale.toLowerCase())) {
-       return locale;
-     }
-   }
+   // Parse Accept-Language with quality values and sort by preference
+   const languages = acceptLanguage
+     .split(",")
+     .map((lang) => {
+       const [code, qValue] = lang.trim().split(";q=");
+       return { code: code.toLowerCase(), q: qValue ? parseFloat(qValue) : 1 };
+     })
+     .sort((a, b) => b.q - a.q);
+
+   for (const { code } of languages) {
+     const matched = locales.find(
+       (locale) => code === locale.toLowerCase() || code.startsWith(`${locale.toLowerCase()}-`)
+     );
+     if (matched) return matched;
+   }
  }
src/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsx (1)

377-381: 建议并行执行缓存失效操作以提升性能。

当前三个 invalidateQueries 调用是串行执行的。由于它们之间没有依赖关系,可以使用 Promise.all 并行执行以减少等待时间。

♻️ 建议的优化
       if (anySuccess) {
-        await queryClient.invalidateQueries({ queryKey: ["users"] });
-        await queryClient.invalidateQueries({ queryKey: ["userKeyGroups"] });
-        await queryClient.invalidateQueries({ queryKey: ["userTags"] });
+        await Promise.all([
+          queryClient.invalidateQueries({ queryKey: ["users"] }),
+          queryClient.invalidateQueries({ queryKey: ["userKeyGroups"] }),
+          queryClient.invalidateQueries({ queryKey: ["userTags"] }),
+        ]);
       }
tests/unit/lib/redis/scan-helper.test.ts (1)

5-39: 建议增加错误处理和默认参数的测试用例。

当前测试覆盖了主要场景,但建议补充以下测试以提高覆盖率:

  1. 错误处理:当 redis.scan 抛出异常时的行为
  2. 默认 COUNT 参数:验证不传 count 时使用默认值 100
♻️ 建议添加的测试用例
+  it("should use default count of 100", async () => {
+    const mockRedis = {
+      scan: vi.fn().mockResolvedValueOnce(["0", []]),
+    } as unknown as Redis;
+
+    await scanPattern(mockRedis, "key:*");
+
+    expect(mockRedis.scan).toHaveBeenCalledWith("0", "MATCH", "key:*", "COUNT", 100);
+  });
+
+  it("should propagate errors from redis.scan", async () => {
+    const mockRedis = {
+      scan: vi.fn().mockRejectedValueOnce(new Error("Connection failed")),
+    } as unknown as Redis;
+
+    await expect(scanPattern(mockRedis, "key:*")).rejects.toThrow("Connection failed");
+  });
src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx (1)

73-73: 考虑整理 max-h 类名。

当前同时使用了 max-h-[90vh]max-h-[90dvh],后者会覆盖前者。如果是为了兼容不支持 dvh 单位的浏览器,建议使用 CSS 变量或 @supports 来实现更清晰的降级方案。

不过,由于 Tailwind 会按顺序应用样式,这种写法在实践中也能正常工作(浏览器会使用最后一个可解析的值)。

♻️ 可选:简化为仅使用 dvh

如果目标浏览器都支持 dvh,可以简化:

-      <DialogContent className="max-w-2xl max-h-[90vh] max-h-[90dvh] p-0 flex flex-col overflow-hidden">
+      <DialogContent className="max-w-2xl max-h-[90dvh] p-0 flex flex-col overflow-hidden">
src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx (1)

221-222: 注释语言不一致。

代码中出现了俄语注释 "Инвалидировать кэш React Query для всех фильтров",与文件中其他中文注释风格不一致。建议统一使用中文或英文注释以保持代码一致性。

-      // Инвалидировать кэш React Query для всех фильтров
-      queryClient.invalidateQueries({ queryKey: ["users"] });
+      // 使 React Query 缓存失效,确保所有筛选条件下的数据刷新
+      queryClient.invalidateQueries({ queryKey: ["users"] });
src/actions/users.ts (2)

1548-1548: Redis 状态检查可能不够健壮。

redis.status === "ready" 检查依赖于 ioredis 的内部状态。在某些边缘情况下(如网络闪断后重连),状态可能不准确。

建议添加更明确的注释说明此检查的目的,或考虑使用 try-catch 包装所有 Redis 操作作为后备方案(当前已有 try-catch,这是好的)。


1552-1605: 注释语言不一致。

此代码段中混用了俄语注释(如 "Параллельно scan всех паттернов"、"Batch delete через pipeline"、"Продолжаем выполнение"),与文件中其他中文注释风格不一致。

建议统一注释语言
-        // Параллельно scan всех паттернов
+        // 并行扫描所有模式
         const scanResults = await Promise.all([
           ...keyIds.map((keyId) =>
             scanPattern(redis, `key:${keyId}:cost_*`).catch((err) => {
...
-        // Batch delete через pipeline
+        // 通过 pipeline 批量删除
         const pipeline = redis.pipeline();
...
-        // Продолжаем выполнение - DB logs уже удалены
+        // 继续执行 - 数据库日志已删除
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6a9e51d and 9877bc3.

📒 Files selected for processing (50)
  • messages/en/auth.json
  • messages/en/common.json
  • messages/en/dashboard.json
  • messages/en/quota.json
  • messages/en/settings/providers/autoSort.json
  • messages/ja/auth.json
  • messages/ja/common.json
  • messages/ja/dashboard.json
  • messages/ja/quota.json
  • messages/ru/auth.json
  • messages/ru/common.json
  • messages/ru/dashboard.json
  • messages/ru/quota.json
  • messages/ru/settings/providers/autoSort.json
  • messages/ru/settings/providers/form/title.json
  • messages/ru/settings/providers/strings.json
  • messages/zh-CN/auth.json
  • messages/zh-CN/common.json
  • messages/zh-CN/dashboard.json
  • messages/zh-TW/auth.json
  • messages/zh-TW/common.json
  • messages/zh-TW/dashboard.json
  • messages/zh-TW/quota.json
  • src/actions/key-quota.ts
  • src/actions/users.ts
  • src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx
  • src/app/[locale]/dashboard/_components/user/key-list.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/dashboard/providers/page.tsx
  • src/app/[locale]/dashboard/users/users-page-client.tsx
  • src/app/api/auth/login/route.ts
  • src/components/form/form-layout.tsx
  • src/components/section.tsx
  • src/components/ui/relative-time.tsx
  • src/lib/redis/index.ts
  • src/lib/redis/scan-helper.ts
  • src/repository/key.ts
  • src/repository/user.ts
  • src/types/user.ts
  • tests/unit/lib/redis/scan-helper.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use emoji characters in any code, comments, or string literals

Files:

  • src/app/[locale]/dashboard/_components/user/key-list.tsx
  • src/repository/user.ts
  • src/lib/redis/index.ts
  • src/actions/key-quota.ts
  • src/lib/redis/scan-helper.ts
  • src/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx
  • src/components/ui/relative-time.tsx
  • src/types/user.ts
  • src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx
  • src/repository/key.ts
  • tests/unit/lib/redis/scan-helper.test.ts
  • src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/components/section.tsx
  • src/app/[locale]/dashboard/providers/page.tsx
  • src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx
  • src/components/form/form-layout.tsx
  • src/app/api/auth/login/route.ts
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/actions/users.ts
  • src/app/[locale]/dashboard/users/users-page-client.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: All user-facing strings must use i18n (5 languages supported: zh-CN, zh-TW, en, ja, ru). Never hardcode display text
Use path alias @/ to reference files in ./src/ directory
Format code with Biome using: double quotes, trailing commas, 2-space indent, 100 character line width

Files:

  • src/app/[locale]/dashboard/_components/user/key-list.tsx
  • src/repository/user.ts
  • src/lib/redis/index.ts
  • src/actions/key-quota.ts
  • src/lib/redis/scan-helper.ts
  • src/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx
  • src/components/ui/relative-time.tsx
  • src/types/user.ts
  • src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx
  • src/repository/key.ts
  • tests/unit/lib/redis/scan-helper.test.ts
  • src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/components/section.tsx
  • src/app/[locale]/dashboard/providers/page.tsx
  • src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx
  • src/components/form/form-layout.tsx
  • src/app/api/auth/login/route.ts
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/actions/users.ts
  • src/app/[locale]/dashboard/users/users-page-client.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer named exports over default exports

Files:

  • src/app/[locale]/dashboard/_components/user/key-list.tsx
  • src/repository/user.ts
  • src/lib/redis/index.ts
  • src/actions/key-quota.ts
  • src/lib/redis/scan-helper.ts
  • src/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx
  • src/components/ui/relative-time.tsx
  • src/types/user.ts
  • src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx
  • src/repository/key.ts
  • tests/unit/lib/redis/scan-helper.test.ts
  • src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/components/section.tsx
  • src/app/[locale]/dashboard/providers/page.tsx
  • src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx
  • src/components/form/form-layout.tsx
  • src/app/api/auth/login/route.ts
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/actions/users.ts
  • src/app/[locale]/dashboard/users/users-page-client.tsx
src/repository/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Drizzle ORM for data access in the repository layer

Files:

  • src/repository/user.ts
  • src/repository/key.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All new features must have unit test coverage of at least 80%

Files:

  • tests/unit/lib/redis/scan-helper.test.ts
tests/**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit testing and happy-dom for DOM testing

Files:

  • tests/unit/lib/redis/scan-helper.test.ts
src/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

API routes should use Hono framework and follow Next.js 16 App Router conventions

Files:

  • src/app/api/auth/login/route.ts
🧠 Learnings (12)
📚 Learning: 2026-01-05T03:02:14.502Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 539
File: src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx:66-66
Timestamp: 2026-01-05T03:02:14.502Z
Learning: In the claude-code-hub project, the translations.actions.addKey field in UserKeyTableRowProps is defined as optional for backward compatibility, but all actual callers in the codebase provide the complete translations object. The field has been added to all 5 locale files (messages/{locale}/dashboard.json).

Applied to files:

  • src/app/[locale]/dashboard/_components/user/key-list.tsx
  • messages/en/auth.json
  • src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx
  • messages/ja/auth.json
  • src/types/user.ts
  • src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx
  • src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx
  • messages/zh-CN/auth.json
  • messages/zh-CN/dashboard.json
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • messages/zh-TW/dashboard.json
  • messages/ja/dashboard.json
  • messages/ru/quota.json
  • src/app/[locale]/dashboard/users/users-page-client.tsx
📚 Learning: 2026-01-05T03:01:39.354Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 539
File: src/types/user.ts:158-170
Timestamp: 2026-01-05T03:01:39.354Z
Learning: In TypeScript interfaces, explicitly document and enforce distinct meanings for null and undefined. Example: for numeric limits like limitTotalUsd, use 'number | null | undefined' when null signifies explicitly unlimited (e.g., matches DB schema or special UI logic) and undefined signifies 'inherit default'. This pattern should be consistently reflected in type definitions across related fields to preserve semantic clarity between database constraints and UI behavior.

Applied to files:

  • src/repository/user.ts
  • src/lib/redis/index.ts
  • src/actions/key-quota.ts
  • src/lib/redis/scan-helper.ts
  • src/types/user.ts
  • src/repository/key.ts
  • src/app/api/auth/login/route.ts
  • src/actions/users.ts
📚 Learning: 2026-01-10T06:19:58.167Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/actions/model-prices.ts:275-335
Timestamp: 2026-01-10T06:19:58.167Z
Learning: Do not modify hardcoded Chinese error messages in Server Actions under src/actions/*.ts as part of piecemeal changes. This is a repo-wide architectural decision that requires a coordinated i18n refactor across all Server Action files (e.g., model-prices.ts, users.ts, system-config.ts). Treat i18n refactor as a separate unified task rather than per-PR changes, and plan a project-wide approach for replacing hardcoded strings with localized resources.

Applied to files:

  • src/actions/key-quota.ts
  • src/actions/users.ts
📚 Learning: 2026-01-10T06:20:04.478Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/actions/model-prices.ts:275-335
Timestamp: 2026-01-10T06:20:04.478Z
Learning: In the `ding113/claude-code-hub` repository, Server Actions (files under `src/actions/*.ts`) currently return hardcoded Chinese error messages directly. This is a codebase-wide architectural decision that applies to all action files (e.g., model-prices.ts, users.ts, system-config.ts). Changing this pattern requires a coordinated i18n refactor across all Server Actions, which should be handled as a separate unified task rather than piecemeal changes in individual PRs.

Applied to files:

  • messages/en/auth.json
  • messages/ja/auth.json
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • messages/zh-CN/auth.json
  • messages/zh-CN/dashboard.json
  • src/app/api/auth/login/route.ts
  • messages/zh-TW/auth.json
  • src/app/[locale]/dashboard/users/users-page-client.tsx
📚 Learning: 2026-01-10T17:53:25.066Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T17:53:25.066Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : All user-facing strings must use i18n (5 languages supported: zh-CN, zh-TW, en, ja, ru). Never hardcode display text

Applied to files:

  • src/components/ui/relative-time.tsx
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
  • src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx
  • src/app/api/auth/login/route.ts
📚 Learning: 2026-01-10T17:53:25.066Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T17:53:25.066Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : All new features must have unit test coverage of at least 80%

Applied to files:

  • tests/unit/lib/redis/scan-helper.test.ts
📚 Learning: 2026-01-10T17:53:25.066Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T17:53:25.066Z
Learning: Applies to tests/**/*.test.{ts,tsx,js,jsx} : Use Vitest for unit testing and happy-dom for DOM testing

Applied to files:

  • tests/unit/lib/redis/scan-helper.test.ts
📚 Learning: 2026-01-10T06:20:13.376Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/app/[locale]/settings/prices/_components/sync-conflict-dialog.tsx:42-53
Timestamp: 2026-01-10T06:20:13.376Z
Learning: In the claude-code-hub project, model pricing display (in files like `src/app/[locale]/settings/prices/_components/sync-conflict-dialog.tsx`) intentionally uses hardcoded USD currency symbol (`$`) and per-million-token notation (`/M`, `/img`) because the system exclusively tracks LiteLLM pricing in USD and the notation is industry standard. Configurability was deemed unnecessary complexity.

Applied to files:

  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
📚 Learning: 2026-01-10T06:19:56.528Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/app/[locale]/settings/prices/_components/model-price-dialog.tsx:205-257
Timestamp: 2026-01-10T06:19:56.528Z
Learning: In the pricing module (src/app/[locale]/settings/prices/_components/), currency symbols ("$") and technical unit notations ("/M" for per-million tokens, "/img" for per-image) are intentionally hardcoded. The system uses USD as the fixed currency for all pricing, and these notations are standard industry conventions. These hardcoded values are an accepted exception to the general i18n requirement.

Applied to files:

  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx
📚 Learning: 2026-01-10T06:20:32.687Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/app/[locale]/settings/prices/_components/sync-litellm-button.tsx:118-125
Timestamp: 2026-01-10T06:20:32.687Z
Learning: In `src/app/[locale]/settings/prices/_components/sync-litellm-button.tsx`, the "Cancel" button in the SyncConflictDialog is intentionally designed to call `onConfirm([])`, which triggers `doSync([])` to continue the sync while skipping (not overwriting) conflicting manual prices. This is the desired product behavior to allow users to proceed with LiteLLM sync for non-conflicting models while preserving their manual price entries.

Applied to files:

  • src/components/form/form-layout.tsx
📚 Learning: 2026-01-10T17:53:25.066Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T17:53:25.066Z
Learning: Use `next-intl` for internationalization with support for 5 languages: zh-CN, zh-TW, en, ja, ru

Applied to files:

  • src/app/api/auth/login/route.ts
📚 Learning: 2026-01-10T17:53:25.066Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T17:53:25.066Z
Learning: Applies to src/repository/**/*.{ts,tsx} : Use Drizzle ORM for data access in the repository layer

Applied to files:

  • src/actions/users.ts
🧬 Code graph analysis (18)
src/app/[locale]/dashboard/_components/user/key-list.tsx (1)
src/components/ui/relative-time.tsx (1)
  • RelativeTime (27-135)
src/actions/key-quota.ts (1)
src/lib/auth.ts (1)
  • getSession (116-128)
src/lib/redis/scan-helper.ts (1)
src/lib/redis/index.ts (1)
  • scanPattern (5-5)
src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx (3)
src/components/ui/dialog.tsx (4)
  • DialogContent (121-121)
  • DialogHeader (124-124)
  • DialogTitle (127-127)
  • DialogDescription (122-122)
scripts/sync-settings-keys.js (1)
  • t (184-184)
src/components/ui/button.tsx (1)
  • Button (58-58)
src/components/ui/relative-time.tsx (1)
src/lib/utils/date-format.ts (1)
  • formatDateDistance (56-74)
src/repository/key.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (276-362)
tests/unit/lib/redis/scan-helper.test.ts (1)
src/lib/redis/scan-helper.ts (1)
  • scanPattern (17-32)
src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx (3)
src/actions/users.ts (1)
  • resetUserAllStatistics (1518-1622)
src/components/ui/alert-dialog.tsx (9)
  • AlertDialog (123-123)
  • AlertDialogTrigger (126-126)
  • AlertDialogContent (127-127)
  • AlertDialogHeader (128-128)
  • AlertDialogTitle (130-130)
  • AlertDialogDescription (131-131)
  • AlertDialogFooter (129-129)
  • AlertDialogCancel (133-133)
  • AlertDialogAction (132-132)
src/components/ui/button.tsx (2)
  • Button (58-58)
  • buttonVariants (58-58)
src/app/[locale]/dashboard/_components/user/key-row-item.tsx (1)
src/components/ui/relative-time.tsx (1)
  • RelativeTime (27-135)
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)
src/components/ui/relative-time.tsx (1)
  • RelativeTime (27-135)
src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx (1)
src/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx (1)
src/components/ui/select.tsx (1)
  • SelectItem (165-165)
src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx (3)
src/components/ui/label.tsx (1)
  • Label (21-21)
scripts/sync-settings-keys.js (1)
  • t (184-184)
src/components/ui/select.tsx (3)
  • SelectItem (165-165)
  • SelectContent (163-163)
  • Select (162-162)
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)
src/components/ui/relative-time.tsx (1)
  • RelativeTime (27-135)
src/app/[locale]/dashboard/providers/page.tsx (1)
src/app/[locale]/settings/providers/_components/auto-sort-priority-dialog.tsx (1)
  • AutoSortPriorityDialog (50-250)
src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx (2)
src/lib/utils/color.ts (2)
  • getGroupColor (5-24)
  • getContrastTextColor (30-86)
scripts/sync-settings-keys.js (1)
  • key (32-32)
src/components/form/form-layout.tsx (1)
src/components/ui/dialog.tsx (4)
  • DialogHeader (124-124)
  • DialogTitle (127-127)
  • DialogDescription (122-122)
  • DialogFooter (123-123)
src/actions/users.ts (6)
src/repository/user.ts (1)
  • findUserById (314-346)
src/drizzle/schema.ts (2)
  • keys (89-128)
  • messageRequest (276-362)
src/repository/key.ts (1)
  • findKeyList (43-72)
src/lib/redis/index.ts (2)
  • getRedisClient (3-3)
  • scanPattern (5-5)
src/lib/redis/client.ts (1)
  • getRedisClient (75-154)
src/lib/redis/scan-helper.ts (1)
  • scanPattern (17-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: pr-review
  • GitHub Check: Greptile Review
  • GitHub Check: pr-description
  • GitHub Check: Docker Build Test
  • GitHub Check: pr-label
  • GitHub Check: 🌐 API Tests
🔇 Additional comments (83)
src/components/form/form-layout.tsx (1)

54-71: 对话框布局改进看起来不错!

这些 CSS 调整实现了固定头部/底部的对话框模式:

  • 头部和底部使用 flex-shrink-0 防止收缩,配合 border-b/border-t 提供清晰的视觉分隔
  • 内容区域使用 overflow-y-auto + min-h-0 实现正确的滚动行为
  • 三个区域的水平内边距 px-6 保持一致

这与 PR 目标中提到的"关键对话框的固定头部/底部修复"相符。

src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx (2)

421-450: Key 筛选器的 "all" 逻辑正确。

使用 "__all__" 作为哨兵值来表示"所有 keys"是合理的做法,可以明确区分"未选择"和"全部"状态。onValueChange 中的解析逻辑和 SelectItem 的添加都是正确的。


604-644: 状态码筛选器的 "all" 逻辑正确。

状态码筛选器正确处理了三种情况:"__all__"(全部)、"!200"(排除 200)和具体状态码。逻辑清晰完整。

src/actions/key-quota.ts (2)

31-31: LGTM!

使用 allowReadOnlyAccess: true 选项允许只读访问用户查看其配额信息,符合 PR 目标。配合后续的授权检查,安全性得到保障。


44-47: 类型兼容性正确,但错误消息需要国际化。

类型验证通过:session.user.idkeyRow.userId 都是 number 类型,!== 比较安全。

但根据编码指南"所有用户面向字符串必须使用 i18n",错误消息 "Access denied" 和 "Key not found" 应该使用国际化。参考 src/actions/users.ts 的模式,使用 getTranslations("errors") 获取本地化的错误文本。

⛔ Skipped due to learnings
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 573
File: src/actions/model-prices.ts:275-335
Timestamp: 2026-01-10T06:20:04.478Z
Learning: In the `ding113/claude-code-hub` repository, Server Actions (files under `src/actions/*.ts`) currently return hardcoded Chinese error messages directly. This is a codebase-wide architectural decision that applies to all action files (e.g., model-prices.ts, users.ts, system-config.ts). Changing this pattern requires a coordinated i18n refactor across all Server Actions, which should be handled as a separate unified task rather than piecemeal changes in individual PRs.
messages/zh-CN/auth.json (1)

22-25: LGTM!

新增的三个错误键(apiKeyRequiredapiKeyInvalidOrExpiredserverError)翻译准确,与其他语言文件保持一致。privacyNote 简化后更加通用。

Also applies to: 40-40

messages/ja/auth.json (1)

33-33: LGTM!

日语翻译准确自然,新增的错误键与其他语言文件结构一致。

Also applies to: 41-44

messages/ru/auth.json (2)

33-33: 验证 privacyNote 语义一致性

俄语的 privacyNote 内容是"如果您忘记了 API 密钥,请联系管理员",而其他语言文件(en、ja、zh-CN、zh-TW)的内容是"请使用您的 API Key 登录后台"。

这两者语义不同:

  • 其他语言:登录指引
  • 俄语:密钥找回指引

如果这是有意为俄语用户提供的本地化差异,则可以保留;否则建议统一为登录指引以保持一致性。


41-44: LGTM!

新增的三个错误键翻译准确,俄语表达自然。

messages/en/auth.json (1)

33-33: LGTM!

新增的错误键清晰明了,privacyNote 简化后更加通用。作为基础语言文件,这些更改为其他语言提供了良好的参考。

Also applies to: 41-44

src/app/api/auth/login/route.ts (2)

2-3: LGTM!

导入正确使用了 next-intl/server 进行服务端翻译,并使用 @/ 路径别名引用 i18n 配置。


38-64: LGTM!

认证主流程实现正确:

  • 正确使用 getTranslations 获取本地化错误消息
  • 保持了原有的响应结构(user 对象包含 id、name、description、role)
  • 符合 PR 目标中的 i18n 改进要求
messages/ru/settings/providers/autoSort.json (1)

2-2: LGTM!

按钮文本简化为"Автосорт",与英文本地化更新保持一致。文件中其他翻译字符串完整且语法正确。

messages/ru/settings/providers/form/title.json (1)

2-3: LGTM!

术语标准化:将"провайдера"(音译词)统一改为"поставщика"(俄语本土词汇),与本 PR 中其他俄语本地化文件保持一致。

messages/en/settings/providers/autoSort.json (1)

2-2: LGTM!

按钮文本简化为"Auto Sort"更加简洁,完整上下文仍保留在对话框标题(第8行)中。

src/app/[locale]/dashboard/providers/page.tsx (1)

3-3: LGTM!

AutoSortPriorityDialog 组件的导入和使用正确:

  • 使用了 @/ 路径别名(符合编码规范)
  • 使用命名导出(符合编码规范)
  • 组件位置合理,与现有的 SchedulingRulesDialog 并列放置

Also applies to: 54-54

src/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsx (1)

133-134: 缓存失效逻辑合理

成功编辑 Key 后同时失效 userKeyGroupsuserTags 查询缓存,确保相关 UI 组件能获取最新数据。此模式与 batch-edit-dialog.tsx 保持一致。

src/types/user.ts (2)

103-103: todayTokens 字段类型定义正确

新增的 todayTokens: number 字段与现有的 todayUsagetodayCallCount 保持一致的类型定义(必填数字),符合接口设计规范。


171-172: 访问限制字段扩展合理

KeyDialogUserContext 中添加 allowedClientsallowedModels 可选字段,使密钥对话框能够展示用户级别的访问限制信息,与 User 接口中的定义保持一致。

src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx (1)

219-235: 国际化实现正确,所有翻译已完成

Cache TTL 相关的用户界面文本已正确使用 t() 函数进行国际化。已验证 cacheTtl.labelcacheTtl.options.inheritcacheTtl.options.5mcacheTtl.options.1hcacheTtl.description 翻译键在全部 5 种语言(en、ja、ru、zh-CN、zh-TW)的 dashboard.json 中均已存在并包含适当的本地化内容。

messages/ru/common.json (1)

52-61: LGTM!

俄语相对时间短格式翻译正确,使用了标准的 {count} 占位符进行插值,与其他语言文件保持一致。

messages/zh-TW/common.json (1)

52-61: LGTM!

繁体中文相对时间短格式翻译准确,结构与其他语言文件一致。

src/lib/redis/scan-helper.ts (1)

17-31: 实现正确,使用游标迭代的 SCAN 是生产环境安全的方案。

代码逻辑正确,相比阻塞式的 KEYS 命令,使用 SCAN 进行游标迭代是生产环境推荐的做法。

需要注意:当匹配模式对应大量键时(如数百万),所有键会被收集到内存数组中。如果预期会有极大规模的键集合,建议考虑添加可选的最大返回数量限制或改用流式/回调处理方式。

src/lib/redis/index.ts (1)

5-5: LGTM!

正确地将 scanPattern./scan-helper 重新导出,与现有的导出模式保持一致。

messages/zh-CN/common.json (1)

52-61: LGTM! 相对时间短格式翻译准确。

中文简短格式翻译(如 "{count}秒前"、"{count}分前")简洁且符合中文习惯,与其他语言文件保持一致的结构。

messages/en/common.json (1)

52-61: LGTM! 英文短格式时间翻译符合惯例。

使用标准缩写(s, m, h, d, w, mo, y)简洁明了,与其他语言文件结构一致。

src/app/[locale]/dashboard/_components/user/user-management-table.tsx (2)

94-95: LGTM! 新增刷新回调属性设计合理。

onRefreshisRefreshing 作为可选属性,保持了向后兼容性,符合组件扩展的最佳实践。


430-441: LGTM! 刷新按钮实现正确。

  • 使用 isRefreshing 禁用按钮防止重复点击
  • animate-spin 提供视觉反馈
  • title 属性通过 i18n 提供无障碍支持
src/components/section.tsx (1)

16-23: LGTM! 布局优化改进了响应式行为。

  • sm:items-start 使标题在 actions 换行时顶部对齐
  • min-w-0 shrink 允许标题容器正确截断和收缩
  • shrink-0 防止操作按钮被压缩

这些改动提升了在窄屏下的布局稳定性。

src/app/[locale]/dashboard/_components/user/add-key-dialog.tsx (1)

76-114: LGTM! 对话框固定头部/底部布局实现良好。

  • border-bborder-t 清晰分隔头部、内容和底部
  • 使用 flex flex-col 配合 overflow-hidden 实现内容区滚动
  • 符合 PR 目标中提到的"fixed header/footer in key dialogs"
src/app/[locale]/dashboard/_components/user/user-key-table-row.tsx (4)

4-13: LGTM!图标导入扩展正确。

新增的状态图标导入(CheckCircle2、CircleOff、Clock、XCircle)与下方的过期/状态指示器 UI 实现相匹配,导入组织清晰。


281-299: 状态指示器实现良好。

使用 Tooltip 包装状态图标提供了良好的无障碍体验,cursor-help 类名提示用户可悬停查看详情。状态映射逻辑清晰:

  • 活跃状态使用绿色勾选图标
  • 禁用状态使用灰色禁止图标
  • 即将过期使用黄色时钟图标
  • 已过期使用红色叉号图标

301-342: 分组徽章显示逻辑清晰。

实现包含以下良好实践:

  • 对 "default" 分组使用 outline 样式以区分
  • 其他分组使用动态颜色(通过 getGroupColorgetContrastTextColor
  • 超出显示限制时显示 "+N" 徽章
  • Tooltip 显示完整分组列表

条件渲染使用 userGroups.length > 0 ? ... : null 是正确的模式。


505-505: todayTokens 数据传递正确。

todayTokens 字段已正确添加到传递给 KeyRowItemkeyData 对象中,与仓库层(findKeyUsageTodayBatch)和类型定义的更改保持一致。

src/actions/users.ts (4)

213-218: usageLookup 结构更新正确。

usageLookup 现在返回包含 totalCosttotalTokens 的对象,与 findKeyUsageTodayBatch 的返回类型变更保持一致。空值合并处理 (?? 0) 确保了默认值的安全性。


1540-1541: 硬删除 messageRequest 记录需确认。

当前实现使用 db.delete(messageRequest) 进行硬删除,而非设置 deletedAt 的软删除。这与 schema 中 messageRequest 表定义的 deletedAt 字段设计模式不一致。

请确认这是预期行为。如果是为了彻底清除用户数据以满足合规要求(如 GDPR),硬删除是合理的;否则建议使用软删除以保持一致性。


1569-1579: Pipeline 删除实现高效。

使用 Redis pipeline 批量删除是正确的做法,相比逐个删除可显著提高性能。代码正确处理了:

  1. 活跃会话键 (key:${keyId}:active_sessions)
  2. 成本统计键(通过 scanPattern 扫描获取)

1581-1590: Pipeline 执行结果检查良好。

正确检查了 pipeline 执行结果中的错误,并在出现部分失败时记录警告日志,同时不中断整体流程。这是合理的容错设计。

src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx (3)

127-128: 扩展缓存失效范围是正确的做法。

在用户编辑成功后,同时使 ["userKeyGroups"]["userTags"] 缓存失效,确保筛选下拉框数据的及时更新。这与其他操作(禁用、启用、删除)中的缓存失效逻辑保持一致。


222-241: 重置统计处理函数实现良好。

实现包含:

  • 加载状态管理 (setIsResettingAll)
  • 错误处理与 toast 提示
  • 成功后关闭确认对话框

使用 window.location.reload() 进行完整页面刷新是合理的选择,因为重置操作涉及大量缓存数据的清除,完整刷新可确保所有组件状态重置。


289-340: 重置数据 UI 部分无需额外的条件渲染保护。

UI 设计考虑周全:

  • 使用 border-destructive/30bg-destructive/5 视觉提示危险操作
  • AlertDialog 确认流程防止误操作
  • 加载状态下禁用取消和确认按钮
  • 使用 e.preventDefault() 阻止 AlertDialogAction 默认关闭行为

此 UI 部分仅在管理员上下文中可见,因为 EditUserDialog 已在 user-management-table.tsx 中通过 isAdmin && editingUser 条件渲染完全受保护。服务端 resetUserAllStatistics 也验证了管理员权限。无需添加额外的条件渲染。

Likely an incorrect or invalid review comment.

src/repository/key.ts (2)

358-361: Map 类型定义与返回值匹配。

类型定义已正确更新为 Map<number, Array<{ keyId: number; totalCost: number; totalTokens: number }>>,与查询结果和使用方保持一致。


375-375: totalTokens 提取逻辑正确。

使用 Number(row.totalTokens) || 0 确保了类型转换的安全性,当值为 falsy 时默认为 0。

src/app/[locale]/dashboard/_components/user/key-row-item.tsx (7)

3-13: 图标导入更新正确。

新增 Activity 图标用于调用次数显示,Coins 图标用于 token 数量显示,语义清晰直观。


54-54: KeyRowItemProps 接口扩展正确。

todayTokens: number 字段添加到 keyData 接口中,与父组件(UserKeyTableRow)传递的数据结构一致。


82-86: 翻译字段扩展完整。

新增 todayTokenstokensLabel 翻译字段,确保了 i18n 支持。根据 coding guidelines,所有用户可见字符串必须使用 i18n。


318-319: 网格列定义已更新。

列定义从 9 列更新为 10 列(多选模式)和 9 列(非多选模式),以容纳新增的 token 列。列宽比例合理,新增列使用 1.2fr


432-436: Tooltip 内容格式改进。

将分组显示从单行改为列表格式(<ul>/<li>),提高了多分组场景下的可读性。font-mono 类名确保了分组名称的等宽显示。


451-458: Token 列 UI 实现良好。

使用 Coins 图标和 formatTokenAmount 格式化函数显示 token 数量,与调用次数列的样式保持一致(右对齐、tabular-nums、图标 + 数字)。


468-468: RelativeTime 使用短格式提升信息密度。

format="short" 参数使用简短的相对时间格式(如 "5分" 而非 "5 分钟前"),在表格行有限空间中更为合适。

messages/zh-CN/dashboard.json (3)

785-793: LGTM!Cache TTL 覆写翻译添加正确。

新增的 cacheTtl 翻译块结构清晰,选项值(inherit、5m、1h)与其他语言文件保持一致。


1157-1171: LGTM!用户管理表格的翻译更新正确。

  • todayUsage 更名为 todayRequests(今日请求)语义更准确
  • 新增 todayTokens(今日Token)和对应的标签字段
  • 新增 refresh(刷新)按钮文本

这些更改与 PR 目标中的用户管理增强功能一致。


1262-1273: LGTM!重置统计功能的翻译完整。

resetData 块包含了完整的用户交互流程所需的所有文本:标题、描述、错误提示、按钮文本、确认对话框、加载状态和成功提示。翻译质量良好。

src/components/ui/relative-time.tsx (2)

41-66: LGTM!短格式时间距离计算实现正确。

使用 useCallback 包装 formatShortDistance 函数是正确的做法,避免了不必要的重新创建。时间计算逻辑清晰,优先级顺序合理(年 → 月 → 周 → 天 → 时 → 分 → 秒)。

注意: 月份(30天)和年份(365天)使用近似值计算,这对于相对时间显示是可接受的,与 date-fnsformatDistance 行为一致。


88-106: LGTM!条件格式化逻辑和依赖数组正确。

  • format === "short" 时使用自定义的 formatShortDistance
  • 否则使用 date-fnsformatDateDistance
  • 依赖数组包含了所有必要的依赖项:date, autoUpdate, updateInterval, locale, format, formatShortDistance
messages/ja/common.json (1)

52-61: LGTM!日语短格式相对时间翻译正确。

翻译质量良好,符合日语自然表达习惯:

  • たった今 (刚刚)
  • {count}秒前, {count}分前 等使用正确的日语后缀

结构与其他语言文件保持一致。

src/app/[locale]/dashboard/_components/user/key-list.tsx (1)

233-252: LGTM!RelativeTime 组件正确使用短格式。

format="short" 的使用与 PR 目标中"使用紧凑的相对时间格式"一致。此更改将使"最后使用"列显示更简洁的时间格式(如"2分前"而非"2分钟前")。

src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)

271-274: LGTM!日志表格时间列使用短格式正确。

在虚拟化日志表格中使用 format="short" 是合理的选择,因为:

  1. 表格空间有限(flex-[0.8] min-w-[80px]
  2. 短格式更适合高密度数据展示
  3. 用户可通过 Tooltip 查看完整时间戳
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)

127-127: LGTM!

使用 format="short" 使日志表格中的时间显示更紧凑,符合 PR 目标中提到的使用紧凑相对时间格式的需求。该属性值与 RelativeTime 组件的类型定义一致。

messages/en/dashboard.json (5)

784-792: LGTM!

Cache TTL 覆盖选项的翻译键结构清晰,包含了 inherit5m1h 三个选项,与 PR 目标中提到的缓存 TTL 覆盖功能一致。


1156-1170: LGTM!

用户管理表格中的翻译更新合理:

  • todayUsage 重命名为 "Requests today"
  • 新增 todayTokens 用于显示 "Tokens today"
  • 新增 refresh 按钮文本
  • 字段标签 callsLabeltokensLabel 命名一致

这些更改与 PR 目标中添加 tokens 追踪和刷新按钮的功能一致。


1261-1272: LGTM!

统计重置功能的翻译键完整覆盖了整个用户流程:标题、描述、错误提示、按钮文本、确认对话框和加载/成功状态。这与 PR 目标中添加管理员"重置所有统计"功能的需求一致。


1371-1435: LGTM!

限制规则的 UI 脚手架翻译键结构完整,包含:

  • 表单字段(类型、值)
  • 每日重置模式选项
  • 限制类型枚举(RPM、5小时、每日、每周、每月、总计、并发会话)
  • 验证错误消息
  • 覆盖提示

这为添加/编辑限制规则功能提供了完整的 i18n 支持。


1658-1665: LGTM!

accessRestrictions 部分的翻译键为键编辑界面中的访问限制功能提供了必要的 i18n 支持,包括模型限制、客户端限制、无限制和继承自用户设置的提示文本。

src/app/[locale]/dashboard/users/users-page-client.tsx (3)

512-559: LGTM!

非管理员用户的 Provider Group 和 Access Restrictions 信息展示块实现良好:

  • 使用响应式网格布局 (grid-cols-1 sm:grid-cols-2)
  • 所有用户可见文本都通过 i18n 翻译
  • 使用 LayersShieldCheck 图标提供清晰的视觉指示
  • 正确处理了空值/无限制的情况(使用 tProviderGroup("allProviders")tRestrictions("noRestrictions")

707-708: LGTM!

正确将 refetch 回调和 isRefreshing 状态传递给 UserManagementTable 组件,使表格能够支持手动刷新功能。这与 PR 目标中添加刷新按钮的需求一致。


768-775: InlineLoading 组件正在被使用。

InlineLoadingUsersTableSkeleton 组件中被正确使用(第 790 行),这是合理的设计。该组件不是遗留代码。

messages/ru/dashboard.json (5)

777-785: LGTM!

Cache TTL 覆盖选项的俄语翻译与英语版本结构一致,翻译质量良好(如 "Переопределение Cache TTL"、"Не переопределять (следовать провайдеру/клиенту)")。


1144-1158: LGTM!

用户管理表格相关的俄语翻译更新与英语版本保持一致:

  • todayUsage: "Запросы сегодня" (Requests today)
  • todayTokens: "Токены сегодня" (Tokens today)
  • callsLabel: "Запросы" (Requests)
  • tokensLabel: "Токены" (Tokens)
  • refresh: "Обновить" (Refresh)

1250-1260: LGTM!

统计重置功能的俄语翻译完整且准确,包含所有必要的 UI 文本:标题、描述、错误消息、按钮和确认对话框。


1361-1423: LGTM!

限制规则 UI 的俄语翻译结构完整,与英语版本保持一致,包含表单字段、每日重置模式、限制类型枚举和验证错误消息。


1647-1654: LGTM!

accessRestrictions 部分的俄语翻译准确,包含模型限制、客户端限制、无限制提示和继承自用户设置的文本。

messages/ru/quota.json (2)

67-113: LGTM!

users 部分新增的俄语翻译键完整覆盖了配额管理所需的所有 UI 文本:

  • 管理提示和链接
  • 配额状态(有配额、无限制)
  • 各类限制标签(5小时、每周、每月、总计、并发)
  • 角色和密钥状态标签

翻译质量良好,术语使用一致。


291-337: LGTM!

editKeyForm 中新增的 descriptionWithUserLimit 变体为各类限制字段提供了用户级别限制的提示消息,与英语版本结构一致:

  • limit5hUsd: "Не может превышать лимит пользователя ({limit})"
  • limitWeeklyUsd: "Не может превышать недельный лимит пользователя ({limit})"
  • limitMonthlyUsd: "Не может превышать месячный лимит пользователя ({limit})"
  • limitTotalUsd: "Не может превышать общий лимит пользователя ({limit})"
  • limitConcurrentSessions: "Не может превышать лимит пользователя ({limit})"

这些翻译为管理员在设置密钥限制时提供了清晰的用户级别约束提示。

messages/en/quota.json (1)

291-338: LGTM!

新增的 descriptionWithUserLimit 字段和 limitTotalUsd 块结构正确,占位符 {limit} 使用一致,与其他语言文件保持同步。

messages/ja/quota.json (1)

268-314: LGTM!

日语翻译质量良好,新增的 descriptionWithUserLimit 字段和 limitTotalUsd 块与英文版结构一致,占位符 {limit} 使用正确。

messages/zh-TW/quota.json (1)

266-312: LGTM!

繁体中文翻译准确,结构与其他语言文件对齐,占位符使用一致。

messages/ja/dashboard.json (3)

720-783: LGTM!

addKeyForm 部分新增的配额限制描述和 cacheTtl 设置翻译准确,结构与英文版一致。


1137-1151: LGTM!

keyRow 字段的更新(todayUsage → 今日請求、新增 todayTokenscallsLabel/tokensLabel 重命名)以及 refresh 标签添加正确,与 PR 目标中的用户管理增强一致。


1203-1249: LGTM!

quickRenew 增强字段和 resetData 对话框翻译完整,占位符 {userName} 使用正确,与其他语言文件结构对齐。

messages/zh-TW/dashboard.json (3)

720-783: LGTM!

addKeyForm 配额限制和 cacheTtl 设置的繁体中文翻译准确,结构完整。


1142-1156: LGTM!

keyRow 字段更新(今日請求、今日Token、請求/Token 标签)和刷新按钮翻译正确。


1208-1258: LGTM!

quickRenew 增强和 resetData 统计重置功能的繁体中文翻译完整准确,与日文版和英文版结构一致。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 65 to 69
} catch (error) {
logger.error("Login error:", error);
return NextResponse.json({ error: "登录失败,请稍后重试" }, { status: 500 });
const t = await getTranslations({ locale, namespace: "auth.errors" });
return NextResponse.json({ error: t("serverError") }, { status: 500 });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

catch 块中重复调用 getTranslations 存在风险。

如果翻译系统本身出现问题(如配置错误),catch 块中的 getTranslations 调用可能再次抛出异常,导致原始错误被掩盖且返回不可控的响应。建议添加回退处理。

🛠️ 建议的修复
  } catch (error) {
    logger.error("Login error:", error);
-   const t = await getTranslations({ locale, namespace: "auth.errors" });
-   return NextResponse.json({ error: t("serverError") }, { status: 500 });
+   try {
+     const t = await getTranslations({ locale, namespace: "auth.errors" });
+     return NextResponse.json({ error: t("serverError") }, { status: 500 });
+   } catch {
+     return NextResponse.json({ error: "Server error" }, { status: 500 });
+   }
  }
🤖 Prompt for AI Agents
In `@src/app/api/auth/login/route.ts` around lines 65 - 69, The catch block
currently calls getTranslations which may throw and mask the original error;
update the catch to avoid rethrowing by wrapping the getTranslations call in its
own try/catch (or use a safe helper) and fall back to a static/default error
message if translation retrieval fails; ensure you still log both the original
error (logger.error) and any translation-fetch error, then return
NextResponse.json({ error: fallbackMessage }, { status: 500 }) so
getTranslations failures cannot propagate.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR adds several dashboard UI improvements including user management enhancements, statistics reset functionality, and i18n improvements. The changes are well-structured overall, but there are a few issues that should be addressed.

PR Size: XL

  • Lines changed: 1263 (1084 additions, 179 deletions)
  • Files changed: 50

Split Suggestion: Consider splitting this PR into smaller, focused PRs:

  1. i18n/localization changes (messages/*.json files)
  2. User management UI improvements (user table, key display, tokens tracking)
  3. Statistics reset feature (resetUserAllStatistics + Redis scan helper)
  4. Logs filter improvements

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 1 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Documentation 0 0 0 0
Tests 0 0 1 0
Simplification 0 0 0 0

High Priority Issues (Should Fix)

  1. [LOGIC-BUG] src/repository/user.ts:214 - Filter logic changed from OR to AND
    • The change from (tagFilterCondition) OR (keyGroupFilterCondition) to (tagFilterCondition) AND (keyGroupFilterCondition) significantly alters filter behavior
    • Previously: users matching EITHER tag filters OR key group filters were returned
    • Now: users must match BOTH conditions (intersection instead of union)
    • This may be intentional but should be verified and documented

Medium Priority Issues (Consider Fixing)

  1. [TEST-MISSING-CRITICAL] src/actions/users.ts - No test for resetUserAllStatistics function
    • Per CLAUDE.md: "All new features must have unit test coverage of at least 80%"
    • The resetUserAllStatistics function is a critical admin operation that deletes user data
    • The scanPattern helper has tests, but the main action function does not

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean (errors are logged and returned properly)
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Missing test for resetUserAllStatistics
  • Code clarity - Good

Notes

Positive observations (not commenting on these per instructions, but noting for context):

  • The Redis pipeline optimization in resetUserAllStatistics is well-implemented
  • Error handling in the new function properly logs and continues execution
  • i18n translations are complete across all 5 locales
  • The scanPattern helper has good test coverage

Automated review by Claude AI

- Remove duplicate max-h class in edit-key-dialog.tsx (keep max-h-[90dvh] only)
- Add try-catch fallback for getTranslations in login route catch block

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request primarily focuses on enhancing internationalization (i18n) by adding numerous new translation keys across multiple languages (English, Japanese, Russian, Simplified Chinese, Traditional Chinese) for login errors, short relative time formats, dashboard elements like 'Cache TTL Override' and 'Tokens today', user management features including 'Reset Statistics', and detailed limit rule configurations. It also refines the user interface with styling adjustments for dialogs and improved display of user groups and key statuses using icons and tooltips. Functionally, the changes introduce a new admin-only feature to reset all user statistics (logs, Redis cache, and sessions), allow non-admin users to view their own key quotas, and update user and key management actions to invalidate relevant React Query caches. A new scanPattern helper for Redis was added to efficiently clear cache keys. The review comment specifically points out the need to translate Russian comments in the resetUserAllStatistics function to English for consistency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comments in this function are in Russian, while the rest of the codebase is in English. For consistency and maintainability, it's best to write all comments in English. This also applies to the comments on lines 1568 and 1605.

Suggested change
// Параллельно scan всех паттернов
// Scan all patterns in parallel

John Doe and others added 2 commits January 14, 2026 17:05
Addresses Gemini Code Assist review feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cover all requirement scenarios:
- Permission check (admin-only)
- User not found handling
- Success path with DB + Redis cleanup
- Redis not ready graceful handling
- Redis partial failure warning
- scanPattern failure warning
- pipeline.exec failure error logging
- Unexpected error handling
- Empty keys list handling

10 test cases with full mock coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ding113 ding113 merged commit 472cfd4 into ding113:dev Jan 19, 2026
6 of 8 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 19, 2026
@github-actions github-actions bot mentioned this pull request Jan 19, 2026
9 tasks
ding113 added a commit that referenced this pull request Jan 20, 2026
* fix: 清理 usage-doc / big-screen i18n 硬编码 + 修复 Next.js params Promise 报错

* feat(leaderboard): add user tag and group filters for user ranking (#607)

* feat(leaderboard): add user tag and group filters for user ranking (#606)

Add filtering capability to the leaderboard user ranking by:
- userTags: filter users by their tags (OR logic)
- userGroups: filter users by their providerGroup (OR logic)

Changes:
- Repository: Add UserLeaderboardFilters interface and SQL filtering
- Cache: Extend LeaderboardFilters and include filters in cache key
- API: Parse userTags/userGroups query params (CSV format, max 20)
- Frontend: Add TagInput filters (admin-only, user scope only)
- i18n: Add translation keys for 5 languages

Closes #606

* refactor: apply reviewer suggestions for leaderboard filters

- Use JSONB ? operator instead of @> for better performance
- Extract parseListParam helper to reduce code duplication

* feat(leaderboard): add tag/group suggestions dropdown for better UX

- Fetch all user tags and groups via getAllUserTags/getAllUserKeyGroups
- Pass suggestions to TagInput for autocomplete dropdown
- Validate input against available suggestions
- Consistent with /dashboard/users filter behavior

* docs: update Privnode offer details in README files

* fix: 修复 1M 上下文标头兼容问题

* fix: resolve container name conflicts in multi-user environments (#625)

* fix: resolve container name conflicts in multi-user environments

- Add top-level `name` field with COMPOSE_PROJECT_NAME env var support
- Remove hardcoded container_name from all services
- Users can now set COMPOSE_PROJECT_NAME in .env for complete isolation

Closes #624

* fix: add top-level name field for project isolation

Add `name: ${COMPOSE_PROJECT_NAME:-claude-code-hub}` to enable
complete project isolation via environment variable.

* feat(dashboard): improve user management, statistics reset, and i18n (#610)

* fix(dashboard/logs): add reset options to filters and use short time format

- Add "All keys" SelectItem to API Key filter dropdown
- Add "All status codes" SelectItem to Status Code filter dropdown
- Use __all__ value instead of empty string (Radix Select requirement)
- Add formatDateDistanceShort() for compact time display (2h ago, 3d ago)
- Update RelativeTime component to use short format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(providers): add Auto Sort button to dashboard and fix i18n

- Add AutoSortPriorityDialog to dashboard/providers page
- EN: "Auto Sort Priority" -> "Auto Sort"
- RU: "Авто сортировка приоритета" -> "Автосорт"
- RU: "Добавить провайдера" -> "Добавить поставщика"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(i18n): improve Russian localization and fix login errors

Russian localization improvements:
- Menu: "Управление поставщиками" -> "Поставщики"
- Menu: "Доступность" -> "Мониторинг"
- Filters: "Последние 7/30 дней" -> "7д/30д"
- Dashboard: "Статистика использования" -> "Статистика"
- Dashboard: "Показать статистику..." -> "Только ваши ключи"
- Quota: add missing translations (manageNotice, withQuotas, etc.)

Login error localization:
- Fix issue where login errors displayed in Chinese ("无效或已过期") regardless of locale
- Add locale detection from NEXT_LOCALE cookie and Accept-Language header
- Add 3 new error keys: apiKeyRequired, apiKeyInvalidOrExpired, serverError
- Support all 5 languages: EN, JA, RU, ZH-CN, ZH-TW
- Remove product name from login privacyNote for all locales

Files changed:
- messages/*/auth.json: new error keys, update privacyNote
- messages/ru/dashboard.json, messages/ru/quota.json: Russian improvements
- src/app/api/auth/login/route.ts: add getLocaleFromRequest()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(dashboard/users): improve user management with key quotas and tokens

- Add access/model restrictions support (allowedClients/allowedModels)
- Add tokens column and refresh button to users table
- Add todayTokens calculation in repository layer (sum all token types)
- Add visual status indicators with color-coded icons (active/disabled/expiring/expired)
- Allow users to view their own key quota (was admin-only)
- Fix React Query cache invalidation on status toggle
- Fix filter logic: change tag/keyGroup from OR to AND
- Refactor time display: move formatDateDistanceShort to component with i18n
- Add fixed header/footer to key dialogs for better UX

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(dashboard/users): add reset statistics with optimized Redis pipeline

- Implement reset all statistics functionality for admins
- Optimize Redis operations: replace sequential redis.keys() with parallel SCAN
- Add scanPattern() helper for production-safe key scanning
- Comprehensive error handling and performance metrics logging
- 50-100x performance improvement with no Redis blocking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(lint): apply biome formatting and fix React hooks dependencies

- Fix useEffect dependencies in RelativeTime component (wrap formatShortDistance in useCallback)
- Remove unused effectiveGroupText variable in key-row-item.tsx
- Apply consistent LF line endings across modified files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review feedback from PR #610

- Remove duplicate max-h class in edit-key-dialog.tsx (keep max-h-[90dvh] only)
- Add try-catch fallback for getTranslations in login route catch block

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: translate Russian comments to English for consistency

Addresses Gemini Code Assist review feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(users): add unit tests for resetUserAllStatistics function

Cover all requirement scenarios:
- Permission check (admin-only)
- User not found handling
- Success path with DB + Redis cleanup
- Redis not ready graceful handling
- Redis partial failure warning
- scanPattern failure warning
- pipeline.exec failure error logging
- Unexpected error handling
- Empty keys list handling

10 test cases with full mock coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(my-usage): cache statistics and timezone fixes (#623)

* feat(my-usage): add cache token statistics to model breakdown

Add cacheCreationTokens and cacheReadTokens fields to ModelBreakdownItem
interface and related DB queries for displaying cache statistics in the
statistics summary card modal.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(my-usage): use configured timezone for date filtering

Use server timezone (TZ config) instead of browser locale when:
- Filtering statistics by date (getMyStatsSummary)
- Calculating daily quota time ranges (getTimeRangeForPeriod)

This ensures consistent date interpretation across different client
timezones. Fixes discrepancy between Daily Quota and Statistics
Summary when user is in a different timezone than the server.

Added comprehensive unit tests covering:
- Date parsing in configured timezone
- Timezone offset calculations
- Day boundary edge cases

* refactor(my-usage): clean up expiration displays

Remove duplicate expiration information:
- Remove Key Expires chip from Welcome header (keep User Expires only)
- Remove "Expiring Soon" warning block from Quota Usage cards

Improve countdown display in ExpirationInfo component:
- Add Clock icon
- Increase font size to match date display
- Use monospace font for better readability
- Color-coded by status (emerald/amber/red)

ExpirationInfo remains the single source of expiration data.

* fix: correct cache hit rate formula to exclude output tokens

Output tokens are never cached (per Anthropic docs), so they should not be
included in cache hit rate calculation. Changed formula from:
  cacheReadTokens / (input + output + cacheCreate + cacheRead)
to:
  cacheReadTokens / (input + cacheCreate + cacheRead)

Affects:
- /my-usage model breakdown cache hit rate display
- /dashboard/leaderboard provider cache hit rate ranking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address PR #623 review feedback

- Fix outdated cache hit rate comment (leaderboard.ts:430)
- Add keyboard accessibility to model breakdown rows (a11y)
- Rename totalTokens -> totalInputTokens in ProviderCacheHitRateLeaderboardEntry
- Extract parseDateRangeInServerTimezone helper to reduce duplication

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add backward compatibility for totalTokens field

Keep returning totalTokens (deprecated) alongside totalInputTokens
for API consumers that haven't migrated yet.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Set HOSTNAME environment variable in Dockerfile (#622)

Add HOSTNAME environment variable for container

* fix: inherit 1M flag from client

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix(my-usage): make date range DST-safe

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(api): cover leaderboard comma-list parsing

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(auth): avoid hardcoded server error fallback

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(actions): localize key quota permission errors

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* docs: clarify context 1M inherit behavior

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(settings): fix model multi-select messages loader

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(dashboard): provide QueryClientProvider in edit key form test

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* chore(scripts): avoid hardcoded docker container names

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(i18n): replace fullwidth parentheses in ja dashboard

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(ui): add a11y label and clean up dialog styles

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* chore: ignore tmp scratch directory

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat: Dashboard Logs:秒级时间筛选 + Session ID 精确筛选/联想/展示(含回归修复) (#611)

* feat: add sessionId filter for usage logs

* feat: add seconds-level time filters for logs

* feat: wire sessionId into logs URL filters

* chore: add i18n keys for logs sessionId

* feat: add sessionId column to logs tables

* feat: add sessionId suggestions for logs

* docs: document dashboard logs call chain

* test: add logs sessionId/time filter coverage config

* fix: keep sessionId search input focused

* fix: drop leaked page param on logs apply

* fix: reload sessionId suggestions on scope change

* fix: harden logs url params and time parsing

* fix: avoid keys join for sessionId suggestions

* test: strengthen empty sessionId filter assertions

* chore: format logs sessionId suggestions test

* Update src/actions/usage-logs.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* feat: 补充公共静态变量 SESSION_ID_SUGGESTION_LIMIT

* fix: use prefix LIKE for sessionId suggestions

* refactor: centralize usage logs sessionId suggestion constants

* refactor: simplify logs url filters parsing

* refactor: reuse clipboard util for sessionId copy

* chore(db): add sessionId prefix index

* docs: clarify sessionId suggestion semantics

* test: add escapeLike unit tests

* chore: apply biome fixes for sessionId search

* feat: include session id in error responses

* test: add coverage suite for session id errors

* docs: add guide for error session id

* chore: format code (feat-logs-sessionid-time-filter-233f96a)

* feat(dashboard/logs): add fullscreen mode (#632)

* Fix provider exhaustion after model redirect (refs #629) (#633)

* Fix provider fallback after model redirect

Use original model for provider selection during failover and tolerate tool_result blocks in non-stream converters to prevent premature provider exhaustion. References #629.

* Add regression test for model redirect provider selection

Ensures provider selection uses original model even when current model is redirected (refs #629).

* chore: format code (fix-629-provider-fallback-toolresult-b17f671)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: YangQing-Lin <56943790+YangQing-Lin@users.noreply.github.com>
Co-authored-by: SaladDay <92240037+SaladDay@users.noreply.github.com>
Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com>
Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: hwa <yll2002mail@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Ding <ding113@users.noreply.github.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@coderabbitai coderabbitai bot mentioned this pull request Jan 21, 2026
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n area:statistics area:UI enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants

Comments