feat: optionally intercept Anthropic warmup requests#525
Conversation
📝 WalkthroughWalkthroughThis pull request introduces support for intercepting Anthropic Warmup requests. It adds a new database column, configuration setting, and guard implementation to detect warmup probes, respond without upstream calls, exclude them from statistics and rate limits, and log them for audit purposes across multiple languages. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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! 此拉取请求引入了一项新功能,允许系统选择性地拦截 Anthropic 的 Warmup 请求。通过在本地直接响应这些探测请求,可以有效减少对上游供应商的无效调用和资源浪费。此功能旨在优化系统效率和成本,同时确保所有被拦截的请求都有详细的日志记录,但不会影响计费和统计数据。 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 新增了对 Anthropic Warmup 请求的可选拦截功能,实现非常完整和健壮。
- 新增的
interceptAnthropicWarmupRequests系统设置开关,提供了灵活的控制。 ProxyWarmupGuard的实现逻辑清晰,能够有效拦截 Warmup 请求并提前返回,避免了不必要的上游调用,节省了资源。- 对日志记录的处理非常细致,通过
blocked_by=warmup标志清晰地标识了这类请求,并且正确地将cost_usd设为 NULL,避免了计费混淆。 - 在所有相关的统计查询中(如用户、密钥、供应商、排行榜等),都通过
EXCLUDE_WARMUP_CONDITION将 Warmup 请求排除,确保了统计数据的准确性。 - 在并发会话计数中也排除了 Warmup 请求,避免了对真实请求的限流产生影响。
- UI 层面也做了相应的适配,在日志列表和详情中对 Warmup 请求有清晰的标注和说明,提升了可观测性。
- 数据库迁移和多语言本地化文件的更新也很完整。
- 尤其值得称赞的是,本次 PR 包含了大量新增的单元测试和集成测试,覆盖了核心逻辑、UI 组件和数据库查询等多个方面,极大地保证了代码质量和未来的可维护性。
总的来说,这是一个高质量的 PR,考虑周全,实现优雅。我只发现了一个非常微小的问题,不影响整体功能。
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/repository/statistics.ts (1)
1012-1164: 添加 EXCLUDE_WARMUP_CONDITION 条件过滤以保持与其他统计函数的一致性。
getRateLimitEventStats函数未显式使用EXCLUDE_WARMUP_CONDITION。虽然 warmup 请求在守卫链中位于限流检查之前(会提前返回 200 响应,不产生限流错误),导致自然被errorMessage LIKE '%rate_limit_metadata%'条件过滤掉,但应与其他统计函数(sumKeyTotalCost、sumUserCostInTimeRange等)保持一致,添加显式的 warmup 排除条件。这样可增强代码的防御性和可维护性。建议在 WHERE 条件中添加:
AND (${messageRequest.blockedBy} IS NULL OR ${messageRequest.blockedBy} <> 'warmup')
🧹 Nitpick comments (10)
tests/setup.ts (1)
137-141: 建议:使用 const 断言提升类型安全根据编码规范中"对不可变数据结构使用 readonly 或 const 断言"的要求,
TEST_CONFIG作为配置对象应该是不可变的。建议添加as const断言以获得更好的类型推断和不可变性保证。🔎 建议的改进
-export const TEST_CONFIG = { +export const TEST_CONFIG = { timeout: DEFAULT_TIMEOUT, apiBaseUrl: process.env.API_BASE_URL, skipAuthTests: !process.env.TEST_AUTH_TOKEN, -}; +} as const;这样可以:
- 将属性类型从
string | undefined精确化为字面量类型- 防止意外修改配置对象
- 符合 TypeScript 严格模式的最佳实践
src/repository/provider.ts (1)
528-531: 成功将 Warmup 日志从供应商统计与最近调用中过滤掉在
provider_stats的 LEFT JOIN 以及latest_callCTE 中增加
(blocked_by IS NULL OR blocked_by <> 'warmup')条件,能确保被标记为 warmup 的message_request既不会计入today_cost/today_calls,也不会被当作最近一次调用时间/模型,行为上与「warmup 不计费、不进统计」的目标一致,且不影响其它 blocked_by 场景的统计结果。可以后续考虑把这段 warmup 过滤条件抽成统一的 SQL 片段/常量(在多个统计查询里复用),以避免将来修改 warmup 标记值或语义时出现条件不一致的情况。
Also applies to: 545-548
src/repository/message.ts (1)
605-608: 建议:aggregateMultipleSessionStats中的空 session 判断逻辑可能需要对齐。在
aggregateSessionStats中使用totalCount === 0判断 session 不存在,但在批量版本中使用requestCount === 0。如果一个 session 只有 warmup 请求,requestCount会是 0 但 session 实际存在。考虑是否应该在批量版本中也添加
totalCount字段并使用它来判断 session 存在性,以保持行为一致。🔎 可选的修改建议
在
aggregateMultipleSessionStats的 select 中添加totalCount:const statsResults = await db .select({ sessionId: messageRequest.sessionId, + totalCount: sql<number>`count(*)::double precision`, requestCount: sql<number>`count(*) FILTER (WHERE ${excludeWarmup})::double precision`, // ... other fields })然后在过滤逻辑中使用:
- if (!stats || !userInfo || stats.requestCount === 0) { + if (!stats || !userInfo || stats.totalCount === 0) { continue; }tests/unit/dashboard-logs-warmup-ui.test.tsx (1)
15-18: 建议:测试描述应使用英文以保持代码库一致性测试逻辑正确,但测试套件和用例的描述使用了中文(lines 15, 53, 54)。为保持代码库一致性,建议改为英文描述,例如:
-// 测试环境不加载 next-intl/navigation -> next/navigation 的真实实现(避免 Next.js 运行时依赖) +// Mock next-intl/navigation to avoid Next.js runtime dependencies in test environment -describe("UsageLogsTable - warmup 跳过展示", () => { - test("blockedBy=warmup 时应在 Provider/Cost 列显示 Skipped 标记", () => { +describe("UsageLogsTable - warmup skip display", () => { + test("should display Skipped badge when blockedBy=warmup", () => {测试本身的实现(mock 设置、组件渲染、断言)都是正确的。
Also applies to: 53-54
tests/unit/proxy/session-guard-warmup-intercept.test.ts (1)
107-108: 建议:测试描述应使用英文测试逻辑和 mock 设置都正确,但测试套件和用例描述使用了中文。建议改为英文以保持代码库一致性:
-describe("ProxySessionGuard:warmup 拦截不应计入并发会话", () => { - test("当 warmup 且开关开启时,不应调用 SessionTracker.trackSession", async () => { +describe("ProxySessionGuard: warmup intercept should not count toward concurrent sessions", () => { + test("should not call SessionTracker.trackSession when warmup and interception is enabled", async () => { // ... }); - test("当 warmup 但开关关闭时,应正常调用 SessionTracker.trackSession", async () => { + test("should call SessionTracker.trackSession when warmup but interception is disabled", async () => {Also applies to: 118-118
tests/unit/error-details-dialog-warmup-ui.test.tsx (1)
26-50: 建议:考虑添加自动清理机制当前
renderWithIntl依赖测试手动调用unmount()。如果测试抛出异常,容器可能不会被清理。🔎 可选的改进方案
import { afterEach } from "vitest"; const containers: HTMLDivElement[] = []; afterEach(() => { containers.forEach(c => c.remove()); containers.length = 0; }); function renderWithIntl(node: ReactNode) { const container = document.createElement("div"); containers.push(container); // ... rest of implementation }tests/unit/proxy/guard-pipeline-warmup.test.ts (1)
41-47: 建议:Mock 返回值保持一致性部分 mock 显式返回
null(如 auth、client),而另一些隐式返回undefined(如 session、requestFilter)。虽然功能上等效(pipeline 检查 falsy),但保持一致性更好。🔎 建议统一返回 null
vi.mock("@/app/v1/_lib/proxy/session-guard", () => ({ ProxySessionGuard: { ensure: async () => { callOrder.push("session"); + return null; }, }, }));src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)
113-118: Warmup 显示实现良好!两处 warmup 显示使用一致的蓝色配色方案,与橙色的"已拦截"状态形成区分。Cost 列的 Tooltip 提供了有用的解释。
可选优化:考虑提取共享的 badge 样式常量
两处蓝色 badge 样式重复,可以提取为共享常量以便维护。
🔎 可选:提取共享样式
// 在文件顶部或共享模块中 const WARMUP_BADGE_CLASSES = "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-800"; // 使用时 <Badge variant="outline" className={`text-[10px] leading-tight px-1 ${WARMUP_BADGE_CLASSES}`}>Also applies to: 313-331
src/app/v1/_lib/proxy/warmup-guard.ts (2)
35-38: 认证状态校验逻辑当
authState不完整时直接返回null继续后续流程是合理的——此时请求会被后续 guard 拦截。但建议添加注释说明此处的设计意图:未认证请求不应被 warmup 拦截,而是应该走正常的认证失败流程。
50-67: Session 数据存储使用 Promise.allSettled使用
Promise.allSettled并行存储 session 数据是合理的——即使部分存储失败也不影响主流程。但当前没有处理失败的情况,建议至少记录警告日志以便排查问题。🔎 建议添加失败日志
if (session.sessionId) { const seq = session.getRequestSequence(); - await Promise.allSettled([ + const results = await Promise.allSettled([ SessionManager.storeSessionResponse(session.sessionId, responseText, seq), SessionManager.storeSessionResponseHeaders(session.sessionId, responseHeaders, seq), SessionManager.storeSessionUpstreamRequestMeta( session.sessionId, { url: WARMUP_UPSTREAM_META_URL, method: session.method }, seq ), SessionManager.storeSessionUpstreamResponseMeta( session.sessionId, { url: WARMUP_UPSTREAM_META_URL, statusCode: 200 }, seq ), ]); + + const failures = results.filter((r) => r.status === "rejected"); + if (failures.length > 0) { + logger.warn("[WarmupGuard] Some session data failed to store", { + sessionId: session.sessionId, + failureCount: failures.length, + }); + } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
📒 Files selected for processing (49)
drizzle/0044_uneven_donald_blake.sqldrizzle/meta/0044_snapshot.jsondrizzle/meta/_journal.jsonmessages/en/dashboard.jsonmessages/en/settings.jsonmessages/ja/dashboard.jsonmessages/ja/settings.jsonmessages/ru/dashboard.jsonmessages/ru/settings.jsonmessages/zh-CN/dashboard.jsonmessages/zh-CN/settings.jsonmessages/zh-TW/dashboard.jsonmessages/zh-TW/settings.jsonsrc/actions/system-config.tssrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/app/[locale]/settings/config/page.tsxsrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.tssrc/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/components/ui/__tests__/badge.test.tsxsrc/drizzle/schema.tssrc/lib/config/system-settings-cache.tssrc/lib/validation/schemas.tssrc/repository/_shared/transformers.test.tssrc/repository/_shared/transformers.tssrc/repository/key.tssrc/repository/leaderboard.tssrc/repository/message.tssrc/repository/overview.tssrc/repository/provider.tssrc/repository/statistics.tssrc/repository/system-config.tssrc/repository/usage-logs.tssrc/types/system-config.tstests/integration/billing-model-source.test.tstests/setup.tstests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/lib/cache/session-cache.test.tstests/unit/lib/config/system-settings-cache.test.tstests/unit/lib/provider-url-validation.test.tstests/unit/proxy/guard-pipeline-warmup.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/session.test.tstests/unit/proxy/warmup-guard.test.tstests/unit/repository/warmup-stats-exclusion.test.ts
🧰 Additional context used
📓 Path-based instructions (27)
**/*.{ts,tsx,js,jsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Use 2-space indentation in all code files
Files:
src/repository/_shared/transformers.test.tssrc/app/v1/_lib/proxy/session.tsmessages/ja/settings.jsonsrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxtests/unit/lib/config/system-settings-cache.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/repository/system-config.tsmessages/ru/settings.jsonsrc/app/v1/_lib/proxy/warmup-guard.tsmessages/zh-TW/dashboard.jsontests/integration/billing-model-source.test.tsmessages/en/settings.jsonmessages/ru/dashboard.jsonmessages/zh-CN/dashboard.jsonsrc/app/v1/_lib/proxy/guard-pipeline.tstests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/proxy/session.test.tstests/setup.tsmessages/zh-CN/settings.jsondrizzle/meta/0044_snapshot.jsondrizzle/meta/_journal.jsonsrc/components/ui/__tests__/badge.test.tsxsrc/repository/usage-logs.tsmessages/zh-TW/settings.jsontests/unit/lib/cache/session-cache.test.tssrc/types/system-config.tsmessages/ja/dashboard.jsonmessages/en/dashboard.jsonsrc/repository/_shared/transformers.tssrc/app/v1/_lib/proxy/session-guard.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tstests/unit/lib/provider-url-validation.test.tssrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/repository/message.tssrc/lib/validation/schemas.tssrc/repository/leaderboard.tssrc/app/[locale]/settings/config/page.tsxsrc/repository/overview.tstests/unit/proxy/warmup-guard.test.tssrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/lib/config/system-settings-cache.tssrc/drizzle/schema.tstests/unit/repository/warmup-stats-exclusion.test.tssrc/actions/system-config.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/_shared/transformers.test.tssrc/app/v1/_lib/proxy/session.tssrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxtests/unit/lib/config/system-settings-cache.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/repository/system-config.tssrc/app/v1/_lib/proxy/warmup-guard.tstests/integration/billing-model-source.test.tssrc/app/v1/_lib/proxy/guard-pipeline.tstests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/proxy/session.test.tstests/setup.tssrc/components/ui/__tests__/badge.test.tsxsrc/repository/usage-logs.tstests/unit/lib/cache/session-cache.test.tssrc/types/system-config.tssrc/repository/_shared/transformers.tssrc/app/v1/_lib/proxy/session-guard.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tstests/unit/lib/provider-url-validation.test.tssrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/repository/message.tssrc/lib/validation/schemas.tssrc/repository/leaderboard.tssrc/app/[locale]/settings/config/page.tsxsrc/repository/overview.tstests/unit/proxy/warmup-guard.test.tssrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/lib/config/system-settings-cache.tssrc/drizzle/schema.tstests/unit/repository/warmup-stats-exclusion.test.tssrc/actions/system-config.ts
src/repository/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Repository pattern in
src/repository/to wrap Drizzle queries
Files:
src/repository/_shared/transformers.test.tssrc/repository/system-config.tssrc/repository/usage-logs.tssrc/repository/_shared/transformers.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tssrc/repository/message.tssrc/repository/leaderboard.tssrc/repository/overview.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/_shared/transformers.test.tssrc/app/v1/_lib/proxy/session.tssrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxtests/unit/lib/config/system-settings-cache.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/repository/system-config.tssrc/app/v1/_lib/proxy/warmup-guard.tstests/integration/billing-model-source.test.tssrc/app/v1/_lib/proxy/guard-pipeline.tstests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/proxy/session.test.tstests/setup.tssrc/components/ui/__tests__/badge.test.tsxsrc/repository/usage-logs.tstests/unit/lib/cache/session-cache.test.tssrc/types/system-config.tssrc/repository/_shared/transformers.tssrc/app/v1/_lib/proxy/session-guard.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tstests/unit/lib/provider-url-validation.test.tssrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/repository/message.tssrc/lib/validation/schemas.tssrc/repository/leaderboard.tssrc/app/[locale]/settings/config/page.tsxsrc/repository/overview.tstests/unit/proxy/warmup-guard.test.tssrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/lib/config/system-settings-cache.tssrc/drizzle/schema.tstests/unit/repository/warmup-stats-exclusion.test.tssrc/actions/system-config.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/_shared/transformers.test.tssrc/app/v1/_lib/proxy/session.tssrc/repository/system-config.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/repository/usage-logs.tssrc/types/system-config.tssrc/repository/_shared/transformers.tssrc/app/v1/_lib/proxy/session-guard.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tssrc/repository/message.tssrc/lib/validation/schemas.tssrc/repository/leaderboard.tssrc/repository/overview.tssrc/lib/config/system-settings-cache.tssrc/drizzle/schema.tssrc/actions/system-config.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:
src/repository/_shared/transformers.test.tstests/unit/lib/config/system-settings-cache.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tstests/integration/billing-model-source.test.tstests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/proxy/session.test.tssrc/components/ui/__tests__/badge.test.tsxtests/unit/lib/cache/session-cache.test.tstests/unit/lib/provider-url-validation.test.tstests/unit/proxy/warmup-guard.test.tstests/unit/repository/warmup-stats-exclusion.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
Ensure test database names contain 'test' keyword for safety validation
Files:
src/repository/_shared/transformers.test.tstests/unit/lib/config/system-settings-cache.test.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tstests/integration/billing-model-source.test.tstests/unit/proxy/session.test.tstests/unit/lib/cache/session-cache.test.tstests/unit/lib/provider-url-validation.test.tstests/unit/proxy/warmup-guard.test.tstests/unit/repository/warmup-stats-exclusion.test.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/_shared/transformers.test.tssrc/repository/system-config.tssrc/repository/usage-logs.tssrc/repository/_shared/transformers.tssrc/repository/statistics.tssrc/repository/key.tssrc/repository/provider.tssrc/repository/message.tssrc/repository/leaderboard.tssrc/repository/overview.tssrc/actions/system-config.ts
src/app/v1/_lib/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Guard pipeline must execute in order: ProxyAuthenticator, SensitiveWordGuard, VersionGuard, ProxySessionGuard, ProxyRateLimitGuard, ProxyProviderResolver
Files:
src/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
src/app/v1/_lib/proxy/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/app/v1/_lib/proxy/**/*.ts: Implement guard pipeline pattern for cross-cutting concerns in request processing (auth, rate limiting, session)
Use undici library for HTTP requests instead of node-fetch for better performance
Files:
src/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
src/app/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Implement Content-Security-Policy headers for XSS prevention
Files:
src/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
src/app/v1/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use Hono router for ultrafast, lightweight routing in proxy endpoints
Files:
src/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
messages/**/*.json
📄 CodeRabbit inference engine (CLAUDE.md)
Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in
messages/{locale}/*.jsonStore message translations in
messages/{locale}/*.jsonfiles
Files:
messages/ja/settings.jsonmessages/ru/settings.jsonmessages/zh-TW/dashboard.jsonmessages/en/settings.jsonmessages/ru/dashboard.jsonmessages/zh-CN/dashboard.jsonmessages/zh-CN/settings.jsonmessages/zh-TW/settings.jsonmessages/ja/dashboard.jsonmessages/en/dashboard.json
**/*.{tsx,json}
📄 CodeRabbit inference engine (AGENTS.md)
Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW
Files:
messages/ja/settings.jsonsrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxmessages/ru/settings.jsonmessages/zh-TW/dashboard.jsonmessages/en/settings.jsonmessages/ru/dashboard.jsonmessages/zh-CN/dashboard.jsontests/unit/error-details-dialog-warmup-ui.test.tsxtests/unit/dashboard-logs-warmup-ui.test.tsxmessages/zh-CN/settings.jsondrizzle/meta/0044_snapshot.jsondrizzle/meta/_journal.jsonsrc/components/ui/__tests__/badge.test.tsxmessages/zh-TW/settings.jsonmessages/ja/dashboard.jsonmessages/en/dashboard.jsonsrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/app/[locale]/settings/config/page.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
src/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.{tsx,jsx}: Uselucide-reactfor icons, no custom SVGs
Use React's automatic escaping to prevent XSS vulnerabilities
Files:
src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxsrc/components/ui/__tests__/badge.test.tsxsrc/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/app/[locale]/settings/config/page.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
src/**/*{guard,auth}*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use constant-time comparison for API key validation to prevent timing attacks
Files:
src/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
src/app/v1/_lib/proxy/**/*guard*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Implement fail-open strategy: allow requests when Redis is unavailable
Files:
src/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.ts
tests/integration/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Integration tests requiring database must be placed in
tests/integration/and use test database with 'test' in the name
Files:
tests/integration/billing-model-source.test.ts
tests/integration/**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
Place integration tests requiring database in
tests/integration/(excluded by default)
Files:
tests/integration/billing-model-source.test.ts
src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
lucide-reactfor icons instead of custom SVGs
Files:
src/components/ui/__tests__/badge.test.tsx
src/components/ui/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Place UI components in
src/components/ui/directory (excluded from typecheck)
Files:
src/components/ui/__tests__/badge.test.tsx
src/components/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
src/components/**/*.{tsx,jsx}: Use Tailwind CSS for styling, place utility classes close to JSX
Use shadcn/ui component library for high-quality, accessible UI components
Files:
src/components/ui/__tests__/badge.test.tsx
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.tssrc/repository/message.ts
src/**/*provider*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Set provider circuit breaker failure threshold, open duration, and half-open success threshold in configuration
Files:
src/repository/provider.ts
src/lib/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use connection pooling for database and Redis connections
Files:
src/lib/validation/schemas.tssrc/lib/config/system-settings-cache.ts
src/drizzle/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Drizzle ORM with PostgreSQL for database operations
src/drizzle/**/*.ts: Use Drizzle ORM with parameterized queries to prevent SQL injection
Use soft delete pattern withdeletedAtcolumn instead of hard deletes
Use JSON columns in PostgreSQL for flexible data structures (modelRedirects, providerChain, etc.)
Implement proper indexing strategy for common queries and foreign keys
Files:
src/drizzle/schema.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/system-config.ts
🧠 Learnings (23)
📚 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/app/v1/_lib/proxy/session.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tstests/unit/proxy/session.test.tssrc/app/v1/_lib/proxy/session-guard.tstests/unit/proxy/warmup-guard.test.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/proxy-handler.ts : Structure request flow in proxy handler through: ProxySession.fromContext() -> detectFormat() -> GuardPipelineBuilder.run() -> ProxyForwarder.send() -> ProxyResponseHandler.dispatch()
Applied to files:
src/app/v1/_lib/proxy/session.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tstests/unit/proxy/session.test.tssrc/app/v1/_lib/proxy/session-guard.tstests/unit/proxy/warmup-guard.test.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:
src/app/v1/_lib/proxy/session.tstests/unit/proxy/session-guard-warmup-intercept.test.tstests/unit/proxy/guard-pipeline-warmup.test.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.tstests/unit/proxy/warmup-guard.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/*handler*.ts : Stream responses with proper backpressure handling and chunked transfer encoding
Applied to files:
src/app/v1/_lib/proxy/session.tssrc/app/v1/_lib/proxy/warmup-guard.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 messages/**/*.json : Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in `messages/{locale}/*.json`
Applied to files:
messages/ja/settings.jsonmessages/ru/settings.jsonmessages/zh-CN/settings.json
📚 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/**/*guard*.ts : Implement fail-open strategy: allow requests when Redis is unavailable
Applied to files:
tests/unit/proxy/guard-pipeline-warmup.test.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.tstests/unit/proxy/warmup-guard.test.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:
tests/unit/proxy/guard-pipeline-warmup.test.tssrc/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.tstests/unit/proxy/warmup-guard.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:
src/app/v1/_lib/proxy/warmup-guard.ts
📚 Learning: 2026-01-03T09:09:37.748Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 517
File: src/app/v1/_lib/models/available-models.ts:45-70
Timestamp: 2026-01-03T09:09:37.748Z
Learning: The `authenticateRequest` function in `src/app/v1/_lib/models/available-models.ts` is intentionally a lightweight version of authentication, specifically designed for the `/v1/models` endpoint. It does not need to check for multi-source conflicts or use the full `ProxyAuthenticator` logic, as the scenario is simpler. The JSON error response format is intentional (more generic than `ProxyResponses.buildError`).
Applied to files:
src/app/v1/_lib/proxy/warmup-guard.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/app/v1/_lib/proxy/warmup-guard.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 **/*.{tsx,json} : Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW
Applied to files:
tests/unit/error-details-dialog-warmup-ui.test.tsx
📚 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:
tests/unit/dashboard-logs-warmup-ui.test.tsxtests/unit/repository/warmup-stats-exclusion.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,tsx} : Use Vitest for unit testing with Node environment, coverage thresholds: 50% lines/functions, 40% branches
Applied to files:
tests/unit/dashboard-logs-warmup-ui.test.tsxtests/setup.tssrc/components/ui/__tests__/badge.test.tsxtests/unit/lib/provider-url-validation.test.tstests/unit/repository/warmup-stats-exclusion.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 tests/integration/**/*.test.ts : Place integration tests requiring database in `tests/integration/` (excluded by default)
Applied to files:
tests/setup.tstests/unit/repository/warmup-stats-exclusion.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/drizzle/**/*.ts : Use JSON columns in PostgreSQL for flexible data structures (modelRedirects, providerChain, etc.)
Applied to files:
drizzle/meta/0044_snapshot.json
📚 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/repository/usage-logs.tssrc/repository/provider.tssrc/repository/message.tstests/unit/repository/warmup-stats-exclusion.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/**/*{message,response,log}*.ts : Log request duration, token usage, and cost to message_request table for analytics
Applied to files:
src/repository/usage-logs.tssrc/repository/statistics.tssrc/repository/provider.tssrc/repository/message.tssrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxtests/unit/repository/warmup-stats-exclusion.test.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/lib/session-manager.ts : Session Manager must use 5-minute Redis context cache with sliding window and record decision chains for audit trail
Applied to files:
tests/unit/lib/cache/session-cache.test.tssrc/app/v1/_lib/proxy/session-guard.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/lib/session/**/*.ts : Store session data in Redis with 5-minute TTL for session stickiness
Applied to files:
tests/unit/lib/cache/session-cache.test.tssrc/app/v1/_lib/proxy/session-guard.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:
tests/unit/repository/warmup-stats-exclusion.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:
tests/unit/repository/warmup-stats-exclusion.test.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:
tests/unit/repository/warmup-stats-exclusion.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/drizzle/**/*.ts : Use Drizzle ORM with parameterized queries to prevent SQL injection
Applied to files:
tests/unit/repository/warmup-stats-exclusion.test.ts
🧬 Code graph analysis (20)
src/repository/_shared/transformers.test.ts (1)
src/repository/_shared/transformers.ts (1)
toSystemSettings(140-158)
src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx (2)
scripts/sync-settings-keys.js (2)
t(72-72)p(102-102)src/components/ui/badge.tsx (1)
Badge(39-39)
tests/unit/proxy/session-guard-warmup-intercept.test.ts (2)
src/app/v1/_lib/proxy/session.ts (1)
ProxySession(48-733)src/app/v1/_lib/proxy/session-guard.ts (1)
ProxySessionGuard(38-150)
tests/unit/proxy/guard-pipeline-warmup.test.ts (1)
src/app/v1/_lib/proxy/guard-pipeline.ts (2)
CHAT_PIPELINE(172-189)GuardPipelineBuilder(144-169)
src/repository/system-config.ts (1)
src/drizzle/schema.ts (1)
systemSettings(439-473)
src/app/v1/_lib/proxy/warmup-guard.ts (5)
src/app/v1/_lib/proxy/session.ts (1)
ProxySession(48-733)src/lib/config/system-settings-cache.ts (1)
getCachedSystemSettings(40-93)src/drizzle/db.ts (1)
db(37-44)src/drizzle/schema.ts (1)
messageRequest(264-345)src/lib/logger.ts (1)
logger(168-187)
src/app/v1/_lib/proxy/guard-pipeline.ts (1)
src/app/v1/_lib/proxy/warmup-guard.ts (1)
ProxyWarmupGuard(23-108)
tests/unit/dashboard-logs-warmup-ui.test.tsx (2)
src/repository/usage-logs.ts (1)
UsageLogRow(30-61)src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)
UsageLogsTable(44-546)
src/components/ui/__tests__/badge.test.tsx (1)
src/components/ui/badge.tsx (1)
Badge(39-39)
src/repository/usage-logs.ts (1)
src/drizzle/schema.ts (1)
messageRequest(264-345)
tests/unit/lib/cache/session-cache.test.ts (1)
src/lib/cache/session-cache.ts (9)
getActiveSessionsCache(145-147)setActiveSessionsCache(152-157)setSessionDetailsCache(169-174)clearActiveSessionsCache(179-181)clearAllSessionsCache(186-188)clearSessionDetailsCache(193-195)clearAllSessionCache(200-203)stopCacheCleanup(222-229)startCacheCleanup(208-217)
src/app/v1/_lib/proxy/session-guard.ts (2)
src/lib/config/system-settings-cache.ts (1)
getCachedSystemSettings(40-93)src/lib/session-tracker.ts (1)
SessionTracker(18-580)
src/repository/key.ts (1)
src/drizzle/schema.ts (1)
messageRequest(264-345)
tests/unit/lib/provider-url-validation.test.ts (1)
src/lib/validation/provider-url.ts (1)
validateProviderUrlForConnectivity(14-48)
src/app/[locale]/settings/config/_components/system-settings-form.tsx (2)
src/components/ui/label.tsx (1)
Label(21-21)src/components/ui/switch.tsx (1)
Switch(28-28)
src/repository/message.ts (1)
src/drizzle/schema.ts (1)
messageRequest(264-345)
src/repository/leaderboard.ts (1)
src/drizzle/schema.ts (1)
messageRequest(264-345)
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (4)
src/components/ui/table.tsx (1)
TableRow(92-92)scripts/sync-settings-keys.js (1)
t(72-72)src/components/ui/tooltip.tsx (4)
TooltipProvider(57-57)Tooltip(57-57)TooltipTrigger(57-57)TooltipContent(57-57)src/components/ui/badge.tsx (1)
Badge(39-39)
src/lib/config/system-settings-cache.ts (1)
src/types/system-config.ts (1)
SystemSettings(6-37)
tests/unit/repository/warmup-stats-exclusion.test.ts (2)
src/repository/usage-logs.ts (2)
findUsageLogsWithDetails(301-475)findUsageLogsStats(524-624)src/repository/provider.ts (1)
getProviderStatistics(483-586)
⏰ 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). (7)
- GitHub Check: Greptile Review
- GitHub Check: 🌐 API Tests
- GitHub Check: pr-description
- GitHub Check: check-codex-status
- GitHub Check: pr-review
- GitHub Check: pr-label
- GitHub Check: Docker Build Test
🔇 Additional comments (83)
tests/setup.ts (1)
127-130: 项目确实使用了 React,该标记是必要且恰当的项目依赖中包含 React 19 及多个 React 相关库,源代码中存在
.tsx文件并广泛使用 React(React.FC、React.ComponentType、React.hooks 等)。在测试环境全局设置IS_REACT_ACT_ENVIRONMENT标记是 React 18+ 的标准做法,可防止 "not configured to support act(...)" 的警告。该配置是必要的且遵循最佳实践,无需修改。drizzle/0044_uneven_donald_blake.sql (1)
1-1: LGTM!数据库迁移语句符合规范。该迁移正确添加了布尔类型的
intercept_anthropic_warmup_requests字段,默认值false确保了向后兼容性,NOT NULL约束也符合预期。tests/unit/lib/provider-url-validation.test.ts (2)
1-1: LGTM!正确引入 Vitest 的 mock 工具。为新增的 mock 测试用例添加
vi导入,符合 Vitest 的最佳实践。
55-82: LGTM!优秀的边界测试覆盖。该测试用例有效覆盖了 URL 构造器抛出非 Error 类型异常的边界场景:
- 使用
vi.stubGlobal正确模拟全局 URL 构造器- 在
finally块中妥善恢复原始 URL,确保测试隔离性- 验证了兜底错误消息 "URL 解析失败" 的正确返回
这有助于提高代码的健壮性测试覆盖率。
tests/integration/billing-model-source.test.ts (1)
102-102: LGTM!测试数据结构更新正确。新增的
interceptAnthropicWarmupRequests: false字段与数据库 schema 变更保持一致,默认值false适用于现有测试场景。src/components/ui/__tests__/badge.test.tsx (3)
1-27: LGTM!测试环境配置和辅助函数实现规范。
- 使用
happy-dom环境适合轻量级 DOM 测试render辅助函数正确使用了 React 18+ 的createRootAPI- 使用
act()包裹渲染和卸载操作,确保 React 状态更新的正确处理- 清理逻辑完整,包含
unmount()和容器移除
29-38: LGTM!默认渲染测试覆盖完整。测试用例正确验证了 Badge 组件的默认行为:
- 使用
data-slot="badge"属性进行查询,符合组件设计规范- 验证了渲染为
<span>标签- 清理逻辑调用了
unmount()
40-54: LGTM!asChild 属性测试覆盖了 Slot 组件分支。测试用例有效验证了
asChild属性的透传行为:
- 确认 Badge 属性被正确应用到子元素
<a>标签上- 验证了
data-slot="badge"属性的存在- 检查了
href属性值的正确性- 测试名称明确标注了覆盖 Slot 分支的意图
src/repository/key.ts (9)
11-12: LGTM!Warmup 请求排除条件定义正确。
EXCLUDE_WARMUP_CONDITION使用合理的 SQL 逻辑排除 warmup 请求:
blockedBy IS NULL:包含正常请求blockedBy <> 'warmup':包含其他类型的拦截请求(如敏感词拦截)- 确保 warmup 预热请求不计入密钥使用统计
该条件将在多个查询中复用,提高了代码的可维护性。
301-301: LGTM!正确排除 warmup 请求统计。在
findKeyUsageToday查询中添加EXCLUDE_WARMUP_CONDITION,确保今日用量统计不包含 warmup 请求。
346-346: LGTM!批量查询保持一致的排除逻辑。
findKeyUsageTodayBatch的批量查询版本正确应用了相同的 warmup 排除条件,与单用户版本保持一致。
571-571: LGTM!今日调用次数统计排除 warmup。在
findKeysWithStatistics的今日调用次数查询中应用排除条件,确保 warmup 请求不计入展示给用户的调用次数。
585-591: LGTM!最后使用时间查询排除 warmup。在查询密钥最后使用时间时应用排除条件,确保 warmup 请求不会更新"最后使用时间"字段,避免产生误导性的活跃状态。
607-607: LGTM!分模型统计排除 warmup。在按模型聚合统计中应用排除条件,确保 warmup 请求不计入各模型的调用次数和费用汇总。
693-693: LGTM!批量今日调用次数统计排除 warmup。批量查询版本的今日调用次数统计正确应用了排除条件,与非批量版本保持一致。
716-722: LGTM!批量最后使用时间查询排除 warmup。批量查询版本的最后使用时间查询使用
DISTINCT ON优化,同时正确应用了 warmup 排除条件。
748-748: LGTM!批量分模型统计排除 warmup。批量查询版本的分模型统计正确应用了排除条件,确保多用户场景下的统计准确性。
src/app/v1/_lib/proxy/session-guard.ts (1)
1-1: Warmup 拦截与会话追踪的集成逻辑合理这里通过
session.isWarmupRequest()+ 认证成功态 +getCachedSystemSettings().interceptAnthropicWarmupRequests组合判断 warmup 请求,并仅在非 warmup 场景下调用SessionTracker.trackSession,既满足“拦截后不计入并发会话”的需求,又利用了缓存设置的 fail‑open 行为,不会因为系统设置读取失败阻塞请求;整体实现清晰且与 Guard 流程解耦良好。Also applies to: 50-56, 111-116
src/lib/validation/schemas.ts (1)
701-702: 系统设置 Schema 中新增字段定义合理
interceptAnthropicWarmupRequests: z.boolean().optional()与其他布尔配置风格一致,适合作为可选部分更新字段,且与后端 SystemSettings 类型保持对齐,没有发现类型或验证层面的遗漏。messages/zh-TW/dashboard.json (1)
130-130: Warmup「已跳過」狀態文案與行為描述一致新增的
logs.table.skipped及logs.details.skipped文案清楚说明請求被識別為 Warmup 並由 CCH 直接回應,且不計費、不限流、不進統計,與後端對 warmup 請求的實際處理策略一致,適合作為儀表板上新的狀態呈現。Also applies to: 156-161
src/app/v1/_lib/proxy/session.ts (1)
570-630: Anthropic Warmup 判定逻辑严格且健壮
isWarmupRequest仅在满足「/v1/messages + 单条 user 消息 + 单个 text block + 文本为 'Warmup'(忽略大小写/空白)+ block 级cache_control.type === 'ephemeral'」时才返回 true,同时避开/v1/messages/count_tokens等端点,并对 message 结构逐层做类型与长度检查,既降低了误判风险,又与 Anthropic 的 Warmup 结构相匹配,实现上比较稳健。messages/ru/settings.json (1)
103-104: Warmup 拦截相关俄文配置文案与功能一致
interceptAnthropicWarmupRequests及其说明清楚表达了开启后由 CCH 直接响应 Anthropic Warmup 请求、仅记录日志但不计费且不参与限流/统计的语义,与英文/其它语言版本保持一致,有利于设置页统一理解。tests/unit/lib/config/system-settings-cache.test.ts (1)
1-155: 系统设置缓存测试覆盖完整,特别是 Warmup 拦截相关分支这组单测通过 mock
getSystemSettings与 logger,验证了:首访缓存、TTL 过期重取、带缓存时的 fail‑open、无缓存时返回最小默认设置(并显式断言interceptAnthropicWarmupRequests: false)、缓存失效以及isHttp2Enabled读取缓存等场景,基本把新增加的 warmup 配置在缓存层的关键行为都跑了一遍,细节和时序控制都比较到位。messages/ru/dashboard.json (1)
130-130: 翻译内容已添加,结构正确。俄语翻译符合功能需求,
skipped和warmup相关的本地化字符串已正确添加,与其他语言文件保持一致。Also applies to: 156-161
messages/ja/dashboard.json (1)
130-130: 日语翻译已正确添加。翻译内容结构与其他语言文件一致,日语表述自然准确。
Also applies to: 156-161
tests/unit/lib/cache/session-cache.test.ts (3)
47-80: 测试用例覆盖了基本的读写和 TTL 行为,结构良好。测试数据包含了
SessionStats所需的所有字段,断言逻辑清晰。使用vi.useFakeTimers()和vi.advanceTimersByTime()正确模拟了时间流逝。
221-282: 清理间隔的生命周期测试覆盖全面。测试验证了:
- 未启动时调用
stop无副作用- 幂等性(重复调用
start不创建新间隔)- 过期条目被正确清理
stop后间隔 ID 被清除这些测试确保了清理机制的正确性。
22-28: 不存在清理间隔 ID 访问方式的不匹配问题。测试中的
CLEANUP_INTERVAL_GLOBAL_KEY常量值为"__CCH_CACHE_CLEANUP_INTERVAL_ID__",而生产代码中cacheCleanupState就是globalThis的类型转换,其中使用cacheCleanupState.__CCH_CACHE_CLEANUP_INTERVAL_ID__访问该属性。两者都在访问globalThis上相同键名的属性,只是使用了不同的访问语法(点号 vs 方括号),这在 JavaScript 中是等价的,因此测试辅助函数与生产代码完全兼容。Likely an incorrect or invalid review comment.
tests/unit/repository/warmup-stats-exclusion.test.ts (4)
3-34:sqlToString辅助函数设计合理,具有循环检测机制。使用
visitedSet 防止无限递归,能够处理 Drizzle ORM 生成的多种 SQL 结构(包括value、queryChunks等属性)。
36-53:createThenableQuery使用Promise.resolve作为基础是一个巧妙的设计。这种方式避免了手动为普通对象添加
then方法可能引发的 Biome 警告,同时保持了查询对象的可链式调用和可 await 特性。
55-108: 测试正确验证了分页 total 与统计 totalRequests 的区别。
total(用于分页) 包含 warmup 行 → 期望值 2summary.totalRequests(统计口径) 排除 warmup → 期望值 1这确保了审计日志完整性与统计准确性的分离。
151-197: Provider 统计测试验证了 SQL 中包含 warmup 排除逻辑。测试检查生成的 SQL 同时包含
warmup和blocked_by,确保供应商统计和最近调用记录不会被 warmup 请求污染。src/repository/message.ts (4)
296-296: Warmup 排除条件定义清晰。使用
blockedBy IS NULL OR blockedBy <> 'warmup'的逻辑正确处理了blockedBy为 NULL 的正常请求和非 warmup 的阻断请求。
301-312:totalCount与requestCount的分离设计合理。
totalCount: 用于判断 session 是否存在(包含所有请求)requestCount+ 其他聚合字段: 使用FILTER (WHERE excludeWarmup)排除 warmup这确保了 warmup 请求虽然被记录但不影响统计数据,符合 PR 目标。
317-319: 使用totalCount判断 session 存在性是正确的。即使 session 中只有 warmup 请求,session 本身仍然存在,应返回相应的统计数据(统计值为 0)。这避免了误判 session 不存在的情况。
455-469: 批量聚合函数aggregateMultipleSessionStats同样应用了 warmup 排除逻辑。与单 session 聚合函数保持一致,确保了批量查询的统计结果也排除 warmup 请求。代码保持了高效的批量查询模式,未引入 N+1 问题。
src/app/[locale]/settings/config/page.tsx (1)
43-43: 新设置项已正确传递至表单组件。
interceptAnthropicWarmupRequests的传递方式与其他设置项(如enableHttp2)保持一致,遵循现有模式。messages/en/dashboard.json (1)
130-130: 英语翻译内容清晰准确。
"Skipped"作为表格状态标签简洁明了details.skipped.desc完整说明了 warmup 请求的处理方式:由 CCH 直接响应、不计费、不限流、不计入统计翻译质量良好,与功能需求一致。
Also applies to: 156-161
drizzle/meta/_journal.json (1)
313-319: 变更正常,迁移元数据格式正确。新增的迁移条目结构规范,idx 递增正确,与现有条目格式保持一致。
src/repository/_shared/transformers.test.ts (2)
234-234: 测试覆盖完整,默认值断言正确。正确验证了当
dbSettings为 undefined 时,interceptAnthropicWarmupRequests字段默认为 false。
239-247: 测试用例结构清晰,验证逻辑正确。新增的测试用例正确验证了
interceptAnthropicWarmupRequests字段的映射行为,覆盖了显式设置为 true 的场景。src/drizzle/schema.ts (1)
465-469: 列定义规范,默认值设置合理。新增的
intercept_anthropic_warmup_requests列定义符合现有模式,默认值 false 适合作为可选功能的初始状态,notNull 约束确保了数据一致性。注释清晰说明了功能用途。src/repository/overview.ts (2)
30-30: SQL 过滤逻辑正确,实现符合预期。新增的
excludeWarmup过滤器正确排除了blockedBy = 'warmup'的请求,同时保留了正常请求(NULL)和其他拦截类型,逻辑清晰且安全。
43-43: 过滤器应用位置正确。在 WHERE 子句中正确应用了 warmup 排除逻辑,与现有的 deletedAt 和日期过滤器配合良好,确保概览统计数据不包含 warmup 请求。
messages/zh-TW/settings.json (1)
103-104: 翻译文本清晰准确,键名规范。新增的繁体中文翻译正确描述了 Warmup 拦截功能的行为特性(直接响应、记录但不计费/限流),与其他设置项的翻译风格保持一致。
src/repository/_shared/transformers.ts (1)
154-154: LGTM!新增字段映射符合预期
interceptAnthropicWarmupRequests字段的映射实现正确,使用?? false作为默认值符合 PR 目标(默认关闭拦截)。代码风格与文件中其他字段保持一致。messages/zh-CN/settings.json (1)
86-87: 翻译清晰准确,表述完整新增的中文翻译键值准确描述了 Warmup 请求拦截功能:
- 标签简洁明了
- 描述详细说明了拦截后的行为(记录日志但不计费、不限流、不计入统计),便于用户理解功能影响
messages/ja/settings.json (1)
103-104: 日语翻译结构完整,表述清晰新增的日语翻译键值结构与其他语言环境保持一致,功能说明详细描述了 Warmup 请求的拦截行为和日志记录策略。
src/repository/leaderboard.ts (4)
176-182: Warmup 排除条件已正确应用到用户排行榜查询WHERE 子句中正确添加了
EXCLUDE_WARMUP_CONDITION,确保 warmup 请求不计入用户消耗排行榜统计。
316-321: Warmup 排除条件已正确应用到供应商排行榜查询供应商排行榜的 WHERE 条件数组中正确包含了
EXCLUDE_WARMUP_CONDITION,与其他过滤条件(deletedAt、日期范围、providerType)一起使用,逻辑清晰。
416-422: Warmup 排除条件已正确应用到缓存命中率排行榜查询供应商缓存命中率排行榜的 WHERE 条件中正确包含了
EXCLUDE_WARMUP_CONDITION,确保 warmup 请求不影响缓存统计分析。
561-567: Warmup 排除条件已正确应用到模型排行榜查询模型排行榜查询的 WHERE 子句中正确添加了
EXCLUDE_WARMUP_CONDITION,确保 warmup 请求不计入模型使用统计。src/types/system-config.ts (2)
32-33: 类型定义正确,注释清晰新增的
interceptAnthropicWarmupRequests字段定义为必需的 boolean 类型,确保系统设置中始终存在该值。注释明确说明默认关闭,符合 PR 设计意图。
65-66: 部分更新接口定义正确
UpdateSystemSettingsInput中将interceptAnthropicWarmupRequests定义为可选字段,支持部分更新场景,与其他配置字段的设计保持一致。src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx (2)
114-115: 逻辑正确:warmup 跳过状态的识别与排除warmup 拦截判断逻辑清晰:
isWarmupSkipped检测blockedBy === "warmup",并将其从isBlocked中排除。这样的设计合理,因为 warmup 是信息性的跳过而非错误拦截,应当区别对待。
261-282: UI 实现规范:warmup 跳过信息展示新增的 warmup 跳过信息块实现良好:
- 使用蓝色主题传达"信息性"而非"错误"的语义
- 图标、Badge 和文本层次清晰
- 国际化文本通过
t()正确引用- 与现有的拦截信息块(lines 284-343)结构一致
messages/en/settings.json (1)
105-106: 翻译文本清晰准确新增的 warmup 拦截功能翻译键命名规范,描述文本准确传达了功能要点:CCH 直接响应 warmup 请求、记录审计、不计费不限流不纳入统计。文本简洁易懂。
tests/unit/dashboard-logs-warmup-ui.test.tsx (1)
27-51: 测试实现正确:renderWithIntl 辅助函数与断言
renderWithIntl辅助函数正确使用createRoot和act()进行 React 18 兼容的渲染- 加载真实的翻译文件确保测试使用实际的 i18n 键
- 断言检查 "Skipped" 和 "Warmup" 文本存在于渲染输出中,覆盖了 warmup 跳过状态的 UI 显示
- cleanup 逻辑(
unmount和container.remove())确保测试隔离Also applies to: 88-103
tests/unit/proxy/session-guard-warmup-intercept.test.ts (1)
49-97: 测试实现正确:动态加载与场景覆盖测试设计良好:
- 使用
loadGuard()动态导入确保 mock 在模块加载前生效createMockSession提供精简但充分的ProxySession模拟- 两个测试用例覆盖了关键场景:
- warmup 拦截开启时,不调用
trackSession(避免计入并发)- warmup 拦截关闭时,正常调用
trackSession- 断言清晰验证了
trackSessionMock的调用次数和参数Also applies to: 108-127
messages/zh-CN/dashboard.json (1)
130-130: 中文翻译准确且专业新增的 warmup 跳过相关翻译质量良好:
- "已跳过" 准确传达了 "skipped" 的语义
- "跳过信息" 部分的各项翻译清晰:
- "Warmup 抢答(CCH)" 生动描述了 CCH 拦截 warmup 请求的行为
- 描述文本详细说明了拦截机制、不计费不限流不统计的特性
- 术语与现有中文翻译保持一致
Also applies to: 156-161
tests/unit/error-details-dialog-warmup-ui.test.tsx (1)
52-91: 测试覆盖良好!测试用例正确验证了 warmup 跳过场景下的 UI 行为:
- 验证显示 "Warmup Fast Response (CCH)" 和 "Skipped"
- 验证不显示 "Blocking Information"
测试结构清晰,cleanup 处理得当。
tests/unit/proxy/guard-pipeline-warmup.test.ts (1)
108-146: 测试设计合理!测试正确验证了:
- warmup 步骤位于 pipeline 中正确位置(session 之后、requestFilter 之前)
- warmup 短路行为:返回响应后后续步骤不会执行
基于 learnings,Guard pipeline 执行顺序验证很重要。
src/actions/system-config.ts (1)
40-41: LGTM!新增字段
interceptAnthropicWarmupRequests的处理方式与现有字段一致,遵循了 Zod 验证 + 类型安全的模式。Also applies to: 61-62
drizzle/meta/0044_snapshot.json (1)
1858-1864: 快照正确反映了 schema 变更!新列
intercept_anthropic_warmup_requests配置正确:
- 类型:boolean
- 非空:true
- 默认值:false
与 PR 目标一致(默认关闭)。
src/repository/system-config.ts (1)
89-89: LGTM!新字段
interceptAnthropicWarmupRequests已正确添加到:
createFallbackSettings()默认值- 所有
select查询insert的returning子句update逻辑和returning子句实现与现有字段模式一致。
Also applies to: 114-114, 147-148, 171-172, 253-256, 275-276
src/app/[locale]/settings/config/_components/system-settings-form.tsx (1)
201-216: LGTM!新增的 Switch 组件实现与现有布尔设置(如
enableHttp2、verboseProviderError)保持一致:
- 使用
Label+ 描述 +Switch的标准布局- 正确使用 i18n 翻译键
isPending时禁用交互src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)
93-94: 逻辑清晰!新增的
isWarmupSkipped和isMutedRow标志正确识别 warmup 跳过的请求,并统一应用样式(与 non-billing 类似的灰度处理)。Also applies to: 101-101
src/app/v1/_lib/proxy/guard-pipeline.ts (3)
13-13: LGTM!导入语句和类型定义遵循现有模式,实现简洁清晰。
Also applies to: 35-35
96-101: LGTM!Warmup guard 实现遵循了与其他 guard 一致的模式,代码结构清晰。
181-181: Guard 顺序设计合理。Warmup guard 放置在
session之后、requestFilter之前的位置是恰当的:
- 在 session 之后:可以访问
authState和sessionId等会话信息- 在 rateLimit 之前:确保 warmup 请求不会被计入速率限制(符合 PR 目标)
- 在 provider 之前:warmup 请求直接响应,无需解析真实的 provider
根据编码指南,guard pipeline 的执行顺序已正确维护。
tests/unit/proxy/session.test.ts (2)
34-34: LGTM!测试辅助函数的扩展设计良好:
- 保持向后兼容(提供默认值)
- 参数命名清晰(
requestUrl,requestMessage)- 最小化对现有测试的影响
Also applies to: 53-60, 78-81
430-669: 测试覆盖全面且结构清晰。新增的
isWarmupRequest测试套件覆盖了:
- ✅ 正向案例:合法 warmup 请求识别(大小写、空格容忍)
- ✅ Endpoint 校验:非
/v1/messages正确拒绝- ✅ Cache control 校验:缺失或非 ephemeral 正确拒绝
- ✅ 结构严格性:多消息/多块/异常结构正确拒绝
- ✅ 边界条件:null 值、错误类型、字段缺失
测试用例设计合理,有效防止误判。
src/lib/config/system-settings-cache.ts (2)
27-30: LGTM!新增配置项遵循现有模式,默认值设置为
false是合理的安全默认值(opt-in 特性)。
88-88: LGTM!Fallback 配置对象正确包含了新字段,确保缓存获取失败时的一致性。
src/repository/statistics.ts (2)
18-19: 常量定义清晰且命名准确。
EXCLUDE_WARMUP_CONDITION的实现正确过滤了被标记为 warmup 的请求,确保这些探测请求不会污染统计数据。
51-51: Warmup 排除逻辑应用一致且全面。在所有统计和成本计算查询中正确应用了
EXCLUDE_WARMUP_CONDITION,确保:
- 用户/密钥统计数据不包含 warmup 请求
- 成本计算(总成本、时间范围成本、成本明细)排除 warmup 请求
- 符合 PR 目标:warmup 请求不计入计费、统计、配额
实现遵循了 DRY 原则,使用统一的条件常量。
Also applies to: 87-87, 123-123, 159-159, 235-235, 276-276, 317-317, 358-358, 440-440, 470-470, 512-512, 542-542, 584-584, 614-614, 656-656, 686-686, 733-733, 785-785, 814-814, 839-840, 875-876, 909-910, 944-945, 990-991, 1183-1184
tests/unit/proxy/warmup-guard.test.ts (3)
1-82: 测试设置结构良好且全面。Mock 配置和测试辅助函数设计合理:
- ✅ 依赖项 mock 覆盖完整(config、db、session-manager、logger)
- ✅
loadGuard()使用动态导入确保 mock 在模块加载前生效- ✅
createMockSession()提供合理的默认值和灵活的覆盖机制- ✅
beforeEach正确重置所有 mock 状态
84-112: 负向测试用例覆盖充分。三个测试用例正确验证了 guard 的各个提前退出条件:
- 非 warmup 请求:验证不读取系统设置(性能优化)
- 功能开关关闭:验证配置项生效
- 认证态不完整:验证必要前置条件
每个测试都确认了不会产生副作用(DB 插入、Session 存储),符合 fail-open 策略。
114-180: 正向测试用例全面且精确。成功拦截测试(lines 114-169)验证了:
- ✅ HTTP 响应:状态码 200、content-type、自定义标识头
- ✅ 响应体结构:符合 Anthropic Messages API 格式
- ✅ Session 存储:响应体、响应头、上下游元数据
- ✅ 数据库日志:
providerId=0、blockedBy='warmup'、costUsd=null等字段- ✅ 调试日志:记录拦截事件
故障恢复测试(lines 171-179)验证了:
- ✅ DB 写入失败不影响 HTTP 响应
- ✅ 错误被正确记录
符合编码指南中的 fail-open 策略。
src/repository/usage-logs.ts (2)
372-389: 统计聚合逻辑正确排除 warmup 请求使用 PostgreSQL 的
FILTER (WHERE ...)子句对聚合函数进行条件过滤是正确的做法。totalRows保持包含所有记录(用于分页),而totalRequests及其他统计字段排除 warmup 请求,符合 PR 目标中"warmup 请求不计入统计"的要求。
587-603: findUsageLogsStats 正确应用 warmup 排除条件
statsConditions在原有条件基础上追加EXCLUDE_WARMUP_CONDITION,确保独立的聚合统计查询也排除 warmup 请求,与findUsageLogsWithDetails保持一致。src/app/v1/_lib/proxy/warmup-guard.ts (2)
110-131: Warmup 响应体结构响应体格式符合 Anthropic API 规范,包含必要字段。使用
crypto.randomBytes生成随机 ID 是安全的做法。一个小建议:
stop_reason: "end_turn"是 Anthropic 的标准值,可以考虑添加注释说明这是模拟正常对话结束的响应。
73-94: definerId=0 为特殊标记的设计已确认安全,建议提取为常量以便维护已确认
providers表使用 PostgreSQLserial类型,自增 ID 从 1 开始,providerId: 0作为特殊标记值不会与实际供应商 ID 冲突。建议将
providerId: 0提取为命名常量(如const WARMUP_PROVIDER_ID = 0或const BLOCKED_PROVIDER_ID = 0),因为该值目前在多个文件中硬编码使用(warmup-guard.ts、sensitive-word-guard.ts、测试/webhook 模板),统一常量定义可提升代码可读性和维护性。
| await db.insert(messageRequest).values({ | ||
| providerId: 0, // 特殊值:表示未请求上游供应商(CCH 抢答) | ||
| userId: authState.user.id, | ||
| key: authState.apiKey, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for authState.apiKey usage and any hashing operations
rg -n "authState.*apiKey|apiKey.*hash|SHA-256|crypto" --type ts -g "*auth*.ts" -g "*session*.ts" -g "*warmup*.ts"Repository: ding113/claude-code-hub
Length of output: 1045
🏁 Script executed:
# Check where authState is defined and how apiKey is assigned
rg -n "authState\s*=" --type ts -B3 -A3Repository: ding113/claude-code-hub
Length of output: 2511
🏁 Script executed:
# Search for database insert operations with key field
rg -n "db\.insert.*messageRequest|messageRequest.*values" --type ts -B5 -A5Repository: ding113/claude-code-hub
Length of output: 2648
🏁 Script executed:
# Check for any hash or encryption functions in the codebase
fd -t f -name "*.ts" | xargs rg -l "SHA-256|sha256|crypto.*hash" | head -20Repository: ding113/claude-code-hub
Length of output: 460
🏁 Script executed:
# Find ProxyAuthenticator definition
rg -n "class ProxyAuthenticator|export.*ProxyAuthenticator" --type ts -B2 -A15Repository: ding113/claude-code-hub
Length of output: 1507
🏁 Script executed:
# Find where authState is defined/typed
rg -n "type AuthState|interface AuthState" --type ts -B2 -A10Repository: ding113/claude-code-hub
Length of output: 816
🏁 Script executed:
# Check the validate method of ProxyAuthenticator
rg -n "validate\s*\(" --type ts -B3 -A20 | grep -A20 "ProxyAuthenticator"Repository: ding113/claude-code-hub
Length of output: 2733
🏁 Script executed:
# Search for the actual ProxyAuthenticator implementation file
fd -type f -name "*authenticator*.ts" -o -name "*auth*.ts" | grep -v node_modulesRepository: ding113/claude-code-hub
Length of output: 237
🏁 Script executed:
# Read the full auth-guard.ts file to see the complete validate implementation
cat -n src/app/v1/_lib/proxy/auth-guard.tsRepository: ding113/claude-code-hub
Length of output: 8430
🏁 Script executed:
# Find database schema for messageRequest table
fd -type f -name "*.ts" | xargs rg -l "messageRequest" | head -10Repository: ding113/claude-code-hub
Length of output: 560
🏁 Script executed:
# Look for schema definitions
rg -n "messageRequest.*=|table.*messageRequest" --type ts -B2 -A10Repository: ding113/claude-code-hub
Length of output: 23465
🏁 Script executed:
# Check if there are migrations or schema files
find . -type f \( -name "*schema*" -o -name "*migrations*" \) | grep -E "\.(ts|sql)$"Repository: ding113/claude-code-hub
Length of output: 317
🏁 Script executed:
# Read the complete messageRequest schema definition
sed -n '264,320p' src/drizzle/schema.tsRepository: ding113/claude-code-hub
Length of output: 2192
API 密钥存储安全问题:未进行 SHA-256 哈希
验证确认:authState.apiKey 为明文字符串(在 src/app/v1/_lib/proxy/auth-guard.ts 中直接从请求头提取),数据库 messageRequest 表的 key 字段存储时未进行任何哈希处理。
这违反了 coding guidelines 的要求:"Hash API keys using SHA-256 before storing in database, never store plaintext keys"。
受影响的位置包括:
warmup-guard.ts第 76 行sensitive-word-guard.ts第 94 行message-service.ts第 37 行
需要在数据库插入前使用 SHA-256 对 API 密钥进行哈希,或在应用层验证时哈希密钥以支持比对。
🤖 Prompt for AI Agents
In src/app/v1/_lib/proxy/warmup-guard.ts around lines 73 to 76, the API key is
being stored in plaintext (authState.apiKey) — compute a SHA-256 hash of the API
key and store the hex (or base64) digest instead of the raw key; implement this
by invoking a shared helper (e.g., hashApiKey(apiKey): string) that performs a
SHA-256 digest using Node's crypto and returns the encoded string, and replace
key: authState.apiKey with key: hashApiKey(authState.apiKey); apply the same
change to the other affected locations (sensitive-word-guard.ts line ~94 and
message-service.ts line ~37) so all DB writes and comparisons use the SHA-256
hashed value.
| // Warmup 抢答请求只用于探测/预热:日志可见,但不计入任何聚合统计 | ||
| const EXCLUDE_WARMUP_CONDITION = sql`(${messageRequest.blockedBy} IS NULL OR ${messageRequest.blockedBy} <> 'warmup')`; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 检查 EXCLUDE_WARMUP_CONDITION 在代码库中的定义和使用情况
rg -n "EXCLUDE_WARMUP_CONDITION" --type tsRepository: ding113/claude-code-hub
Length of output: 3611
提取 EXCLUDE_WARMUP_CONDITION 常量到共享文件
EXCLUDE_WARMUP_CONDITION 在 4 个文件中重复定义:
- src/repository/leaderboard.ts:11
- src/repository/usage-logs.ts:9
- src/repository/statistics.ts:19
- src/repository/key.ts:12
所有定义使用完全相同的 SQL 逻辑。建议创建专用的 constants 文件(如 src/repository/constants.ts)并从各文件中导入此常量,避免重复定义,降低后续维护风险。
There was a problem hiding this comment.
Code Review Summary
This PR adds an optional system setting to intercept Anthropic warmup requests directly at the proxy level. The implementation is thorough, well-structured, and follows existing patterns in the codebase. The code properly excludes warmup requests from billing, rate limiting, quotas, and statistics while maintaining audit visibility in logs.
PR Size: XL
- Lines changed: 4494 (4426 additions + 68 deletions)
- Files changed: 49
Split suggestions for future consideration: Given the XL size, future PRs of this nature could potentially be split into:
- Core warmup detection/interception logic
- Repository layer changes (statistics exclusion)
- UI changes (logs display)
However, the current PR is cohesive as a single feature and the complexity is justified.
Issues Found
| 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 |
No significant issues identified in this PR.
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean (errors properly logged, failures don't block responses)
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Comprehensive (8 new test files)
- Code clarity - Good
Review Notes
Strengths observed:
- The warmup detection logic (
isWarmupRequest()) insession.ts:567-634is thorough with strict validation criteria to minimize false positives - Error handling in
warmup-guard.ts:70-76properly logs failures without blocking the response - Consistent application of
EXCLUDE_WARMUP_CONDITIONacross all statistics/billing repositories - Session tracking is correctly skipped for warmup requests to avoid affecting concurrent session limits
- UI properly distinguishes warmup requests with separate visual styling (blue badge vs orange for blocked)
- All 5 locales have been updated with appropriate translations
Architecture decisions validated:
- The guard pipeline placement (after session, before rateLimit/provider) correctly ensures warmup requests bypass billing/limits
- Using
providerId: 0andblockedBy: 'warmup'provides clear audit trail - The feature is opt-in (default: false) which is appropriate for potentially breaking behavior changes
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
This PR adds an optional system setting to intercept Anthropic warmup requests directly at the proxy level. The implementation is thorough, well-structured, and follows existing patterns in the codebase. The code properly excludes warmup requests from billing, rate limiting, quotas, and statistics while maintaining audit visibility in logs.
PR Size: XL
- Lines changed: 4494 (4426 additions + 68 deletions)
- Files changed: 49
Split suggestions for future consideration: Given the XL size, future PRs of this nature could potentially be split into:
- Core warmup detection/interception logic
- Repository layer changes (statistics exclusion)
- UI changes (logs display)
However, the current PR is cohesive as a single feature and the complexity is justified.
Issues Found
| 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 |
No significant issues identified in this PR.
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean (errors properly logged, failures do not block responses)
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Comprehensive (8 new test files)
- Code clarity - Good
Review Notes
Strengths observed:
- The warmup detection logic in session.ts:567-634 is thorough with strict validation criteria to minimize false positives
- Error handling in warmup-guard.ts:70-76 properly logs failures without blocking the response
- Consistent application of EXCLUDE_WARMUP_CONDITION across all statistics/billing repositories
- Session tracking is correctly skipped for warmup requests to avoid affecting concurrent session limits
- UI properly distinguishes warmup requests with separate visual styling (blue badge vs orange for blocked)
- All 5 locales have been updated with appropriate translations
Architecture decisions validated:
- The guard pipeline placement (after session, before rateLimit/provider) correctly ensures warmup requests bypass billing/limits
- Using providerId: 0 and blockedBy: warmup provides clear audit trail
- The feature is opt-in (default: false) which is appropriate for potentially breaking behavior changes
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
No significant issues identified in this PR based on diff-scope review.
Reviewed the warmup interception flow, system settings plumbing, repository/statistics exclusions, and added unit/UI tests.
PR Size: XL
- Lines changed: 4494
- Files changed: 49
- Split suggestions: Consider splitting into (1) proxy warmup guard + pipeline changes, (2) system settings + DB migration, (3) repository/statistics exclusions + dashboard UI, (4) tests + i18n updates.
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
Automated review by Codex AI
Summary
Adds an optional system setting to intercept Anthropic warmup requests directly at the proxy level, avoiding unnecessary upstream API calls and output token costs.
背景 / Background
Anthropic
/v1/messagesoccasionally sends warmup requests that waste upstream API calls and output tokens.变更 / Changes
interceptAnthropicWarmupRequests(default: off) - when enabled, CCH directly responds to warmup requests with minimal valid responsemessage_requestwithblocked_by=warmup,provider_id=0,cost_usd=NULL, and clearly labeled in logs list/detail viewsSessionTracker.trackSessionnot called)x-cch-intercepted: warmupheaderDatabase Migration
drizzle/0044_uneven_donald_blake.sqlintercept_anthropic_warmup_requests(default: false) tosystem_settingstableFiles Changed
Core Changes
src/app/v1/_lib/proxy/warmup-guard.tssrc/app/v1/_lib/proxy/guard-pipeline.tssrc/app/v1/_lib/proxy/session-guard.tssrc/app/v1/_lib/proxy/session.tsUI Changes
src/app/[locale]/settings/config/_components/system-settings-form.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-table.tsxsrc/app/[locale]/dashboard/logs/_components/error-details-dialog.tsxRepository/Data Layer
src/repository/statistics.tssrc/repository/message.tssrc/repository/usage-logs.tssrc/repository/leaderboard.tsi18n
Testing
Automated Tests
tests/unit/proxy/warmup-guard.test.ts- Warmup detection logictests/unit/proxy/session-guard-warmup-intercept.test.ts- Session guard integrationtests/unit/proxy/guard-pipeline-warmup.test.ts- Pipeline integrationtests/unit/proxy/session.test.ts- Session warmup methodstests/unit/repository/warmup-stats-exclusion.test.ts- Statistics exclusiontests/unit/dashboard-logs-warmup-ui.test.tsx- Logs UItests/unit/error-details-dialog-warmup-ui.test.tsx- Error dialog UIVerification Steps
Checklist
Description enhanced by Claude AI