feat(dashboard): improve user management, statistics reset, and i18n#610
feat(dashboard): improve user management, statistics reset, and i18n#610ding113 merged 9 commits intoding113:devfrom
Conversation
…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>
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthrough此 PR 扩展多语言本地化(新增短格式相对时间与多语言 auth 文案)、引入 todayTokens 与若干配额/仪表板文案、添加 Redis SCAN 辅助及测试、在统计流水中传播 tokens、并新增管理员“重置数据”功能与多处 UI/缓存失效调整(含 layout/对话框改动)。 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
🧹 Recent nitpick comments
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro Cache: Disabled due to Reviews > Disable Cache setting 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (5)**/*.{js,ts,tsx,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.test.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/**/*.test.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (1)📓 Common learnings🧬 Code graph analysis (1)tests/unit/actions/users-reset-all-statistics.test.ts (3)
⏰ 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)
🔇 Additional comments (4)
✏️ Tip: You can disable this entire section by setting Comment |
Summary of ChangesHello @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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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 行的
confirmDeleteDesc、confirmDeleteProvider、confirmDeleteProviderDesc、editProvider)。建议统一术语以确保用户体验一致性。
🤖 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! 筛选逻辑变更实现正确。将
tagFilters和keyGroupFilters的组合逻辑从 OR 改为 AND 的实现是正确的,符合 PR 目标中描述的行为变更。建议在
UserListBatchFilters接口的文档注释中补充说明:当同时提供tagFilters和keyGroupFilters时,两者之间使用 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: 建议增加错误处理和默认参数的测试用例。当前测试覆盖了主要场景,但建议补充以下测试以提高覆盖率:
- 错误处理:当
redis.scan抛出异常时的行为- 默认 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
📒 Files selected for processing (50)
messages/en/auth.jsonmessages/en/common.jsonmessages/en/dashboard.jsonmessages/en/quota.jsonmessages/en/settings/providers/autoSort.jsonmessages/ja/auth.jsonmessages/ja/common.jsonmessages/ja/dashboard.jsonmessages/ja/quota.jsonmessages/ru/auth.jsonmessages/ru/common.jsonmessages/ru/dashboard.jsonmessages/ru/quota.jsonmessages/ru/settings/providers/autoSort.jsonmessages/ru/settings/providers/form/title.jsonmessages/ru/settings/providers/strings.jsonmessages/zh-CN/auth.jsonmessages/zh-CN/common.jsonmessages/zh-CN/dashboard.jsonmessages/zh-TW/auth.jsonmessages/zh-TW/common.jsonmessages/zh-TW/dashboard.jsonmessages/zh-TW/quota.jsonsrc/actions/key-quota.tssrc/actions/users.tssrc/app/[locale]/dashboard/_components/user/add-key-dialog.tsxsrc/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsxsrc/app/[locale]/dashboard/_components/user/edit-key-dialog.tsxsrc/app/[locale]/dashboard/_components/user/edit-user-dialog.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsxsrc/app/[locale]/dashboard/_components/user/key-list.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/_components/user/user-key-table-row.tsxsrc/app/[locale]/dashboard/_components/user/user-management-table.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsxsrc/app/[locale]/dashboard/providers/page.tsxsrc/app/[locale]/dashboard/users/users-page-client.tsxsrc/app/api/auth/login/route.tssrc/components/form/form-layout.tsxsrc/components/section.tsxsrc/components/ui/relative-time.tsxsrc/lib/redis/index.tssrc/lib/redis/scan-helper.tssrc/repository/key.tssrc/repository/user.tssrc/types/user.tstests/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.tsxsrc/repository/user.tssrc/lib/redis/index.tssrc/actions/key-quota.tssrc/lib/redis/scan-helper.tssrc/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsxsrc/app/[locale]/dashboard/_components/user/add-key-dialog.tsxsrc/components/ui/relative-time.tsxsrc/types/user.tssrc/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsxsrc/repository/key.tstests/unit/lib/redis/scan-helper.test.tssrc/app/[locale]/dashboard/_components/user/edit-user-dialog.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/dashboard/_components/user/edit-key-dialog.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsxsrc/components/section.tsxsrc/app/[locale]/dashboard/providers/page.tsxsrc/app/[locale]/dashboard/_components/user/user-key-table-row.tsxsrc/components/form/form-layout.tsxsrc/app/api/auth/login/route.tssrc/app/[locale]/dashboard/_components/user/user-management-table.tsxsrc/actions/users.tssrc/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.tsxsrc/repository/user.tssrc/lib/redis/index.tssrc/actions/key-quota.tssrc/lib/redis/scan-helper.tssrc/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsxsrc/app/[locale]/dashboard/_components/user/add-key-dialog.tsxsrc/components/ui/relative-time.tsxsrc/types/user.tssrc/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsxsrc/repository/key.tstests/unit/lib/redis/scan-helper.test.tssrc/app/[locale]/dashboard/_components/user/edit-user-dialog.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/dashboard/_components/user/edit-key-dialog.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsxsrc/components/section.tsxsrc/app/[locale]/dashboard/providers/page.tsxsrc/app/[locale]/dashboard/_components/user/user-key-table-row.tsxsrc/components/form/form-layout.tsxsrc/app/api/auth/login/route.tssrc/app/[locale]/dashboard/_components/user/user-management-table.tsxsrc/actions/users.tssrc/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.tsxsrc/repository/user.tssrc/lib/redis/index.tssrc/actions/key-quota.tssrc/lib/redis/scan-helper.tssrc/app/[locale]/dashboard/_components/user/batch-edit/batch-edit-dialog.tsxsrc/app/[locale]/dashboard/_components/user/add-key-dialog.tsxsrc/components/ui/relative-time.tsxsrc/types/user.tssrc/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsxsrc/repository/key.tstests/unit/lib/redis/scan-helper.test.tssrc/app/[locale]/dashboard/_components/user/edit-user-dialog.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/dashboard/_components/user/edit-key-dialog.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsxsrc/components/section.tsxsrc/app/[locale]/dashboard/providers/page.tsxsrc/app/[locale]/dashboard/_components/user/user-key-table-row.tsxsrc/components/form/form-layout.tsxsrc/app/api/auth/login/route.tssrc/app/[locale]/dashboard/_components/user/user-management-table.tsxsrc/actions/users.tssrc/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.tssrc/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.tsxmessages/en/auth.jsonsrc/app/[locale]/dashboard/_components/user/add-key-dialog.tsxmessages/ja/auth.jsonsrc/types/user.tssrc/app/[locale]/dashboard/_components/user/forms/edit-key-form.tsxsrc/app/[locale]/dashboard/_components/user/edit-user-dialog.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/_components/user/edit-key-dialog.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/app/[locale]/dashboard/_components/user/user-key-table-row.tsxmessages/zh-CN/auth.jsonmessages/zh-CN/dashboard.jsonsrc/app/[locale]/dashboard/_components/user/user-management-table.tsxmessages/zh-TW/dashboard.jsonmessages/ja/dashboard.jsonmessages/ru/quota.jsonsrc/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.tssrc/lib/redis/index.tssrc/actions/key-quota.tssrc/lib/redis/scan-helper.tssrc/types/user.tssrc/repository/key.tssrc/app/api/auth/login/route.tssrc/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.tssrc/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.jsonmessages/ja/auth.jsonsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxmessages/zh-CN/auth.jsonmessages/zh-CN/dashboard.jsonsrc/app/api/auth/login/route.tsmessages/zh-TW/auth.jsonsrc/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.tsxsrc/app/[locale]/dashboard/_components/user/key-row-item.tsxsrc/app/[locale]/dashboard/_components/user/forms/add-key-form.tsxsrc/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.id和keyRow.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!新增的三个错误键(
apiKeyRequired、apiKeyInvalidOrExpired、serverError)翻译准确,与其他语言文件保持一致。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 后同时失效
userKeyGroups和userTags查询缓存,确保相关 UI 组件能获取最新数据。此模式与batch-edit-dialog.tsx保持一致。src/types/user.ts (2)
103-103:todayTokens字段类型定义正确新增的
todayTokens: number字段与现有的todayUsage和todayCallCount保持一致的类型定义(必填数字),符合接口设计规范。
171-172: 访问限制字段扩展合理在
KeyDialogUserContext中添加allowedClients和allowedModels可选字段,使密钥对话框能够展示用户级别的访问限制信息,与User接口中的定义保持一致。src/app/[locale]/dashboard/_components/user/forms/add-key-form.tsx (1)
219-235: 国际化实现正确,所有翻译已完成Cache TTL 相关的用户界面文本已正确使用
t()函数进行国际化。已验证cacheTtl.label、cacheTtl.options.inherit、cacheTtl.options.5m、cacheTtl.options.1h和cacheTtl.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! 新增刷新回调属性设计合理。
onRefresh和isRefreshing作为可选属性,保持了向后兼容性,符合组件扩展的最佳实践。
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-b和border-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 样式以区分
- 其他分组使用动态颜色(通过
getGroupColor和getContrastTextColor)- 超出显示限制时显示 "+N" 徽章
- Tooltip 显示完整分组列表
条件渲染使用
userGroups.length > 0 ? ... : null是正确的模式。
505-505: todayTokens 数据传递正确。
todayTokens字段已正确添加到传递给KeyRowItem的keyData对象中,与仓库层(findKeyUsageTodayBatch)和类型定义的更改保持一致。src/actions/users.ts (4)
213-218: usageLookup 结构更新正确。
usageLookup现在返回包含totalCost和totalTokens的对象,与findKeyUsageTodayBatch的返回类型变更保持一致。空值合并处理 (?? 0) 确保了默认值的安全性。
1540-1541: 硬删除 messageRequest 记录需确认。当前实现使用
db.delete(messageRequest)进行硬删除,而非设置deletedAt的软删除。这与 schema 中messageRequest表定义的deletedAt字段设计模式不一致。请确认这是预期行为。如果是为了彻底清除用户数据以满足合规要求(如 GDPR),硬删除是合理的;否则建议使用软删除以保持一致性。
1569-1579: Pipeline 删除实现高效。使用 Redis pipeline 批量删除是正确的做法,相比逐个删除可显著提高性能。代码正确处理了:
- 活跃会话键 (
key:${keyId}:active_sessions)- 成本统计键(通过 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/30和bg-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: 翻译字段扩展完整。新增
todayTokens和tokensLabel翻译字段,确保了 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-fns的formatDistance行为一致。
88-106: LGTM!条件格式化逻辑和依赖数组正确。
format === "short"时使用自定义的formatShortDistance- 否则使用
date-fns的formatDateDistance- 依赖数组包含了所有必要的依赖项:
date,autoUpdate,updateInterval,locale,format,formatShortDistancemessages/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"是合理的选择,因为:
- 表格空间有限(
flex-[0.8] min-w-[80px])- 短格式更适合高密度数据展示
- 用户可通过 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 覆盖选项的翻译键结构清晰,包含了
inherit、5m、1h三个选项,与 PR 目标中提到的缓存 TTL 覆盖功能一致。
1156-1170: LGTM!用户管理表格中的翻译更新合理:
todayUsage重命名为 "Requests today"- 新增
todayTokens用于显示 "Tokens today"- 新增
refresh按钮文本- 字段标签
callsLabel和tokensLabel命名一致这些更改与 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 翻译
- 使用
Layers和ShieldCheck图标提供清晰的视觉指示- 正确处理了空值/无限制的情况(使用
tProviderGroup("allProviders")和tRestrictions("noRestrictions"))
707-708: LGTM!正确将
refetch回调和isRefreshing状态传递给UserManagementTable组件,使表格能够支持手动刷新功能。这与 PR 目标中添加刷新按钮的需求一致。
768-775:InlineLoading组件正在被使用。
InlineLoading在UsersTableSkeleton组件中被正确使用(第 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→ 今日請求、新增todayTokens、callsLabel/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.
src/app/[locale]/dashboard/_components/user/edit-key-dialog.tsx
Outdated
Show resolved
Hide resolved
| } 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 }); | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- i18n/localization changes (messages/*.json files)
- User management UI improvements (user table, key display, tokens tracking)
- Statistics reset feature (resetUserAllStatistics + Redis scan helper)
- 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)
- [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
- The change from
Medium Priority Issues (Consider Fixing)
- [TEST-MISSING-CRITICAL]
src/actions/users.ts- No test forresetUserAllStatisticsfunction- Per CLAUDE.md: "All new features must have unit test coverage of at least 80%"
- The
resetUserAllStatisticsfunction is a critical admin operation that deletes user data - The
scanPatternhelper 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
resetUserAllStatisticsis well-implemented - Error handling in the new function properly logs and continues execution
- i18n translations are complete across all 5 locales
- The
scanPatternhelper 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>
There was a problem hiding this comment.
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.
src/actions/users.ts
Outdated
There was a problem hiding this comment.
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.
| // Параллельно scan всех паттернов | |
| // Scan all patterns in parallel |
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>
* 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>
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
Related Issues:
Solution
User Management Enhancements
todayTokensin user tableresetUserAllStatisticsaction with optimized Redis pipeline (batch delete via SCAN + pipeline)i18n Improvements
relativeTimeShorttranslations for compact time displayDashboard Improvements
Changes
Core Changes
src/actions/users.ts- AddresetUserAllStatisticsaction with Redis pipeline optimizationsrc/lib/redis/scan-helper.ts- New Redis SCAN utility for batch key operationssrc/app/[locale]/dashboard/_components/user/*- User management UI improvementssrc/app/[locale]/dashboard/providers/page.tsx- Add Auto Sort buttonSupporting Changes
src/actions/key-quota.ts- Read-only access supportsrc/components/ui/relative-time.tsx- Short format support, useCallback fixsrc/app/[locale]/dashboard/logs/_components/*- Logs filter reset optionssrc/repository/key.ts- Add tokens to key datasrc/types/user.ts- AddtodayTokenstypei18n Updates
messages/*/auth.json- Login error messagesmessages/*/common.json- Short relative time formatsmessages/*/dashboard.json- User management, logs, providers translationsmessages/*/quota.json- Quota descriptions with user limitsTesting
Automated Tests
scan-helper.ts(tests/unit/lib/redis/scan-helper.test.ts)Manual Testing
Checklist
bun run typecheck)bun run lint)bun run build)Description enhanced by Claude AI