fix: restore dashboard API compatibility#1145
Conversation
📝 WalkthroughWalkthrough本次变更:引入仪表板兼容请求头并在客户端/服务端透传该头;集中/规范用户相关日期字段的传输序列化(ISO/null);扩展路由与处理器以支持内部 providerType 值并在兼容模式下放宽可见性检查;将部分原生 SQL 改为 Drizzle 类型化表达式;补充/更新大量测试。 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a dashboard compatibility layer via a new X-CCH-Dashboard-Compat header, allowing the V1 API to handle 'hidden' provider types that were previously filtered out. The changes span API handlers, Zod schemas, and client-side actions to conditionally permit these types. Additionally, the PR improves date serialization/deserialization for transport, increases user search limits, and refactors the rate-limit statistics repository to use type-safe SQL construction. Feedback highlights opportunities to remove redundant client-side filtering and aggregation logic in the provider-endpoints client, as the server-side handlers now natively support these operations when the compatibility header is present.
| const providerTypeQuery = hiddenProviderTypes.has(input.providerType ?? "") | ||
| ? undefined | ||
| : input.providerType; |
There was a problem hiding this comment.
Suppressing the providerType query parameter for hidden types in getProviderEndpoints is unnecessary and leads to over-fetching data from the server. The server-side handler in src/app/api/v1/resources/provider-endpoints/handlers.ts now correctly handles hidden types when the X-CCH-Dashboard-Compat header is present. Passing the provider type directly allows the server to perform the filtering, which is more efficient than fetching all endpoints for a vendor and filtering them on the client.
const providerTypeQuery = input.providerType;| VendorTypeQuerySchema, | ||
| } from "@/lib/api/v1/schemas/provider-endpoints"; | ||
|
|
||
| const INTERNAL_PROVIDER_TYPE_VALUES = [ | ||
| "claude", | ||
| "claude-auth", | ||
| "codex", | ||
| "gemini", | ||
| "gemini-cli", |
There was a problem hiding this comment.
INTERNAL_PROVIDER_TYPE_VALUES duplicated across handler files
The exact same array is defined verbatim in both src/app/api/v1/resources/provider-endpoints/handlers.ts and src/app/api/v1/resources/providers/handlers.ts. If a new internal provider type is introduced, both files must be updated in sync. Moving this constant to @/lib/api/v1/_shared/constants.ts alongside HIDDEN_PROVIDER_TYPES would eliminate the duplication and make it easier to maintain.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/api/v1/resources/provider-endpoints/handlers.ts
Line: 29-37
Comment:
**`INTERNAL_PROVIDER_TYPE_VALUES` duplicated across handler files**
The exact same array is defined verbatim in both `src/app/api/v1/resources/provider-endpoints/handlers.ts` and `src/app/api/v1/resources/providers/handlers.ts`. If a new internal provider type is introduced, both files must be updated in sync. Moving this constant to `@/lib/api/v1/_shared/constants.ts` alongside `HIDDEN_PROVIDER_TYPES` would eliminate the duplication and make it easier to maintain.
How can I resolve this? If you propose a fix, please make it concise.| providerType?: string; | ||
| dashboard?: boolean; | ||
| }) { | ||
| const providerTypeQuery = hiddenProviderTypes.has(input.providerType ?? "") | ||
| ? undefined | ||
| : input.providerType; | ||
| return apiGet<{ items?: ProviderEndpoint[] }>( | ||
| `/api/v1/provider-vendors/${input.vendorId}/endpoints${searchParams({ | ||
| providerType: input.providerType, | ||
| providerType: providerTypeQuery, | ||
| dashboard: input.dashboard, | ||
| })}` | ||
| ).then(unwrapItems); | ||
| })}`, | ||
| dashboardCompatOptions | ||
| ) | ||
| .then(unwrapItems) | ||
| .then((items) => | ||
| input.providerType ? items.filter((item) => item.providerType === input.providerType) : items | ||
| ); | ||
| } | ||
|
|
There was a problem hiding this comment.
Over-fetching when resolving hidden-type endpoint stats
For hidden provider types the providerType query param is intentionally stripped from the request URL, so the server returns all endpoints for the vendor regardless of type. The client then filters locally via items.filter(item => item.providerType === input.providerType). A vendor with large numbers of mixed-type endpoints will always transfer the full set just to discard the non-matching rows. This is acceptable for a compat layer, but noting it so the team is aware of the N-per-vendor fan-out and the unbounded response size in pathological cases.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/api-client/v1/actions/provider-endpoints.ts
Line: 70-88
Comment:
**Over-fetching when resolving hidden-type endpoint stats**
For hidden provider types the `providerType` query param is intentionally stripped from the request URL, so the server returns all endpoints for the vendor regardless of type. The client then filters locally via `items.filter(item => item.providerType === input.providerType)`. A vendor with large numbers of mixed-type endpoints will always transfer the full set just to discard the non-matching rows. This is acceptable for a compat layer, but noting it so the team is aware of the N-per-vendor fan-out and the unbounded response size in pathological cases.
How can I resolve this? If you propose a fix, please make it concise.
🧪 测试结果
总体结果: ✅ 所有测试通过 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/app/api/v1/resources/provider-endpoints/handlers.ts (1)
32-59: ⚡ Quick win避免在 compat 路径里再维护一份 providerType 白名单。
这里把公开类型和隐藏类型手工拼成了第二份枚举。后续只要主 schema 新增或更名 provider type,这些 compat 分支就会先开始返回 400,把这次修复过的兼容层再次变成回归点。更稳妥的是从同一个权威来源派生,或者至少复用共享常量来组装这些内部 schema。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/api/v1/resources/provider-endpoints/handlers.ts` around lines 32 - 59, The internal enum duplication (INTERNAL_PROVIDER_TYPE_VALUES and InternalProviderTypeSchema) should be replaced by deriving from the authoritative public provider-type source instead of hand-maintaining a second list; update the code that defines INTERNAL_PROVIDER_TYPE_VALUES / InternalProviderTypeSchema and any schemas that extend it (InternalProviderEndpointListQuerySchema, InternalProviderEndpointCreateSchema, InternalBatchVendorEndpointStatsSchema, InternalVendorTypeQuerySchema, InternalVendorTypeManualOpenSchema, InternalVendorTypeBodySchema) to import or reuse the shared provider-type constant or base schema (the public ProviderType values/schema) and then add any hidden-only types (e.g. "claude-auth", "gemini-cli") programmatically (e.g. union or extend) so the internal schemas are always derived from the single authoritative source rather than a manually duplicated array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/api/v1/resources/provider-endpoints/handlers.ts`:
- Around line 510-512: The current isDashboardCompatRequest(c: Context) trusts
only the X-CCH-Dashboard-Compat header (DASHBOARD_COMPAT_HEADER), allowing
clients to spoof access to hidden provider types; change it to require a
server-trust signal instead (e.g., authenticated session attribute,
role/permission check, or an internal-only route flag) by updating
isDashboardCompatRequest to consult a verified property on Context such as
c.state.session.isInternal, c.state.user?.roles.includes('internal') or
c.auth.hasPermission('dashboard:compat') (or an explicit internalRoute boolean)
and drop the raw header-only check; if you still need to accept the header from
trusted internal services, validate it against the authenticated service
identity in Context rather than trusting it standalone.
In `@src/lib/api-client/v1/actions/provider-endpoints.ts`:
- Around line 181-199: This code creates unbounded parallel requests by
Promise.all over input.vendorIds calling getProviderEndpoints for each vendorId;
limit concurrency (e.g., use a concurrency-controlled mapper like p-map or an
async pool to cap simultaneous calls to a reasonable number such as 5–10) or
batch vendorIds into chunks and perform sequential batches instead of firing all
at once, and ideally move the hidden-type aggregation back to the server so you
can request aggregated stats for multiple vendorIds in one call; update the
block that currently maps input.vendorIds (the Promise.all +
getProviderEndpoints usage) to use the chosen concurrency/batching approach and
ensure the returned totals/healthy/unhealthy/unknown calculations remain the
same.
---
Nitpick comments:
In `@src/app/api/v1/resources/provider-endpoints/handlers.ts`:
- Around line 32-59: The internal enum duplication
(INTERNAL_PROVIDER_TYPE_VALUES and InternalProviderTypeSchema) should be
replaced by deriving from the authoritative public provider-type source instead
of hand-maintaining a second list; update the code that defines
INTERNAL_PROVIDER_TYPE_VALUES / InternalProviderTypeSchema and any schemas that
extend it (InternalProviderEndpointListQuerySchema,
InternalProviderEndpointCreateSchema, InternalBatchVendorEndpointStatsSchema,
InternalVendorTypeQuerySchema, InternalVendorTypeManualOpenSchema,
InternalVendorTypeBodySchema) to import or reuse the shared provider-type
constant or base schema (the public ProviderType values/schema) and then add any
hidden-only types (e.g. "claude-auth", "gemini-cli") programmatically (e.g.
union or extend) so the internal schemas are always derived from the single
authoritative source rather than a manually duplicated array.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 684be430-9dad-4250-b595-c4a6d8084643
📒 Files selected for processing (19)
src/actions/users.tssrc/app/api/v1/resources/provider-endpoints/handlers.tssrc/app/api/v1/resources/providers/handlers.tssrc/lib/api-client/v1/actions/_compat.tssrc/lib/api-client/v1/actions/provider-endpoints.tssrc/lib/api-client/v1/actions/providers.tssrc/lib/api-client/v1/actions/users.tssrc/lib/api/v1/_shared/constants.tssrc/lib/api/v1/_shared/serialization.tssrc/lib/api/v1/schemas/users.tssrc/repository/statistics.tstests/api/v1/dashboard/dashboard.test.tstests/api/v1/provider-endpoints/provider-endpoints.test.tstests/api/v1/providers/providers.read.test.tstests/api/v1/users/users.test.tstests/unit/api/v1/api-client-actions.test.tstests/unit/api/v1/schema-serialization.test.tstests/unit/repository/rate-limit-stats-query.test.tstests/unit/users-action-get-users-compat.test.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0a9cb5f5e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (hiddenInput) { | ||
| return toActionResult(loadHiddenVendorEndpointStats(hiddenInput)); |
There was a problem hiding this comment.
Use batch endpoint for hidden vendor endpoint stats
When providerType is hidden, this branch bypasses /api/v1/provider-vendors/endpoint-stats:batch and calls getProviderEndpoints once per vendor via Promise.all. In the dashboard flow, batches can include up to 500 vendor IDs, so this introduces an N-request fan-out that can dramatically increase latency and trigger request failures under load. The same commit already added hidden-type parsing support for compat requests on the batch stats route, so this fallback now creates a performance regression without a functional requirement.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Code Review Summary
This PR effectively restores dashboard/API compatibility broken by the v1 REST management migration. The fixes address date serialization, hidden provider types, and rate-limit stats queries. Code is well-tested and the architecture appropriately uses a header-based compatibility layer.
PR Size: L
- Lines changed: 954 (829 additions, 125 deletions)
- Files changed: 19
Note: This is a large but focused PR fixing regressions across 5 independent areas. The changes are cohesive and splitting would add unnecessary overhead given the interrelated nature of the dashboard compatibility fixes.
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 1 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Standards | 0 | 0 | 1 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Medium Priority Issues (Should Fix)
-
toISOString() without invalid-Date guard (src/actions/users.ts:953)
- Functions toActionTransportDate and toRequiredActionTransportDate call toISOString() without validating the Date is valid. Invalid dates throw RangeError. The existing dateToIsoString helper in serialization.ts shows the correct pattern with Number.isFinite(timestamp) validation.
- Suggested fix: Add guard: const ts = value.getTime(); return (Number.isFinite(ts) ? value.toISOString() : null) as unknown as Date;
-
INTERNAL_PROVIDER_TYPE_VALUES duplication (src/app/api/v1/resources/provider-endpoints/handlers.ts:32-39, src/app/api/v1/resources/providers/handlers.ts:44-51)
- The same array is defined in both files. Per CLAUDE.md conventions, move this to src/lib/api/v1/_shared/constants.ts alongside HIDDEN_PROVIDER_TYPES for maintainability.
Positive Observations
- Good test coverage: New unit and integration tests cover date serialization, dashboard compatibility headers, and rate-limit query changes
- Proper error handling: Rate-limit stats query properly refactored to use Drizzle ORM with type-safe parameters
- Security conscious: Dashboard compat header (X-CCH-Dashboard-Compat) provides clean separation between public and internal schemas
- Date serialization: Cross-realm Date handling and toJSON support added to serializeDates
Review Coverage
- Logic and correctness - Fixes correctly address the 5 regression areas
- Security (OWASP Top 10) - No vulnerabilities identified
- Error handling - Proper logging and error propagation
- Type safety - Appropriate Zod schema extensions for internal types
- Documentation accuracy - Comments match code behavior
- Test coverage - Adequate unit and integration tests
- Code clarity - Clear separation of concerns with dashboard compat layer
Automated review by Claude AI
| const hiddenInput = parseHiddenVendorEndpointStatsInput(input); | ||
| if (hiddenInput) { | ||
| return toActionResult(loadHiddenVendorEndpointStats(hiddenInput)); | ||
| } |
There was a problem hiding this comment.
[MEDIUM] [PERFORMANCE-ISSUE] Hidden provider endpoint stats fall back to N+1 requests
Evidence (src/lib/api-client/v1/actions/provider-endpoints.ts:149):
return toActionResult(loadHiddenVendorEndpointStats(hiddenInput));
Why this is a problem: loadHiddenVendorEndpointStats issues one getProviderEndpoints request per vendorId (via Promise.all). That can significantly slow the dashboard and increase server load when vendorIds is large, despite a batch endpoint existing.
The v1 server handler already accepts hidden provider types under the dashboard compat header (InternalBatchVendorEndpointStatsSchema), so the batch endpoint should be usable for hidden provider types as well.
Guideline: "[PERFORMANCE-ISSUE] - N+1 queries, memory leaks, inefficient algorithms".
Suggested fix (prefer the batch endpoint; optionally keep a fallback only for older servers):
export function batchGetVendorTypeEndpointStats(input: unknown) {
return toActionResult(
apiPost<VendorTypeEndpointStats[]>(
"/api/v1/provider-vendors/endpoint-stats:batch",
input,
dashboardCompatOptions
)
);
}There was a problem hiding this comment.
Code Review Summary
This PR introduces multiple compatibility shims (dashboard-compat header, date rehydration, hidden provider type handling) across server handlers, client actions, and repository queries. The scope is broad for a regression fix and increases the chance of subtle behavioral drift unless the remaining edge cases and performance tradeoffs are tightened.
PR Size: L
- Lines changed: 954
- Files changed: 19
- Split suggestion: (1) user date transport/rehydration, (2) provider/provider-endpoint dashboard-compat + hidden types, (3) rate-limit stats query + serialization changes.
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 1 | 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 | 1 | 0 |
Critical Issues (Must Fix)
None.
High Priority Issues (Should Fix)
- (Confidence 90) [LOGIC-BUG]
normalizeDatedrops numeric timestamp0-> null (src/lib/api-client/v1/actions/users.ts:88). - (Confidence 85) [PERFORMANCE-ISSUE] Hidden provider endpoint stats fan out into N+1 requests instead of batch (
src/lib/api-client/v1/actions/provider-endpoints.ts:149).
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.
- Applied PR label
size/L. - Posted 2 inline review comments:
src/lib/api-client/v1/actions/users.ts:88—[LOGIC-BUG]normalizeDatedrops numeric timestamp0.src/lib/api-client/v1/actions/provider-endpoints.ts:149—[PERFORMANCE-ISSUE]hidden provider endpoint stats fan out into N+1 requests instead of using the batch endpoint.
- Submitted the required “Code Review Summary” review comment on PR #1145.
🧪 测试结果
总体结果: ✅ 所有测试通过 |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/api/v1/resources/provider-endpoints/handlers.ts`:
- Around line 67-70: The details endpoint getProviderVendor is returning
sanitizeProviderEndpointData(result.data) without applying visibility filtering,
so hidden providerTypes can leak for compat requests; modify the
getProviderVendor handler to detect compat requests with
isDashboardCompatRequest(c) and, when true, pass result.data through
filterVisibleProviderTypes before calling sanitizeProviderEndpointData (mirror
the logic used in the list handler), ensuring the same filtered data is returned
for /provider-vendors/{id}.
In `@src/app/api/v1/resources/provider-endpoints/router.ts`:
- Around line 47-71: The route-level schema
DashboardCompatProviderTypeRouteSchema currently restricts providerType to
PUBLIC_PROVIDER_TYPE_VALUES which blocks dashboard-compat requests that use
hidden/internal types (e.g., claude-auth, gemini-cli) before handlers run;
update DashboardCompatProviderTypeRouteSchema to accept the broader set (use
INTERNAL_PROVIDER_TYPE_VALUES or a union of PUBLIC_PROVIDER_TYPE_VALUES and
INTERNAL_PROVIDER_TYPE_VALUES) so routes like
ProviderEndpointListRouteQuerySchema, ProviderEndpointCreateRouteSchema,
BatchVendorEndpointStatsRouteSchema, VendorTypeRouteQuerySchema,
VendorTypeManualOpenRouteSchema and VendorTypeBodyRouteSchema can validate
compat requests without rejecting hidden types, or alternatively create a
separate route-level schema for dashboard-compat routes that uses
INTERNAL_PROVIDER_TYPE_VALUES.
In `@src/app/api/v1/resources/providers/router.ts`:
- Around line 63-81: The route-level Zod schemas (ProviderListRouteQuerySchema,
ProviderUnifiedTestRouteSchema, ProviderTypeRouteQuerySchema,
ProviderFetchUpstreamModelsRouteSchema) currently constrain providerType to
PUBLIC_PROVIDER_TYPE_VALUES via DashboardCompatProviderTypeRouteSchema, which
prevents hidden types from reaching handlers; change the route registration to
select a looser schema when DASHBOARD_COMPAT_HEADER is present (e.g., in
createRoute check request.headers[DASHBOARD_COMPAT_HEADER] and use the
InternalProviderListQuerySchema / InternalProviderUnifiedTestSchema or a
permissive z.string() providerType schema instead) so requests with hidden
provider types (handled by isDashboardCompatRequest()) are not blocked by
pre-handler validation. Ensure the referenced symbols are used: modify
createRoute, ProviderListRouteQuerySchema, ProviderUnifiedTestRouteSchema,
ProviderTypeRouteQuerySchema, ProviderFetchUpstreamModelsRouteSchema,
DashboardCompatProviderTypeRouteSchema, isDashboardCompatRequest(),
InternalProviderListQuerySchema and InternalProviderUnifiedTestSchema.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0f552ff9-07b6-4c89-ad6b-bf2aa25c9294
📒 Files selected for processing (13)
src/actions/users.tssrc/app/api/v1/resources/provider-endpoints/handlers.tssrc/app/api/v1/resources/provider-endpoints/router.tssrc/app/api/v1/resources/providers/handlers.tssrc/app/api/v1/resources/providers/router.tssrc/lib/api-client/v1/actions/provider-endpoints.tssrc/lib/api-client/v1/openapi-types.gen.tssrc/lib/api/v1/_shared/constants.tssrc/lib/api/v1/schemas/_common.tstests/api/v1/provider-endpoints/provider-endpoints.test.tstests/api/v1/providers/providers.read.test.tstests/unit/api/v1/api-client-actions.test.tstests/unit/users-action-get-users-compat.test.ts
✅ Files skipped from review due to trivial changes (2)
- src/lib/api-client/v1/openapi-types.gen.ts
- src/lib/api/v1/schemas/_common.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/api/v1/providers/providers.read.test.ts
- src/lib/api/v1/_shared/constants.ts
- tests/unit/api/v1/api-client-actions.test.ts
🧪 测试结果
总体结果: ✅ 所有测试通过 |
🧪 测试结果
总体结果: ✅ 所有测试通过 |
Summary
Fixes dashboard/API regressions introduced by the v1 REST management migration where multiple dashboard pages rendered empty, threw Date-related client errors, or hit v1 validation failures.
Reproduced issues
TypeError: S?.getTime is not a functionbecause v1 JSON transport returned date strings while legacy dashboard components expectedDateinstances.claude-auth,gemini-cli) after public v1 schemas filtered them out./api/v1/dashboard/rate-limit-statsdue Date values flowing through raw SQL parameters./api/v1/users:search?limit=5000400 because the v1 user search limit was capped below the legacy action/repository contract.Fixes
::timestamptzcasts./api/v1/users:searchand filter-search limit support up to 5000.Agent Browser QA
Local target:
http://localhost:13500Local verification
bunx vitest run tests/unit/users-action-get-users-compat.test.ts tests/unit/api/v1/schema-serialization.test.ts tests/unit/api/v1/api-client-actions.test.ts tests/api/v1/users/users.test.ts tests/api/v1/providers/providers.read.test.ts tests/api/v1/provider-endpoints/provider-endpoints.test.ts tests/api/v1/dashboard/dashboard.test.ts tests/unit/repository/rate-limit-stats-query.test.ts --reporter=verbosebun run buildbun run lintbun run lint:fixbun run lintbun run typecheckbun run testNote:
bun run buildstill prints existing Next/Turbopack warnings aboutnode:netimports in Edge runtime traces, but the build exits successfully.Greptile Summary
This PR fixes a set of dashboard/API regressions introduced by the v1 REST management migration: user date fields crashing dashboard components, hidden provider types (
claude-auth,gemini-cli) disappearing from provider/endpoint surfaces, rate-limit stats returning 400 due to rawDateSQL parameters, and user search limit caps blocking legacy callers. The core strategy is aX-CCH-Dashboard-Compat: 1request header (admin-only) that unlocks internal schemas server-side, while the client action layer adds the header automatically and revives ISO date strings back toDateinstances.Confidence Score: 5/5
Safe to merge; all findings are P2 style/quality suggestions with no present runtime defects.
Only P2 findings: a type-lie in
toRequiredActionTransportDate(no runtime crash because the client handles null) and the 5000-user search limit being wider than the dashboard-only intent. The compat-header approach is properly gated behind admin role checks, the Drizzle migration for stats queries is correct, and date normalization logic is well-covered by tests.src/lib/api/v1/schemas/users.ts — limit increase affects both public search endpoints, not just the dashboard path.
Important Files Changed
toActionTransportUserDisplayto serialize Date fields to ISO strings for JSON transport;toRequiredActionTransportDatelies about itsDatereturn type and can actually returnnull.isDashboardCompatRequestguard and internal schemas to allow hidden provider types through the compat header path; logic is consistent and admin-gated.loadVisibleProvidersfor dashboard requests; pre-existingHIDDEN_PROVIDER_TYPES.has()call is unchanged.normalizeUsers/normalizeDateto revive JSON date strings toDateinstances on the client; handles null, string, and numeric epoch inputs correctly.::timestamptzcast correctly avoids passing rawDateobjects through the parameterized query layer.UserFilterSearchQuerySchemalimit from 100 to 5000 — shared by both:searchand:filter-searchendpoints, opening large unbounded fetches to any admin caller.serializeDatesto handle Date-like objects via duck typing and guard invalid dates indateToIsoString;toJSONrecursion guard (jsonValue !== value) prevents simple cycles.DASHBOARD_COMPAT_HEADER,PUBLIC_PROVIDER_TYPE_VALUES, andINTERNAL_PROVIDER_TYPE_VALUES; consolidates provider type enumeration cleanly.Sequence Diagram
sequenceDiagram participant Dashboard as Dashboard (browser) participant ActionClient as API Client (v1 actions) participant Server as Hono Route Handler participant Action as Server Action participant DB as Database Dashboard->>ActionClient: getProviders() / getProviderEndpoints() ActionClient->>Server: GET /api/v1/providers<br/>X-CCH-Dashboard-Compat: 1 Server->>Server: isDashboardCompatRequest(c)<br/>checks header + admin role alt compat + admin Server->>Action: getProviders() [all types] Action->>DB: SELECT providers DB-->>Action: rows (incl. claude-auth, gemini-cli) Action-->>Server: ProviderDisplay[] Server-->>ActionClient: {items: [...all types...]} else public Server->>Action: getProviders() [filtered] Action->>DB: SELECT providers DB-->>Action: rows Action-->>Server: ProviderDisplay[] Server->>Server: filterVisibleProviderTypes() Server-->>ActionClient: {items: [...public types only...]} end ActionClient-->>Dashboard: ProviderDisplay[] Dashboard->>ActionClient: getUsersBatchCore() ActionClient->>Server: GET /api/v1/users Server->>Action: getUsersBatchCore() Action->>Action: toActionTransportUserDisplay()<br/>(Date to ISO string for JSON) Action-->>Server: UserDisplay[] (dates as strings) Server-->>ActionClient: {users: [...]} ActionClient->>ActionClient: normalizeUsers()<br/>(ISO string to Date) ActionClient-->>Dashboard: UserDisplay[] (dates as Date objects)Prompt To Fix All With AI
Reviews (4): Last reviewed commit: "fix: filter hidden provider types in ven..." | Re-trigger Greptile