Request logs - #3719
Conversation
…andling in API log recording
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a complete API/webhook logging system: datasources and pipes for Tinybird, server routes to list/count/fetch logs, capture/ingest logic for requests and webhooks, Prisma-based enrichment, retention utilities, client UI for listing and viewing log details with filters, and instrumentation in middleware and webhook handlers. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Request
participant Middleware as Workspace Middleware
participant Handler as Route Handler
participant Capture as captureRequestLog
participant Record as recordApiLog
participant Tinybird as Tinybird Ingestion
Client->>Middleware: HTTP request
Middleware->>Middleware: extract session, token, workspace, startTime
Middleware->>Handler: invoke route handler
Handler->>Middleware: return Response
Middleware->>Capture: call captureRequestLog (if mutation & workspace)
Capture->>Capture: parse bodies, match routePattern
Capture->>Record: enqueue recordApiLog via waitUntil
Record->>Tinybird: ingest payload (retry/backoff)
Middleware->>Client: respond
sequenceDiagram
participant Browser as Logs UI
participant Router as Next.js Router
participant APIRoute as GET /api/logs
participant Middleware as withWorkspace
participant GetLogs as getApiLogs
participant Tinybird as Tinybird Query
participant Enrich as enrichApiLogs
participant Prisma as Prisma DB
Browser->>Router: request logs with filters/page
Router->>APIRoute: call API route
APIRoute->>Middleware: check workspace & perms
Middleware->>GetLogs: call with filters & date range
GetLogs->>Tinybird: run query (WHERE, LIMIT/OFFSET)
Tinybird->>GetLogs: return raw logs
GetLogs->>Enrich: enrich logs
Enrich->>Prisma: fetch tokens & users by ids
Prisma->>Enrich: return entities
Enrich->>APIRoute: return enriched logs
APIRoute->>Browser: JSON response
sequenceDiagram
participant Webhook as Webhook Handler
participant Capture as captureWebhookLog
participant Record as recordApiLog
participant Tinybird as Tinybird Ingestion
participant Caller as External Sender
Caller->>Webhook: POST webhook
Webhook->>Webhook: process event -> finalResponse
Webhook->>Capture: waitUntil(captureWebhookLog(...))
Capture->>Record: call recordApiLog(requestType: "webhook")
Record->>Tinybird: ingest (retry/backoff)
Webhook->>Caller: return finalResponse
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
- Create log-utils.ts with shared ApiLog type and badge variant helpers - Add LogsTable with method/status/endpoint/API key filters, pagination - Refactor detail page to use shared utils from log-utils.ts - Add use-log-filters hook with URL param-based filter state
- Add exponential backoff retry (3 attempts) to recordApiLog - Move Logs nav item after Tracking in Developer sidebar - Update StackY3 icon SVG and use it in empty state - Simplify empty state description
- Introduced HTTP_METHODS and HTTP_STATUS_CODES constants in api-logs/constants.ts - Updated use-log-filters.ts to utilize the new constants for method and status options
- Added new filter for "Endpoint" with options from LOGGED_PATH_PREFIXES - Updated icons for "Method" and "Status" filters to ArrowsOppositeDirectionX and CircleCheck respectively - Removed duplicate "Endpoint" filter definition
- Replace startsWith with minimatch for flexible glob pattern matching in shouldLogRoute - Rename LOGGED_PATH_PREFIXES to LOGGED_API_PATH_PATTERNS with glob suffixes - Add /api/commissions/** and /api/bounties/** to logged paths - Color-code status filter icons (green for 2xx, red for others) - Reorder filters: status, endpoint, method, API key
- Move endpoint column first, timestamp last in logs table - Simplify recordApiLog retry to use return await
- Left column: request/response JSON bodies with Shiki syntax highlighting - Right column: log details sidebar (endpoint, date, status, method, duration, etc.) - Breadcrumb title matching commission detail page pattern - Loading skeleton and error states - TimestampTooltip on date, CopyButton on request ID - 4xx status codes now use error variant instead of warning
- Add enrichApiLogs() to batch-fetch tokens and users from Prisma - Add apiLogEnrichedSchema with token/user fields - Enrich logs in both list and detail API endpoints - Show token partialKey or user name+avatar in table and detail sidebar
- Introduced API_LOGS_MAX_PAGE_SIZE constant set to 10 in constants.ts - Updated get-api-logs.ts and schemas.ts to import the new constant - Removed redundant API_LOGS_MAX_PAGE_SIZE definition from schemas.ts - Simplified logs-table.tsx by removing unused useRouterStuff import
…tion - Replace LIKE with startsWith/exact match in Tinybird pipes to avoid parameterized query escaping issues and prevent LIKE pattern injection - Fix pagination by passing API_LOGS_MAX_PAGE_SIZE to usePagination and including page param in SWR fetch key - Add request ID search input and pass requestId through to Tinybird - Fix SearchBoxPersisted using hardcoded "search" key instead of urlParam - Add column filter buttons (path, method, status code) to logs table - Increase JSON preview max height to 800px
- Gate captureRequestLog to enterprise plan only - Gate Logs sidebar nav item to enterprise plan workspaces - Move EnrichedApiLog type to shared types.ts - Use HTTP_METHODS constant in capture-request-log - Increase JSON preview max height to 800px
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
apps/web/app/api/logs/route.ts (1)
11-17:⚠️ Potential issue | 🟠 MajorMap
page/pageSizebefore callinggetApiLogs.
getApiLogsQuerySchemastill parses page-based params, but this route forwards them unchanged. That keeps pagination as a no-op ifgetApiLogsstill expectslimit/offset, so page 2+ will keep returning the first batch.🛠️ Suggested fix
- const filters = getApiLogsQuerySchema.parse(searchParams); + const { page = 1, pageSize, ...filters } = + getApiLogsQuerySchema.parse(searchParams); const logs = await getApiLogs({ ...filters, ...getApiLogsDateRange(workspace.plan), workspaceId: workspace.id, + limit: pageSize, + offset: (page - 1) * pageSize, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/api/logs/route.ts` around lines 11 - 17, The route currently parses pagination with getApiLogsQuerySchema and passes filters straight into getApiLogs, but getApiLogs expects limit/offset; map page and pageSize from filters to limit and offset before calling getApiLogs (e.g., compute limit = pageSize, offset = (page - 1) * pageSize) and pass those along with ...getApiLogsDateRange(workspace.plan) and workspaceId: workspace.id; update the usage of the parsed filters variable so getApiLogs receives limit/offset instead of page/pageSize.apps/web/app/api/logs/[logId]/route.ts (1)
23-25:⚠️ Potential issue | 🟠 MajorParse the retention cutoff as UTC.
getApiLogsDateRange()returns a ClickHouse-formatted UTC string, butnew Date(start)parses that format in the server's local timezone. The retention boundary will drift accordingly.🛠️ Suggested fix
const { start } = getApiLogsDateRange(workspace.plan); + const retentionStart = new Date(start.replace(" ", "T") + "Z"); - if (new Date(log.timestamp) < new Date(start)) { + if (new Date(log.timestamp) < retentionStart) { throw new DubApiError({ code: "not_found", message: "API log not found.", });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/api/logs/`[logId]/route.ts around lines 23 - 25, getApiLogsDateRange returns a UTC timestamp string but new Date(start) treats it as local time, causing retention cutoff drift; update the comparison in the route handling (the new Date(log.timestamp) < new Date(start) check) to parse the returned start as UTC — e.g., transform the ClickHouse string from getApiLogsDateRange(workspace.plan).start into an ISO UTC form (replace the space with 'T' and append 'Z' or otherwise normalize to UTC) before constructing the Date object, then compare against new Date(log.timestamp).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx:
- Around line 185-208: The UI currently treats
highlightedRequest/highlightedResponse empty strings as "no body", causing valid
payloads to show "No request body" while Shiki highlighting initializes; change
the empty-state checks to use the raw payloads (log.request_body and
log.response_body) to decide whether there is content, and keep the
highlightedRequest/highlightedResponse variables to indicate highlighting state
— when the raw body exists but highlighted* is empty, render a loading indicator
or a plain preformatted/raw-text fallback until highlighting completes or fails;
update the render logic around highlightedRequest, highlightedResponse,
log.request_body, and log.response_body accordingly.
In `@apps/web/ui/logs/logs-table.tsx`:
- Around line 107-111: meta.filterParams currently forwards row.original.method
verbatim but getApiLogsQuerySchema only accepts POST, PATCH, PUT, DELETE; update
the meta.filterParams implementation in logs-table.tsx to validate
row.original.method against the allowed set (e.g. const allowed = new
Set(['POST','PATCH','PUT','DELETE'])) and only return { method } when allowed;
otherwise return an empty object or undefined so the inline filter button isn't
emitted for unsupported methods. Reference the meta.filterParams function and
row.original.method and ensure behavior matches getApiLogsQuerySchema.
---
Duplicate comments:
In `@apps/web/app/api/logs/`[logId]/route.ts:
- Around line 23-25: getApiLogsDateRange returns a UTC timestamp string but new
Date(start) treats it as local time, causing retention cutoff drift; update the
comparison in the route handling (the new Date(log.timestamp) < new Date(start)
check) to parse the returned start as UTC — e.g., transform the ClickHouse
string from getApiLogsDateRange(workspace.plan).start into an ISO UTC form
(replace the space with 'T' and append 'Z' or otherwise normalize to UTC) before
constructing the Date object, then compare against new Date(log.timestamp).
In `@apps/web/app/api/logs/route.ts`:
- Around line 11-17: The route currently parses pagination with
getApiLogsQuerySchema and passes filters straight into getApiLogs, but
getApiLogs expects limit/offset; map page and pageSize from filters to limit and
offset before calling getApiLogs (e.g., compute limit = pageSize, offset = (page
- 1) * pageSize) and pass those along with
...getApiLogsDateRange(workspace.plan) and workspaceId: workspace.id; update the
usage of the parsed filters variable so getApiLogs receives limit/offset instead
of page/pageSize.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 34133f6e-23f9-4674-8d0f-0f703521d33a
📒 Files selected for processing (7)
apps/web/app/api/logs/[logId]/route.tsapps/web/app/api/logs/count/route.tsapps/web/app/api/logs/route.tsapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsxapps/web/lib/swr/use-api-logs-count.tsapps/web/ui/logs/logs-table.tsxpackages/tinybird/datasources/dub_api_logs_id.datasource
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/lib/swr/use-api-logs-count.ts
- packages/tinybird/datasources/dub_api_logs_id.datasource
| {highlightedRequest ? ( | ||
| <div | ||
| className="shiki-wrapper max-h-[800px] overflow-auto rounded-xl border border-neutral-200 bg-white p-4 text-sm" | ||
| dangerouslySetInnerHTML={{ __html: highlightedRequest }} | ||
| /> | ||
| ) : ( | ||
| <div className="rounded-xl border border-neutral-200 bg-white p-4 font-mono text-xs text-neutral-500"> | ||
| No request body | ||
| </div> | ||
| )} | ||
| </div> | ||
| <div className="flex flex-col gap-2"> | ||
| <h3 className="text-content-emphasis text-lg font-semibold"> | ||
| Response body | ||
| </h3> | ||
| {highlightedResponse ? ( | ||
| <div | ||
| className="shiki-wrapper max-h-[800px] overflow-auto rounded-xl border border-neutral-200 bg-white p-4 text-sm" | ||
| dangerouslySetInnerHTML={{ __html: highlightedResponse }} | ||
| /> | ||
| ) : ( | ||
| <div className="rounded-xl border border-neutral-200 bg-white p-4 font-mono text-xs text-neutral-500"> | ||
| No response body | ||
| </div> |
There was a problem hiding this comment.
Separate “no body” from “highlighter still loading”.
Both sections key off highlightedRequest / highlightedResponse, which start as empty strings. That makes valid payloads render as “No request body” / “No response body” until the Shiki import finishes, and forever if it fails. Base the empty-state on log.request_body / log.response_body instead, and show a loading or raw-text fallback while highlighting initializes.
🧰 Tools
🪛 ast-grep (0.42.1)
[warning] 187-187: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
[warning] 202-202: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx
around lines 185 - 208, The UI currently treats
highlightedRequest/highlightedResponse empty strings as "no body", causing valid
payloads to show "No request body" while Shiki highlighting initializes; change
the empty-state checks to use the raw payloads (log.request_body and
log.response_body) to decide whether there is content, and keep the
highlightedRequest/highlightedResponse variables to indicate highlighting state
— when the raw body exists but highlighted* is empty, render a loading indicator
or a plain preformatted/raw-text fallback until highlighting completes or fails;
update the render logic around highlightedRequest, highlightedResponse,
log.request_body, and log.response_body accordingly.
| meta: { | ||
| filterParams: ({ row }: { row: Row<EnrichedApiLog> }) => ({ | ||
| method: row.original.method, | ||
| }), | ||
| }, |
There was a problem hiding this comment.
Don't emit method filters the backend won't parse.
This forwards row.original.method verbatim, but getApiLogsQuerySchema only accepts POST, PATCH, PUT, and DELETE. A row with GET (or any other method) will turn the inline filter button into a failing /api/logs request. Either widen the schema or suppress the filter action for unsupported methods.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/ui/logs/logs-table.tsx` around lines 107 - 111, meta.filterParams
currently forwards row.original.method verbatim but getApiLogsQuerySchema only
accepts POST, PATCH, PUT, DELETE; update the meta.filterParams implementation in
logs-table.tsx to validate row.original.method against the allowed set (e.g.
const allowed = new Set(['POST','PATCH','PUT','DELETE'])) and only return {
method } when allowed; otherwise return an empty object or undefined so the
inline filter button isn't emitted for unsupported methods. Reference the
meta.filterParams function and row.original.method and ensure behavior matches
getApiLogsQuerySchema.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
apps/web/lib/api-logs/record-api-log.ts (2)
59-60:⚠️ Potential issue | 🟡 MinorGuard serialization so circular payloads don’t break log ingestion.
JSON.stringifycan throw on circular structures, which causes this call path to fail before ingest retries can help.🛡️ Suggested change
+const safeStringify = (value: unknown) => { + try { + return JSON.stringify(value); + } catch { + return '"[unserializable]"'; + } +}; + export const recordApiLog = async ({ @@ - request_body: JSON.stringify(requestBody), - response_body: JSON.stringify(responseBody), + request_body: safeStringify(requestBody), + response_body: safeStringify(responseBody),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api-logs/record-api-log.ts` around lines 59 - 60, The current JSON.stringify(requestBody)/JSON.stringify(responseBody) can throw on circular structures; update the record-api-log logic (e.g., in recordApiLog) to guard serialization by using a safe serializer: implement or call a safeSerialize(value) helper that attempts JSON.stringify(value) in try/catch and on failure returns a fallback string (e.g., "[unserializable]" plus minimal error info) or uses a circular-safe replacer/serializer, then replace the direct JSON.stringify calls for request_body and response_body with safeSerialize(requestBody) and safeSerialize(responseBody).
59-60:⚠️ Potential issue | 🟠 MajorAvoid persisting/logging unsanitized request/response bodies.
This currently stores raw bodies and also prints the full payload on failure (Line 79), which can leak secrets/PII. Redact sensitive keys and log only metadata in error paths.
🔒 Suggested tightening
+const SENSITIVE_KEYS = /password|token|authorization|secret|api[-_]?key|cookie|ssn|credit_?card/i; +const MAX_BODY_LEN = 2048; + +const sanitize = (value: unknown): string => { + try { + const seen = new WeakSet<object>(); + const json = JSON.stringify(value, (k, v) => { + if (SENSITIVE_KEYS.test(k)) return "[redacted]"; + if (v && typeof v === "object") { + if (seen.has(v as object)) return "[circular]"; + seen.add(v as object); + } + return v; + }); + return json.length > MAX_BODY_LEN ? `${json.slice(0, MAX_BODY_LEN)}…` : json; + } catch { + return '"[unserializable]"'; + } +}; @@ - request_body: JSON.stringify(requestBody), - response_body: JSON.stringify(responseBody), + request_body: sanitize(requestBody), + response_body: sanitize(responseBody), @@ - console.error("Failed to record API log", error, JSON.stringify(apiLog)); + console.error("Failed to record API log", error, { + id: apiLog.id, + timestamp: apiLog.timestamp, + method: apiLog.method, + path: apiLog.path, + route_pattern: apiLog.route_pattern, + status_code: apiLog.status_code, + request_type: apiLog.request_type, + workspace_id: apiLog.workspace_id, + });Also applies to: 79-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api-logs/record-api-log.ts` around lines 59 - 60, The code currently persists raw requestBody and responseBody (see request_body: JSON.stringify(requestBody) and response_body: JSON.stringify(responseBody)) and prints full payload on the error path around line 79; instead, implement a sanitizer that redacts sensitive keys (e.g., "password", "token", "authorization", "apiKey", "ssn", "creditCard") from requestBody and responseBody before stringifying for storage in recordApiLog (or whatever function wraps this block), and change the error logging path to emit only non-sensitive metadata (status, headers summary, truncated length) rather than the full payload; locate the serialization calls and replace them with calls to the sanitizer/metadata extractor so only redacted bodies are persisted and only metadata is logged on failures.
🧹 Nitpick comments (1)
apps/web/lib/api-logs/record-api-log.ts (1)
54-54: Normalize only a leading/apiprefix.Line 54 uses
replace("/api/", "/"), which can rewrite a non-leading/api/segment. Make normalization prefix-only to avoid unintended path mutation.♻️ Suggested change
- path: path.replace("/api/", "/"), // remove the /api/ prefix from the path + path: + path === "/api" + ? "/" + : path.startsWith("/api/") + ? path.slice(4) + : path, // remove only leading /api prefix🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api-logs/record-api-log.ts` at line 54, The current normalization uses path.replace("/api/", "/"), which can replace non-leading `/api/` segments; change it to only strip a leading `/api` by using a prefix-aware regex such as path.replace(/^\/api(\/|$)/, "/") so "/api/foo" -> "/foo" and "/api" -> "/"; update the occurrence of path.replace in record-api-log (the path property assignment) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/web/lib/api-logs/record-api-log.ts`:
- Around line 59-60: The current
JSON.stringify(requestBody)/JSON.stringify(responseBody) can throw on circular
structures; update the record-api-log logic (e.g., in recordApiLog) to guard
serialization by using a safe serializer: implement or call a
safeSerialize(value) helper that attempts JSON.stringify(value) in try/catch and
on failure returns a fallback string (e.g., "[unserializable]" plus minimal
error info) or uses a circular-safe replacer/serializer, then replace the direct
JSON.stringify calls for request_body and response_body with
safeSerialize(requestBody) and safeSerialize(responseBody).
- Around line 59-60: The code currently persists raw requestBody and
responseBody (see request_body: JSON.stringify(requestBody) and response_body:
JSON.stringify(responseBody)) and prints full payload on the error path around
line 79; instead, implement a sanitizer that redacts sensitive keys (e.g.,
"password", "token", "authorization", "apiKey", "ssn", "creditCard") from
requestBody and responseBody before stringifying for storage in recordApiLog (or
whatever function wraps this block), and change the error logging path to emit
only non-sensitive metadata (status, headers summary, truncated length) rather
than the full payload; locate the serialization calls and replace them with
calls to the sanitizer/metadata extractor so only redacted bodies are persisted
and only metadata is logged on failures.
---
Nitpick comments:
In `@apps/web/lib/api-logs/record-api-log.ts`:
- Line 54: The current normalization uses path.replace("/api/", "/"), which can
replace non-leading `/api/` segments; change it to only strip a leading `/api`
by using a prefix-aware regex such as path.replace(/^\/api(\/|$)/, "/") so
"/api/foo" -> "/foo" and "/api" -> "/"; update the occurrence of path.replace in
record-api-log (the path property assignment) accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0a745d85-8172-40bd-ba2e-dfc53fb00052
📒 Files selected for processing (5)
apps/web/app/api/logs/[logId]/route.tsapps/web/lib/api-logs/capture-request-log.tsapps/web/lib/api-logs/constants.tsapps/web/lib/api-logs/record-api-log.tsapps/web/ui/layout/sidebar/app-sidebar-nav.tsx
✅ Files skipped from review due to trivial changes (1)
- apps/web/lib/api-logs/constants.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/ui/layout/sidebar/app-sidebar-nav.tsx
- apps/web/app/api/logs/[logId]/route.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
apps/web/ui/logs/use-log-filters.ts (1)
135-139:⚠️ Potential issue | 🟡 Minor
onRemoveAllshould still clearpage.Clearing filters can shrink the result set, so keeping the old page cursor can strand the user on an empty page.
onSelectandonRemovealready reset pagination; this handler should stay consistent.♻️ Proposed fix
const onRemoveAll = useCallback( () => queryParams({ - del: ["method", "statusCode", "routePattern", "tokenId", "requestType"], + del: [ + "method", + "statusCode", + "routePattern", + "tokenId", + "requestType", + "page", + ], }), [queryParams], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/ui/logs/use-log-filters.ts` around lines 135 - 139, The onRemoveAll handler currently calls queryParams({ del: ["method","statusCode","routePattern","tokenId","requestType"] }) but does not clear pagination; modify the onRemoveAll implementation (the function named onRemoveAll that calls queryParams) to also remove the "page" param (e.g., include "page" in the del array or explicitly reset page) so it matches onSelect/onRemove behavior and avoids leaving the user on an empty page.apps/web/lib/api-logs/schemas.ts (1)
80-88:⚠️ Potential issue | 🟠 MajorAllow
GEThere, or stop the UI from emitting it.The logs UI can surface
GETrows and buildmethod=GETfilters, but this schema rejects that query. That turns a valid row action into a failing/api/logsrequest. This is the same mismatch raised earlier on the table side; the schema is the root cause.♻️ Proposed fix
export const getApiLogsQuerySchema = z .object({ routePattern: z.string().optional(), - method: z.enum(["POST", "PATCH", "PUT", "DELETE"]).optional(), + method: z.enum(["GET", "POST", "PATCH", "PUT", "DELETE"]).optional(), statusCode: z.coerce.number().int().optional(), tokenId: z.string().optional(), requestId: z.string().optional(), requestType: requestTypeSchema.optional(), })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api-logs/schemas.ts` around lines 80 - 88, The getApiLogsQuerySchema rejects GET requests causing valid UI filters to fail; update the method enum in getApiLogsQuerySchema (the `method` property) to include "GET" (i.e., allow "GET","POST","PATCH","PUT","DELETE") so the schema matches the UI, or alternatively change the UI to stop emitting method=GET filters—prefer adding "GET" to the z.enum for immediate compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/ui/logs/use-log-filters.ts`:
- Around line 38-49: activeFilters currently omits requestId so UI and
empty-state logic don't reflect that hidden filter; update the useMemo that
builds activeFilters (using searchParamsObj destructured into method,
statusCode, routePattern, tokenId, requestType) to also include requestId when
present (e.g., add ...(requestId ? [{ key: "requestId", value: requestId }] :
[])); repeat the same change for the other filter-construction occurrence that
mirrors lines 143-156 so both places track requestId consistently.
- Around line 51-54: The grouped fetch for routePattern uses useApiLogsCount
while the active routePattern filter is still applied, causing the response to
only include the currently selected route; update the useApiLogsCount call where
routePatterns is defined so it explicitly omits or clears the routePattern
filter when building params (e.g., remove/override the routePattern field in the
request payload) whenever selectedFilter === "routePattern" so the dropdown
receives all endpoint options; locate the call to useApiLogsCount and adjust the
filters/params you pass (or clone and delete routePattern) so the grouped
request is not constrained by the active routePattern.
---
Duplicate comments:
In `@apps/web/lib/api-logs/schemas.ts`:
- Around line 80-88: The getApiLogsQuerySchema rejects GET requests causing
valid UI filters to fail; update the method enum in getApiLogsQuerySchema (the
`method` property) to include "GET" (i.e., allow
"GET","POST","PATCH","PUT","DELETE") so the schema matches the UI, or
alternatively change the UI to stop emitting method=GET filters—prefer adding
"GET" to the z.enum for immediate compatibility.
In `@apps/web/ui/logs/use-log-filters.ts`:
- Around line 135-139: The onRemoveAll handler currently calls queryParams({
del: ["method","statusCode","routePattern","tokenId","requestType"] }) but does
not clear pagination; modify the onRemoveAll implementation (the function named
onRemoveAll that calls queryParams) to also remove the "page" param (e.g.,
include "page" in the del array or explicitly reset page) so it matches
onSelect/onRemove behavior and avoids leaving the user on an empty page.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 54178e86-b6b7-461e-af36-fbf6dcd200b3
📒 Files selected for processing (7)
apps/web/app/api/logs/count/route.tsapps/web/lib/api-logs/get-api-logs-count.tsapps/web/lib/api-logs/schemas.tsapps/web/lib/swr/use-api-logs-count.tsapps/web/lib/types.tsapps/web/ui/logs/logs-table.tsxapps/web/ui/logs/use-log-filters.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/lib/swr/use-api-logs-count.ts
- apps/web/lib/types.ts
|
@coderabbitai final review, any breaking changes? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainLet me investigate the key areas for potential breaking changes. 🏁 Script executed: Length of output: 610 🏁 Script executed: Length of output: 67953
✅ Safe Changes (non-breaking)
|
|
@coderabbitai please fully review the changes in stripe/integration/webhook – we need to make sure it's bulletproof. Anything that could break? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainLet me pull the actual file contents for a thorough review. 🏁 Script executed: Length of output: 34704
✅ What's safe
|
| Issue | Severity | Breaking? |
|---|---|---|
responseBody double-encoded as JSON string |
Low | No |
Full Stripe event as requestBody (size concern) |
Medium | No — fails gracefully |
No try/catch in waitUntil IIFE |
Low | No |
statusCode: 200 hardcoded |
Info | No |
| Exceptions in handlers not logged | Low | No |
🧠 Learnings used
Learnt from: steven-tey
Repo: dubinc/dub PR: 3679
File: apps/web/app/(ee)/api/discount-codes/route.ts:69-90
Timestamp: 2026-04-01T17:57:15.397Z
Learning: In the dubinc/dub codebase, a workspace can only ever have one installed Stripe integration at a time. Therefore, `prisma.installedIntegration.findFirst({ where: { projectId, integrationId: STRIPE_INTEGRATION_ID } })` is always deterministic and there is no risk of returning the wrong row. Do not flag this pattern as nondeterministic in future reviews.
Summary by CodeRabbit