Conversation
📝 WalkthroughWalkthrough该PR新增“我的用量”只读API端点、扩展认证以支持 Authorization: Bearer 与只读令牌,排除 warmup 日志的查询条件,增强 system_settings 容错与若干仓库查询,并补充大量单元/集成测试与专用 Vitest 配置(覆盖与并行清理协调)。 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Summary of ChangesHello @ding113, 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! 此拉取请求主要旨在提升用户在 my-usage 页面上的数据准确性和 API 访问的灵活性。通过修复今日使用统计的计算逻辑,用户现在可以获得正确的用量数据。同时,引入只读 API 自助查询端点和增强的认证机制,使得外部系统能够更便捷、安全地访问用户用量信息。此外,还对系统配置的数据库兼容性进行了改进,并增加了全面的测试覆盖,以确保功能的稳定性和安全性。 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.
Code Review
本次 PR 质量很高,有效地修复了 my-usage 页面的今日统计功能,并通过引入 allowReadOnlyAccess 机制和 Authorization: Bearer 支持,安全地开放了只读 API,提升了系统的灵活性。代码层面,通过使用 Drizzle 的 lt 操作符替代原生 SQL 片段,修复了潜在的 SQL 注入风险,这是一个关键的安全改进。同时,对 getSystemSettings 的重构增强了对旧数据库架构的兼容性,使系统更具鲁棒性。此外,PR 包含了大量高质量的单元测试和集成测试,特别是为新功能增加了专门的测试覆盖率配置,这极大地保证了代码的正确性和未来的可维护性。整体而言,这是一次非常出色的代码贡献。
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/repository/system-config.ts (1)
74-133: 建议抽取通用的递归错误检测逻辑以减少重复代码。
isUndefinedColumnError与isTableMissingError(第12-72行)结构几乎完全相同,仅错误码和匹配字符串不同。可考虑抽取一个通用的递归错误检测工厂函数。🔎 重构建议
type ErrorMatcher = (value: string) => boolean; function createRecursiveErrorChecker(matcher: ErrorMatcher) { return function check(error: unknown, depth = 0): boolean { if (!error || depth > 5) return false; if (typeof error === "string") { return matcher(error.toLowerCase()); } if (typeof error === "object") { const err = error as { code?: unknown; message?: unknown; cause?: unknown; errors?: unknown; originalError?: unknown; }; if (typeof err.code === "string" && matcher(err.code.toLowerCase())) { return true; } if (typeof err.message === "string" && check(err.message, depth + 1)) { return true; } if ("cause" in err && err.cause && check(err.cause, depth + 1)) { return true; } if (Array.isArray(err.errors)) { return err.errors.some((item) => check(item, depth + 1)); } if (err.originalError && check(err.originalError, depth + 1)) { return true; } try { const stringified = String(error); if (stringified && check(stringified, depth + 1)) return true; } catch { /* ignore */ } } return false; }; } const isTableMissingError = createRecursiveErrorChecker((s) => s.includes("42p01") || (s.includes("system_settings") && (s.includes("does not exist") || s.includes("doesn't exist") || s.includes("找不到"))) ); const isUndefinedColumnError = createRecursiveErrorChecker((s) => s.includes("42703") || (s.includes("column") && (s.includes("does not exist") || s.includes("doesn't exist") || s.includes("不存在"))) );src/actions/my-usage.ts (1)
23-24: Warmup 排除条件定义正确,但存在重复定义。该条件与
src/repository/usage-logs.ts第 9 行的EXCLUDE_WARMUP_CONDITION完全相同。考虑提取到共享模块避免重复。🔎 可选:提取到共享常量
在
src/repository/usage-logs.ts中导出该常量:-const EXCLUDE_WARMUP_CONDITION = sql`(${messageRequest.blockedBy} IS NULL OR ${messageRequest.blockedBy} <> 'warmup')`; +export const EXCLUDE_WARMUP_CONDITION = sql`(${messageRequest.blockedBy} IS NULL OR ${messageRequest.blockedBy} <> 'warmup')`;然后在
src/actions/my-usage.ts中导入:-// Warmup 抢答请求只用于探测/预热:日志可见,但不计入任何聚合统计 -const EXCLUDE_WARMUP_CONDITION = sql`(${messageRequest.blockedBy} IS NULL OR ${messageRequest.blockedBy} <> 'warmup')`; +import { EXCLUDE_WARMUP_CONDITION } from "@/repository/usage-logs";tests/api/action-adapter-openapi.unit.test.ts (1)
173-196: 测试覆盖基本功能,可考虑增强 createActionRoutes 验证。当前
createActionRoutes测试仅验证返回数组长度,未验证生成的 route/handler 是否可用。🔎 可选:增强 createActionRoutes 测试
test("createActionRoutes:应批量生成 route/handler", () => { const routes = createActionRoutes( "demo", { a: async () => ({ ok: true, data: 1 }), b: async () => 2, }, { b: { requiresAuth: false }, } ); expect(routes).toHaveLength(2); + for (const { route, handler } of routes) { + expect(route).toBeDefined(); + expect(typeof handler).toBe("function"); + } });tests/api/my-usage-readonly.test.ts (1)
321-321: 精度参数过高,建议简化。
toBeCloseTo(0.02, 10)中精度参数 10 表示比较到小数点后 10 位,对于 USD 金额过于严格。建议使用 2-4 位。- expect(data.costUsd).toBeCloseTo(0.02, 10); + expect(data.costUsd).toBeCloseTo(0.02, 4);tests/api/auth.unit.test.ts (1)
29-39: Mock 与 Next.js 15+ 异步 cookies API 的兼容性在 Next.js 15/16 中,
cookies()函数是异步的,返回Promise<ReadonlyRequestCookies>。当前 mock 返回的是一个同步对象。虽然由于await在非 Promise 值上会直接返回该值,这个 mock 在当前测试中仍然可以工作,但建议显式返回 Promise 以保持与实际 API 的一致性,避免未来潜在的兼容性问题。🔎 建议的改进
vi.mock("next/headers", () => ({ - cookies: () => ({ + cookies: async () => ({ get: (name: string) => { if (name !== "auth-token") return undefined; return currentCookieValue ? { value: currentCookieValue } : undefined; }, set: cookieSet, delete: cookieDelete, has: (name: string) => name === "auth-token" && Boolean(currentCookieValue), }), }));
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
📒 Files selected for processing (16)
package.jsonsrc/actions/my-usage.tssrc/app/api/actions/[...route]/route.tssrc/lib/api/action-adapter-openapi.tssrc/repository/system-config.tssrc/repository/usage-logs.tstests/api/action-adapter-openapi.unit.test.tstests/api/api-actions-integrity.test.tstests/api/auth.unit.test.tstests/api/my-usage-readonly.test.tstests/cleanup-utils.tstests/setup.tstests/test-utils.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/repository/warmup-stats-exclusion.test.tsvitest.my-usage.config.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx,js,jsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Use 2-space indentation in all code files
Files:
src/repository/usage-logs.tstests/api/api-actions-integrity.test.tssrc/repository/system-config.tssrc/actions/my-usage.tspackage.jsontests/api/action-adapter-openapi.unit.test.tstests/api/my-usage-readonly.test.tstests/unit/repository/warmup-stats-exclusion.test.tstests/setup.tstests/api/auth.unit.test.tstests/test-utils.tsvitest.my-usage.config.tssrc/lib/api/action-adapter-openapi.tstests/cleanup-utils.tssrc/app/api/actions/[...route]/route.tstests/unit/proxy/session-guard-warmup-intercept.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use double quotes for strings instead of single quotes
Use trailing commas in multi-line structures
Enforce maximum line length of 100 characters
Use path alias@/*to reference files from./src/*directory
**/*.{ts,tsx,js,jsx}: Use Biome for linting and formatting with 2-space indent, double quotes, trailing commas, and 100 character max line length
Use path alias@/*to reference files in./src/*directory
Files:
src/repository/usage-logs.tstests/api/api-actions-integrity.test.tssrc/repository/system-config.tssrc/actions/my-usage.tstests/api/action-adapter-openapi.unit.test.tstests/api/my-usage-readonly.test.tstests/unit/repository/warmup-stats-exclusion.test.tstests/setup.tstests/api/auth.unit.test.tstests/test-utils.tsvitest.my-usage.config.tssrc/lib/api/action-adapter-openapi.tstests/cleanup-utils.tssrc/app/api/actions/[...route]/route.tstests/unit/proxy/session-guard-warmup-intercept.test.ts
src/repository/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Repository pattern in
src/repository/to wrap Drizzle queries
Files:
src/repository/usage-logs.tssrc/repository/system-config.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript strict mode for type safety
Use readonly or const assertions for immutable data structures
Files:
src/repository/usage-logs.tstests/api/api-actions-integrity.test.tssrc/repository/system-config.tssrc/actions/my-usage.tstests/api/action-adapter-openapi.unit.test.tstests/api/my-usage-readonly.test.tstests/unit/repository/warmup-stats-exclusion.test.tstests/setup.tstests/api/auth.unit.test.tstests/test-utils.tsvitest.my-usage.config.tssrc/lib/api/action-adapter-openapi.tstests/cleanup-utils.tssrc/app/api/actions/[...route]/route.tstests/unit/proxy/session-guard-warmup-intercept.test.ts
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Hash API keys using SHA-256 before storing in database, never store plaintext keys
Mask API keys and sensitive data in application logs
Validate required environment variables at startup with clear error messages
Files:
src/repository/usage-logs.tssrc/repository/system-config.tssrc/actions/my-usage.tssrc/lib/api/action-adapter-openapi.tssrc/app/api/actions/[...route]/route.ts
src/**/*{message,response,log}*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Log request duration, token usage, and cost to message_request table for analytics
Files:
src/repository/usage-logs.ts
src/{repository,actions}/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Avoid N+1 queries by using eager loading and batch queries for statistics
Files:
src/repository/usage-logs.tssrc/repository/system-config.tssrc/actions/my-usage.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Vitest for unit testing with Node environment, coverage thresholds: 50% lines/functions, 40% branches
Files:
tests/api/api-actions-integrity.test.tstests/api/action-adapter-openapi.unit.test.tstests/api/my-usage-readonly.test.tstests/unit/repository/warmup-stats-exclusion.test.tstests/api/auth.unit.test.tstests/unit/proxy/session-guard-warmup-intercept.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
Ensure test database names contain 'test' keyword for safety validation
Files:
tests/api/api-actions-integrity.test.tstests/api/action-adapter-openapi.unit.test.tstests/api/my-usage-readonly.test.tstests/unit/repository/warmup-stats-exclusion.test.tstests/api/auth.unit.test.tstests/unit/proxy/session-guard-warmup-intercept.test.ts
src/actions/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/actions/**/*.ts: Validate all user inputs with Zod schemas before processing
Use Server Actions innext-safe-actionwith OpenAPI generation for admin API endpoints
Use Next.js API Routes and Server Actions for admin operations and REST endpoints
Files:
src/actions/my-usage.ts
**/*.{tsx,json}
📄 CodeRabbit inference engine (AGENTS.md)
Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW
Files:
package.json
src/lib/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use connection pooling for database and Redis connections
Files:
src/lib/api/action-adapter-openapi.ts
src/app/api/actions/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Server Actions in
src/app/api/actions/must auto-generate OpenAPI 3.1.0 spec from Zod schemas and expose as REST endpoints
Files:
src/app/api/actions/[...route]/route.ts
src/app/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Implement Content-Security-Policy headers for XSS prevention
Files:
src/app/api/actions/[...route]/route.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Implement health check endpoint returning database and Redis status
Files:
src/app/api/actions/[...route]/route.ts
🧠 Learnings (24)
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/app/api/actions/**/*.ts : Server Actions in `src/app/api/actions/` must auto-generate OpenAPI 3.1.0 spec from Zod schemas and expose as REST endpoints
Applied to files:
tests/api/api-actions-integrity.test.tstests/api/action-adapter-openapi.unit.test.tssrc/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/actions/**/*.ts : Use Server Actions in `next-safe-action` with OpenAPI generation for admin API endpoints
Applied to files:
tests/api/api-actions-integrity.test.tstests/api/action-adapter-openapi.unit.test.tssrc/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/drizzle/**/*.ts : Use soft delete pattern with `deletedAt` column instead of hard deletes
Applied to files:
src/actions/my-usage.tstests/cleanup-utils.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*{message,response,log}*.ts : Log request duration, token usage, and cost to message_request table for analytics
Applied to files:
src/actions/my-usage.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/{repository,actions}/**/*.ts : Avoid N+1 queries by using eager loading and batch queries for statistics
Applied to files:
src/actions/my-usage.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/drizzle/**/*.ts : Use Drizzle ORM with PostgreSQL for database operations
Applied to files:
src/actions/my-usage.tstests/cleanup-utils.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/drizzle/**/*.ts : Use Drizzle ORM with parameterized queries to prevent SQL injection
Applied to files:
src/actions/my-usage.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/drizzle/**/*.ts : Implement proper indexing strategy for common queries and foreign keys
Applied to files:
src/actions/my-usage.tstests/cleanup-utils.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/drizzle/**/*.ts : Use JSON columns in PostgreSQL for flexible data structures (modelRedirects, providerChain, etc.)
Applied to files:
src/actions/my-usage.tstests/cleanup-utils.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/repository/**/*.ts : Use Repository pattern in `src/repository/` to wrap Drizzle queries
Applied to files:
src/actions/my-usage.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to **/*.test.{ts,tsx} : Use Vitest for unit testing with Node environment, coverage thresholds: 50% lines/functions, 40% branches
Applied to files:
package.jsontests/setup.tstests/api/auth.unit.test.tsvitest.my-usage.config.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to tests/integration/**/*.test.{ts,tsx} : Integration tests requiring database must be placed in `tests/integration/` and use test database with 'test' in the name
Applied to files:
package.jsontests/api/my-usage-readonly.test.tstests/setup.tstests/api/auth.unit.test.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to **/*.test.ts : Ensure test database names contain 'test' keyword for safety validation
Applied to files:
package.jsontests/setup.tstests/api/auth.unit.test.tstests/cleanup-utils.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to tests/integration/**/*.test.ts : Place integration tests requiring database in `tests/integration/` (excluded by default)
Applied to files:
package.jsontests/setup.tstests/api/auth.unit.test.tsvitest.my-usage.config.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/actions/**/*.ts : Use Next.js API Routes and Server Actions for admin operations and REST endpoints
Applied to files:
tests/api/action-adapter-openapi.unit.test.tstests/test-utils.tssrc/app/api/actions/[...route]/route.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to **/*.{ts,tsx} : Use readonly or const assertions for immutable data structures
Applied to files:
tests/api/my-usage-readonly.test.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*.ts : Validate required environment variables at startup with clear error messages
Applied to files:
tests/setup.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*.ts : Mask API keys and sensitive data in application logs
Applied to files:
tests/setup.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*{guard,auth}*.ts : Use constant-time comparison for API key validation to prevent timing attacks
Applied to files:
tests/api/auth.unit.test.tssrc/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/app/v1/_lib/**/*.ts : Guard pipeline must execute in order: ProxyAuthenticator, SensitiveWordGuard, VersionGuard, ProxySessionGuard, ProxyRateLimitGuard, ProxyProviderResolver
Applied to files:
tests/api/auth.unit.test.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*.ts : Implement guard pipeline pattern for cross-cutting concerns in request processing (auth, rate limiting, session)
Applied to files:
tests/api/auth.unit.test.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*.ts : Use undici library for HTTP requests instead of node-fetch for better performance
Applied to files:
tests/test-utils.ts
📚 Learning: 2026-01-03T09:10:02.182Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 517
File: src/app/v1/_lib/proxy/auth-guard.ts:205-232
Timestamp: 2026-01-03T09:10:02.182Z
Learning: In `src/app/v1/_lib/proxy/auth-guard.ts`, the `extractApiKeyFromHeaders` function is intentionally a standalone simplified version for non-Guard flows. It deliberately does not check for multi-source conflicts (unlike `ProxyAuthenticator.validate`), and the code duplication is intentional to avoid coupling between Guard and non-Guard authentication flows.
Applied to files:
src/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.020Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.020Z
Learning: Keep utility functions single-purpose and reuse helpers from `src/lib` and `src/actions`
Applied to files:
src/app/api/actions/[...route]/route.ts
🧬 Code graph analysis (9)
src/repository/usage-logs.ts (1)
src/drizzle/schema.ts (1)
messageRequest(264-345)
src/repository/system-config.ts (2)
src/drizzle/schema.ts (1)
systemSettings(439-473)src/repository/_shared/transformers.ts (1)
toSystemSettings(140-158)
tests/api/action-adapter-openapi.unit.test.ts (1)
src/lib/api/action-adapter-openapi.ts (6)
createActionRoute(253-419)createActionRoutes(451-460)createParamSchema(466-467)IdParamSchema(472-474)PaginationSchema(479-482)SortSchema(487-490)
tests/api/my-usage-readonly.test.ts (3)
src/drizzle/db.ts (1)
db(37-44)src/drizzle/schema.ts (3)
users(33-84)keys(87-126)messageRequest(264-345)tests/test-utils.ts (1)
callActionsRoute(36-77)
tests/setup.ts (2)
src/lib/rate-limit/service.ts (1)
redis(97-99)tests/cleanup-utils.ts (1)
cleanupRecentTestData(86-88)
tests/api/auth.unit.test.ts (1)
src/lib/auth.ts (6)
validateKey(17-86)getSession(116-128)setAuthCookie(94-104)getAuthCookie(106-109)clearAuthCookie(111-114)getLoginRedirectTarget(88-92)
src/lib/api/action-adapter-openapi.ts (1)
src/lib/auth.ts (1)
validateKey(17-86)
tests/cleanup-utils.ts (2)
src/drizzle/db.ts (1)
db(37-44)src/drizzle/schema.ts (1)
users(33-84)
src/app/api/actions/[...route]/route.ts (1)
src/lib/api/action-adapter-openapi.ts (1)
createActionRoute(253-419)
⏰ 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). (8)
- GitHub Check: Greptile Review
- GitHub Check: pr-review
- GitHub Check: check-codex-status
- GitHub Check: pr-label
- GitHub Check: pr-description
- GitHub Check: 🌐 API Tests
- GitHub Check: Docker Build Test
- GitHub Check: dev-build-deploy
🔇 Additional comments (34)
src/repository/system-config.ts (2)
160-205: 降级读取逻辑设计合理。
selectSettingsRow内部 helper 采用"先尝试全字段、失败后降级到最小字段集"的策略,能够优雅地兼容尚未完成迁移的旧版数据库。toSystemSettings转换器会为缺失字段填充默认值,确保返回完整的SystemSettings对象。
214-224:onConflictDoNothing+ 后续查询模式正确处理并发竞态。使用
onConflictDoNothing避免了多进程并发初始化时的唯一约束冲突,随后调用selectSettingsRow确保获取实际写入的记录。这种模式比.returning()更健壮,因为returning在onConflictDoNothing场景下不会返回已存在的行。tests/test-utils.ts (2)
53-57: LGTM:Bearer 令牌认证支持正确实现了 Authorization 头的后备逻辑,与 PR 中新增的 bearerAuth 认证方式保持一致。使用
??运算符避免覆盖已存在的 Authorization 头是正确的做法。
63-67: 代码正确使用了 undici.Request,与 Next.js 路由处理器完全兼容undici^7 提供的 Request 实现遵循 Web API 标准,Hono 的 handle() 函数(通过 hono/vercel)完全支持任何符合 Web API 的 Request 对象。这种测试方式是标准的 in-process 测试模式,避免了启动完整开发服务器的开销,同时保留了 HTTP 语义验证。代码实现无需修改。
tests/unit/proxy/session-guard-warmup-intercept.test.ts (1)
40-49: LGTM:完善 logger mock 覆盖扩展了 logger mock 以包含
info和fatal方法,与其他测试文件中的类似更新保持一致。这确保了测试代码可以调用所有 logger 方法而不会出错。tests/unit/repository/warmup-stats-exclusion.test.ts (1)
180-189: LGTM:完善 logger mock 覆盖扩展了 logger mock 以包含
debug、info、warn和fatal方法,确保测试中所有 logger 调用都有对应的 mock。与其他测试文件的更新保持一致。tests/cleanup-utils.ts (3)
7-9: LGTM:导入优化正确添加了
inArray用于新的查询构建器语法,并将keys别名为keysTable以避免与 JavaScript 关键字冲突。
56-68: 优秀:迁移到 Drizzle 查询构建器从原始 SQL 迁移到 Drizzle 查询构建器是正确的改进:
- 使用共享的
now时间戳确保 keys 和 users 的deletedAt和updatedAt一致- 正确实现了软删除模式(使用
deletedAt列,符合 learnings)- 类型安全且更易维护
- WHERE 条件逻辑正确:
inArray用于 ID 筛选,isNull确保不重复删除根据学习记录,基于 learnings...
74-74: LGTM:正确调整返回值处理使用
.length获取数组长度是正确的,因为 Drizzle 的.returning()返回的是记录数组而非包含 count 属性的对象。tests/setup.ts (3)
13-16: LGTM:简化测试输出为 dotenv 配置添加
quiet: true可以抑制环境变量加载消息,使测试输出更清晰。
67-104: 良好的并行清理协调机制Redis 计数器协调逻辑设计合理:
- 使用数据库名称隔离不同测试套件的计数器
- 2 秒 Redis 就绪超时确保不会无限等待
- 15 分钟过期时间防止异常退出导致的计数器泄漏
- 失败时仅警告而不影响测试运行
但需要注意该逻辑的复杂性,建议添加文档说明计数器的生命周期。
116-150: 清理协调逻辑正确但需注意潜在竞态afterAll 中的计数器递减和清理逻辑设计合理:
- 只有最后一个 worker(
remaining <= 0)执行清理- 清理后删除计数器键防止重用
- 多层级的错误处理确保测试不会因清理失败而中断
注意事项:
虽然 Redis 命令本身是原子的,但在极端情况下(如多个 worker 几乎同时完成),decr和del之间可能存在微小时间窗口。不过由于decr返回值决定是否清理,且 Redis 命令是原子的,实际风险很低。tests/api/api-actions-integrity.test.ts (2)
116-130: LGTM!新增的 my-usage 模块端点完整性测试符合规范。测试结构与其他模块保持一致,覆盖了所有 6 个 my-usage 端点。
227-240: 缺少 my-usage 模块的标签映射。
moduleTagMapping中未添加/api/actions/my-usage/的标签映射。如果 my-usage 端点在 OpenAPI 中配置了tags,这个测试会因标签不匹配而失败。请确认 my-usage 端点是否配置了 OpenAPI tags,如有需要,请补充:
const moduleTagMapping: Record<string, string> = { "/api/actions/users/": "用户管理", "/api/actions/keys/": "密钥管理", "/api/actions/providers/": "供应商管理", "/api/actions/model-prices/": "模型价格", "/api/actions/statistics/": "统计分析", "/api/actions/usage-logs/": "使用日志", "/api/actions/overview/": "概览", "/api/actions/sensitive-words/": "敏感词管理", "/api/actions/active-sessions/": "Session 管理", "/api/actions/notifications/": "通知管理", "/api/actions/webhook-targets/": "通知管理", "/api/actions/notification-bindings/": "通知管理", + "/api/actions/my-usage/": "我的用量", };package.json (1)
22-22: LGTM!新增的 my-usage 专项覆盖率脚本符合规范。命名和配置模式与现有的
test:coverage:quota保持一致。src/repository/usage-logs.ts (1)
268-281: LGTM!模型筛选口径统一修复正确。将
COALESCE(original_model, model)改为直接使用messageRequest.model,与findUsageLogsWithDetails的筛选字段保持一致,避免筛选器下拉列表和实际筛选结果不匹配的问题。src/actions/my-usage.ts (2)
305-313: LGTM!时间范围查询修复正确。使用
gte()和lt()替代原始 SQL 模板,类型更安全且与 Drizzle ORM 风格一致。Warmup 排除条件正确应用于聚合查询。
325-333: LGTM!Breakdown 查询与聚合查询条件保持一致。Warmup 排除条件正确应用于模型分组统计,确保"今日统计"与"模型明细"口径统一。
tests/api/action-adapter-openapi.unit.test.ts (2)
1-38: LGTM!测试辅助函数设计合理。
createMockContext模拟了 Hono 上下文的核心接口,支持自定义请求体和 JSON 解析异常场景,覆盖了测试所需的关键路径。
40-171: LGTM!执行器逻辑测试覆盖全面。测试用例涵盖了:
- 非 ActionResult 返回值自动包装
- 单字段/多字段参数推断
- 显式 argsMapper 映射
- 业务错误透传(ok=false)
- Error 与非 Error 异常处理
- 无效 JSON 降级处理
vitest.my-usage.config.ts (1)
11-58: LGTM!my-usage 专项覆盖率配置合理。配置要点:
- 针对安全敏感的只读 API 设置更高覆盖率阈值(80%)
- 独立的覆盖率报告目录避免与全局覆盖率混淆
- 测试文件精准包含,避免不相关模块影响阈值
tests/api/my-usage-readonly.test.ts (4)
20-30: LGTM!next/headers mock 实现正确。通过
currentAuthToken变量同步 adapter 和 action 的认证状态,避免测试中的假失败。
118-141: LGTM!afterAll 清理逻辑正确使用软删除。符合项目规范,避免硬删除带来的外键约束问题。清理顺序(message → keys → users)也符合依赖关系。
212-379: LGTM!今日统计测试覆盖全面。测试验证了:
- Warmup 日志排除(统计口径)
- 跨用户数据隔离
- 模型分组统计
- billingModelSource 配置一致性
- 筛选器接口可用性
76-111: 无须修改 - 当前实现不存在外键约束风险。
message_request表的providerId列在 Drizzle schema 中定义为integer('provider_id').notNull(),未使用.references()方法。Drizzle 的relations()API 仅定义 ORM 层级的关系(用于查询便利),不会创建数据库层级的外键约束。因此硬编码的providerId: 0不会触发任何外键约束错误。tests/api/auth.unit.test.ts (3)
44-77: 测试辅助函数设计良好
createTestUser和createTestKey辅助函数结构清晰,包含了显式的错误处理(插入失败时抛出异常),这有助于快速定位测试数据准备阶段的问题。
79-189: 安全边界测试覆盖全面测试套件覆盖了以下关键场景:
- Admin Token 直接验证(无需 DB 查询)
- 不存在的 key 返回 null
canLoginWebUi=false且allowReadOnlyAccess=false时拒绝访问allowReadOnlyAccess=true时允许只读 key 访问- 软删除用户的 key 返回 null
getSession在有/无 Cookie 时的行为清理逻辑使用软删除模式,避免影响测试数据库的完整性约束。
191-229: Cookie 工具函数与跳转目标测试完整测试验证了:
setAuthCookie/getAuthCookie/clearAuthCookie的读写一致性getLoginRedirectTarget根据role和canLoginWebUi正确返回跳转路径覆盖了
getLoginRedirectTarget的三个分支逻辑,测试结构清晰。src/lib/api/action-adapter-openapi.ts (2)
69-78: allowReadOnlyAccess 选项文档清晰,安全警告到位新增的
allowReadOnlyAccess选项文档明确说明了其用途和安全边界:
- 仅用于"只读且强制绑定当前会话"的端点
- 明确警告不能用于可传入 userId/keyId 等可能导致越权的管理型接口
默认值为
false,遵循最小权限原则。
305-312: 认证流程扩展设计合理认证逻辑的改进点:
- Cookie 优先,Bearer Token 作为备选(适合脚本/CLI 调用场景)
allowReadOnlyAccess选项正确传递给validateKey- OpenAPI 安全定义同时声明了
cookieAuth和bearerAuthsrc/app/api/actions/[...route]/route.ts (4)
59-65: bearerAuth 安全方案注册正确新增的
bearerAuth安全方案使用 HTTP Bearer 认证,描述清晰说明了适用场景(脚本/CLI 调用)以及 token 与 Cookie 中auth-token值一致的特性。
619-646: my-usage 模块首个端点结构良好
getMyUsageMetadata端点设计合理:
allowReadOnlyAccess: true允许只读 Key 访问- 响应 schema 详细定义了返回字段及其描述
- 标签分类为"概览"
作为该模块的模板,后续端点结构一致。
734-783: getMyUsageLogs 端点分页与过滤设计完整日志查询端点支持:
- 时间范围过滤(
startDate/endDate)- 模型、endpoint、状态码过滤
- 分页参数(
pageSize默认 20,最大 100)响应 schema 包含完整的日志字段定义,
allowReadOnlyAccess: true确保只读 Key 可访问自己的数据。
785-805: 筛选项端点设计简洁
getMyAvailableModels和getMyAvailableEndpoints两个端点:
- 无需请求参数
- 返回字符串数组
- 用于日志筛选下拉框填充
设计简洁,符合只读数据获取的最佳实践。
There was a problem hiding this comment.
Code Review Summary
This PR implements critical bug fixes for the my-usage page statistics and adds readonly API endpoints. The implementation is solid with comprehensive test coverage.
PR Size: XL
- Lines changed: 1437 (1333 additions + 104 deletions)
- Files changed: 16
Note: This is a large PR. Consider these logical splits for future changes:
- Core Bug Fixes (my-usage.ts query fixes + warmup exclusion)
- API Infrastructure (Bearer auth + action-adapter changes)
- Readonly Endpoints (6 new my-usage API endpoints)
- Backward Compatibility (system-config degradation logic)
- Test Suite (all test files)
Issues Found
No significant issues identified. The code demonstrates good practices:
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Adequate
- Code clarity - Good
Highlights
Good Practices Observed:
- Proper SQL Migration: Replaced unsafe
sql\${var}`with typed Drizzle operators (lt()`) - Security-First Design:
allowReadOnlyAccessproperly documented with security warnings - Bearer Auth Implementation: Correct regex pattern with case-insensitive matching
- Warmup Exclusion: Centralized
EXCLUDE_WARMUP_CONDITIONprevents duplication - Graceful Degradation: system-config handles missing DB columns for backward compatibility
- Comprehensive Testing: Unit tests cover adapter logic, auth flows, and edge cases
- Error Handling: All async functions properly wrapped in try/catch with logging
Automated review by Claude AI
| const authToken = getCookie(c, "auth-token"); | ||
| const authToken = | ||
| getCookie(c, "auth-token") ?? getBearerTokenFromAuthHeader(c.req.header("authorization")); | ||
| if (!authToken) { |
There was a problem hiding this comment.
[HIGH] [LOGIC-BUG] bearerAuth 在 adapter 通过,但多数 action 仍依赖 Cookie 的 getSession(),导致 Bearer-only 调用失败
Why this is a problem: createActionRoute 现在允许从 Authorization: Bearer <token> 读取 token 并通过 validateKey() 放行,但多数 server action(包括本 PR 新增暴露的 my-usage)仍在 action 内部调用 getSession(),而 getSession() 只读取 auth-token Cookie。结果是:Bearer-only(不带 Cookie)的脚本/CLI 请求会在 adapter 通过后,在 action 内部变成 Unauthorized,最终返回 400。
Evidence:
src/lib/api/action-adapter-openapi.ts:305-316:getCookie(c, "auth-token") ?? getBearerTokenFromAuthHeader(c.req.header("authorization"))
src/lib/auth.ts:122-127:const keyString = await getAuthCookie();(没有从 Authorization header 取值)
src/actions/my-usage.ts:149-150:const session = await getSession({ allowReadOnlyAccess: true }); if (!session) return { ok: false, error: "Unauthorized" };
- OpenAPI 也声明 Bearer 适合脚本/CLI:
src/app/api/actions/[...route]/route.ts:59-65
Suggested fix: 让 getSession()(或 getAuthCookie())在 Cookie 缺失时回退读取 Authorization: Bearer,并补充 Bearer-only 的 API 测试。
// src/lib/auth.ts
import { cookies, headers } from "next/headers";
function parseBearer(raw: string | null): string | undefined {
const match = /^Bearer\s+(.+)$/i.exec(raw?.trim() ?? "");
const token = match?.[1]?.trim();
return token || undefined;
}
async function getAuthToken(): Promise<string | undefined> {
const cookieStore = await cookies();
const cookieToken = cookieStore.get("auth-token")?.value;
if (cookieToken) return cookieToken;
const headerStore = await headers();
return parseBearer(headerStore.get("authorization"));
}
export async function getSession(options?: { allowReadOnlyAccess?: boolean }) {
const token = await getAuthToken();
if (!token) return null;
return validateKey(token, options);
}// tests/api/my-usage-readonly.test.ts
// 增加用例:仅设置 Authorization(不设置 Cookie)时,my-usage 端点应 200。There was a problem hiding this comment.
Code Review Summary
This PR introduces a new /api/actions/my-usage/* read-only surface and expands the Actions adapter to accept Authorization: Bearer tokens. The current implementation is likely to break Bearer-only clients because most server actions still derive session state from the auth-token cookie.
PR Size: XL
- Lines changed: 1437
- Files changed: 16
- Split suggestions: (1) auth/bearer session plumbing + tests, (2) my-usage stats/warmup aggregation fix, (3) test infra + coverage config.
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 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 1 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Critical Issues (Must Fix)
- Bearer-only
/api/actions/*requests can pass adapter auth but fail inside actions that callgetSession()(cookie-only), contradicting the newbearerAuthscheme:src/lib/api/action-adapter-openapi.ts:306,src/lib/auth.ts:122,src/actions/my-usage.ts:149.
High Priority Issues (Should Fix)
Review Coverage
- Logic and correctness
- Security (OWASP Top 10)
- Error handling
- Type safety
- Documentation accuracy
- Test coverage
- Code clarity
Automated review by Codex AI
There was a problem hiding this comment.
- Identified PR
#532and applied size labelsize/XL(1437 lines changed across 16 files). - Posted an inline review comment for a [HIGH] [LOGIC-BUG]:
bearerAuthis accepted insrc/lib/api/action-adapter-openapi.ts:306, but most actions (incl.my-usage) still callgetSession()which is cookie-only insrc/lib/auth.ts:122, so Bearer-only clients will fail (src/actions/my-usage.ts:149). - Submitted the required PR review summary via
gh pr review --comment.
🧪 测试结果
总体结果: ✅ 所有测试通过 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/lib/auth.ts (2)
139-147: 认证 token 获取的回退机制实现正确,但存在轻微不一致
getAuthToken正确实现了 Cookie 优先、Authorization 头部回退的认证流程,解决了之前评审中提到的"Bearer token 在 adapter 通过但在 action 中失败"的问题。轻微不一致:
parseBearerToken返回string | undefined,而src/lib/api/action-adapter-openapi.ts中的getBearerTokenFromAuthHeader返回string | null。建议统一返回类型以提高代码一致性。🔎 可选:统一返回类型为 undefined
function parseBearerToken(raw: string | null | undefined): string | undefined { const trimmed = raw?.trim(); if (!trimmed) return undefined; const match = /^Bearer\s+(.+)$/i.exec(trimmed); const token = match?.[1]?.trim(); return token || undefined; }同时更新
action-adapter-openapi.ts中的getBearerTokenFromAuthHeader也返回undefined而非null。
130-147: 可选:考虑抽取共享的 Bearer token 解析逻辑
parseBearerToken(此文件) 和getBearerTokenFromAuthHeader(src/lib/api/action-adapter-openapi.ts) 实现了相同的功能。可以考虑将其抽取为共享工具函数,减少代码重复。💡 建议的重构方案
在
src/lib/auth.ts中导出parseBearerToken:-function parseBearerToken(raw: string | null | undefined): string | undefined { +export function parseBearerToken(raw: string | null | undefined): string | undefined { const trimmed = raw?.trim(); if (!trimmed) return undefined; const match = /^Bearer\s+(.+)$/i.exec(trimmed); const token = match?.[1]?.trim(); return token || undefined; }然后在
src/lib/api/action-adapter-openapi.ts中复用:+import { parseBearerToken } from "@/lib/auth"; -function getBearerTokenFromAuthHeader(raw: string | undefined): string | null { - const trimmed = raw?.trim(); - if (!trimmed) return null; - - const match = /^Bearer\s+(.+)$/i.exec(trimmed); - const token = match?.[1]?.trim(); - return token ? token : null; -} +function getBearerTokenFromAuthHeader(raw: string | undefined): string | undefined { + return parseBearerToken(raw); +}
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
📒 Files selected for processing (5)
src/lib/api/action-adapter-openapi.tssrc/lib/auth.tstests/api/auth.unit.test.tstests/api/my-usage-readonly.test.tstests/nextjs.mock.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/api/auth.unit.test.ts
- tests/api/my-usage-readonly.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Use 2-space indentation in all code files
Files:
tests/nextjs.mock.tssrc/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use double quotes for strings instead of single quotes
Use trailing commas in multi-line structures
Enforce maximum line length of 100 characters
Use path alias@/*to reference files from./src/*directory
**/*.{ts,tsx,js,jsx}: Use Biome for linting and formatting with 2-space indent, double quotes, trailing commas, and 100 character max line length
Use path alias@/*to reference files in./src/*directory
Files:
tests/nextjs.mock.tssrc/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript strict mode for type safety
Use readonly or const assertions for immutable data structures
Files:
tests/nextjs.mock.tssrc/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Hash API keys using SHA-256 before storing in database, never store plaintext keys
Mask API keys and sensitive data in application logs
Validate required environment variables at startup with clear error messages
Files:
src/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
src/**/*{guard,auth}*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use constant-time comparison for API key validation to prevent timing attacks
Files:
src/lib/auth.ts
src/**/*{auth,password,credential}*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Never store plaintext passwords; use bcrypt or similar hashing algorithms
Files:
src/lib/auth.ts
src/lib/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use connection pooling for database and Redis connections
Files:
src/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
🧠 Learnings (7)
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to **/*.{tsx,json} : Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW
Applied to files:
tests/nextjs.mock.ts
📚 Learning: 2026-01-03T09:10:02.182Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 517
File: src/app/v1/_lib/proxy/auth-guard.ts:205-232
Timestamp: 2026-01-03T09:10:02.182Z
Learning: In `src/app/v1/_lib/proxy/auth-guard.ts`, the `extractApiKeyFromHeaders` function is intentionally a standalone simplified version for non-Guard flows. It deliberately does not check for multi-source conflicts (unlike `ProxyAuthenticator.validate`), and the code duplication is intentional to avoid coupling between Guard and non-Guard authentication flows.
Applied to files:
src/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*.ts : Implement guard pipeline pattern for cross-cutting concerns in request processing (auth, rate limiting, session)
Applied to files:
src/lib/auth.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*{guard,auth}*.ts : Use constant-time comparison for API key validation to prevent timing attacks
Applied to files:
src/lib/auth.tssrc/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/actions/**/*.ts : Use Server Actions in `next-safe-action` with OpenAPI generation for admin API endpoints
Applied to files:
src/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/actions/**/*.ts : Use Next.js API Routes and Server Actions for admin operations and REST endpoints
Applied to files:
src/lib/api/action-adapter-openapi.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/app/api/actions/**/*.ts : Server Actions in `src/app/api/actions/` must auto-generate OpenAPI 3.1.0 spec from Zod schemas and expose as REST endpoints
Applied to files:
src/lib/api/action-adapter-openapi.ts
🧬 Code graph analysis (1)
src/lib/api/action-adapter-openapi.ts (1)
src/lib/auth.ts (1)
validateKey(17-86)
⏰ 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). (3)
- GitHub Check: 🌐 API Tests
- GitHub Check: Docker Build Test
- GitHub Check: dev-build-deploy
🔇 Additional comments (5)
tests/nextjs.mock.ts (1)
29-31: 测试基础设施补充合理新增的
headersmock 与现有cookiesmock 结构保持一致,默认返回null适合作为测试基础,可在具体测试中按需覆盖以模拟Authorization头部场景。src/lib/auth.ts (1)
130-137: Bearer token 解析逻辑正确
parseBearerToken的正则表达式/^Bearer\s+(.+)$/i使用了单反斜杠\s正确匹配空白字符,提取逻辑健全。src/lib/api/action-adapter-openapi.ts (3)
18-25: Bearer token 提取逻辑正确
getBearerTokenFromAuthHeader的正则表达式/^Bearer\s+(.+)$/i使用了单反斜杠\s正确匹配空白字符(并非之前评审中提到的双反斜杠\\s),实现逻辑正确。注:与
src/lib/auth.ts中的parseBearerToken功能重复,已在该文件的评审中建议可选的代码整合。
69-78: 只读访问选项设计合理,文档清晰
allowReadOnlyAccess选项的文档明确说明了使用场景和安全边界("白名单开关"、"只读且强制绑定当前会话"、"绝不能用于允许传入 userId/keyId 等可导致越权的管理型接口"),有助于防止误用导致的安全问题。
305-316: 双源 token 认证实现正确,已解决之前的评审问题认证流程现在正确支持从 Cookie 或 Authorization 头部获取 token:
- Line 305-306:
getCookie(c, "auth-token") ?? getBearerTokenFromAuthHeader(...)- Line 312:
validateKey(authToken, { allowReadOnlyAccess })配合
src/lib/auth.ts中getSession()的更新(通过getAuthToken()同样支持双源 token),之前评审中提到的"Bearer token 在 adapter 通过但在 action 内部失败"的问题已得到解决。
|
Greptile encountered an error while reviewing this PR. Please reach out to support@greptile.com for assistance. |
Summary
This PR fixes bugs in the my-usage page's "today stats" feature and adds readonly API endpoints for self-service usage queries. Users were seeing all zeros in their "today usage" statistics despite logs showing correctly below.
Problem
getMyTodayStatsquery used raw SQL${timeRange.endTime}which caused Date injection issues, resulting in incorrect time range filteringoriginalModelbut logs filtered bymodel, causing filter mismatchesRelated Issues:
Solution
1. Fix Today Stats Query
Changed from raw SQL comparison to proper Drizzle ORM operator:
```typescript
// Before: Date injection issue
sql`${messageRequest.createdAt} < ${timeRange.endTime}`
// After: Proper typed comparison
lt(messageRequest.createdAt, timeRange.endTime)
```
2. Exclude Warmup Requests from Stats
Added unified
EXCLUDE_WARMUP_CONDITIONto all statistics queries:```typescript
const EXCLUDE_WARMUP_CONDITION = sql`(blocked_by IS NULL OR blocked_by <> 'warmup')`;
```
3. New Readonly API Endpoints
Added 6 my-usage endpoints accessible via Bearer token (for scripts/CLI):
POST /api/actions/my-usage/getMyUsageMetadataPOST /api/actions/my-usage/getMyQuotaPOST /api/actions/my-usage/getMyTodayStatsPOST /api/actions/my-usage/getMyUsageLogsPOST /api/actions/my-usage/getMyAvailableModelsPOST /api/actions/my-usage/getMyAvailableEndpoints4. Bearer Token Authentication
Added
Authorization: Bearer <token>support as alternative to Cookie authentication:bearerAuthsecurity scheme in OpenAPI specaction-adapter-openapi.tsnow checks both Cookie and Authorization headerallowReadOnlyAccessoption to bypasscanLoginWebUicheck for readonly endpoints5. Fix Model Filter Consistency
Changed
getDistinctModelsForKeyto usemessage_request.modelfield (matching log filter behavior) instead oforiginalModel.Changes
Core Changes
src/actions/my-usage.tslt(), add warmup exclusionsrc/app/api/actions/[...route]/route.tssrc/lib/api/action-adapter-openapi.tsallowReadOnlyAccessoptionsrc/repository/usage-logs.tsmessage_request.modelSupporting Changes
src/repository/system-config.tstests/api/action-adapter-openapi.unit.test.tstests/api/auth.unit.test.tstests/api/my-usage-readonly.test.tsvitest.my-usage.config.tsTesting
Automated Tests
bun run lint- Code style checksbun run typecheck- TypeScript validationbun run build- Production buildbun run test- All existing tests passbun run test:coverage:my-usage- New my-usage specific testsManual Testing
/my-usagepagecurl -H "Authorization: Bearer <key>" -X POST .../api/actions/my-usage/getMyQuotaBreaking Changes
None. All changes are backwards compatible:
Description enhanced by Claude AI