Skip to content

Add date range filter to request logs, hide DELETE body - #3742

Merged
steven-tey merged 9 commits into
mainfrom
request-logs-2
Apr 13, 2026
Merged

Add date range filter to request logs, hide DELETE body#3742
steven-tey merged 9 commits into
mainfrom
request-logs-2

Conversation

@devkiran

@devkiran devkiran commented Apr 13, 2026

Copy link
Copy Markdown
Collaborator

Changes

  • Date Range Filtering: Added date range picker to the logs table, allowing users to filter logs by custom start/end dates or preset intervals (24h, 7d, 30d, 90d based on plan)
  • Request Body Visibility: Hide request body section for DELETE requests in log details
  • API Enhancements: Updated /api/logs and /api/logs/count endpoints to accept and process start, end, and interval query parameters
  • Retention Boundaries: Date range inputs are now clamped to the plan's data retention boundary to prevent out-of-range queries
  • UI/UX: Added SimpleDateRangePicker component to logs filters with dynamic preset options based on workspace plan retention days
  • Schema Updates: Extended query schemas to include date range parameters and updated SWR hook to track these new filters
CleanShot 2026-04-13 at 11  04 02@2x

Summary by CodeRabbit

  • New Features

    • Date range filtering for API logs with plan-aware retention presets and custom intervals
    • Date range picker added to the filter header with a from-date option
  • Bug Fixes / Improvements

    • Log queries and counts now honor explicit start/end/interval selections
    • Request body hidden for DELETE entries in log details

- Update getApiLogsDateRange to accept start/end/interval params with plan-based retention clamping
- Add date range picker to logs table filter bar with plan-appropriate presets
- Wire date params through API schemas, routes, and frontend query hooks
- Add fromDate support to SimpleDateRangePicker to disable dates outside retention
- Hide request body section on log detail page for DELETE requests
@vercel

vercel Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Apr 13, 2026 10:08am

Request Review

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Handlers, schemas, hooks, and UI now accept optional start, end, and interval. Date-range resolution calls getApiLogsDateRange({ plan, start?, end?, interval? }), which returns startDate/endDate clamped to retention; those returned values are used for queries and retention checks.

Changes

Cohort / File(s) Summary
API Routes
apps/web/app/api/logs/route.ts, apps/web/app/api/logs/count/route.ts, apps/web/app/api/logs/[logId]/route.ts
Handlers destructure start, end, interval from validated query, call getApiLogsDateRange({ plan, start, end, interval }), and use returned startDate/endDate for queries and retention checks.
Date Range Logic
apps/web/lib/api-logs/api-log-retention.ts
getApiLogsDateRange signature changed to accept { plan, start?, end?, interval? }, clamps/normalizes ranges to retention boundary, and now returns { startDate, endDate } instead of { start, end }.
Schemas & Constants
apps/web/lib/api-logs/schemas.ts, apps/web/lib/api-logs/constants.ts
Query schema adds optional start, end, interval; HTTP_MUTATION_METHODS is now derived from HTTP_METHODS; added API_LOGS_PRESETS_BY_RETENTION.
Hooks / SWR
apps/web/lib/swr/use-api-logs-count.ts, apps/web/ui/logs/use-log-filters.ts
Query-string builders and memo deps updated to include start, end, interval; onRemoveAll now clears these params.
UI Components
apps/web/ui/logs/logs-table.tsx, apps/web/ui/shared/simple-date-range-picker.tsx
LogsFilters receives plan, computes retentionDays and presets, and renders SimpleDateRangePicker; SimpleDateRangePicker accepts optional fromDate.
Log Details UI
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx
“Request body” block is now hidden for DELETE requests; response body unchanged.
Type/Fetch Signatures
apps/web/lib/api-logs/get-api-logs.ts, apps/web/lib/api-logs/get-api-logs-count.ts
Parameter types refined to explicitly re-declare start/end rather than inheriting them from schema types.
Minor UI/Formatting
apps/web/ui/partners/confirm-payouts-sheet.tsx, apps/web/ui/shared/inline-badge-popover.tsx, apps/web/lib/api/payouts/get-eligible-payouts.ts
Formatting/whitespace and small JSX class ordering tweaks with no behavioral changes.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Logs Dashboard
    participant Hook as useLogFilters / useApiLogsCount
    participant API as /api/logs Route
    participant Range as getApiLogsDateRange
    participant Service as getApiLogs / getApiLogsCount
    participant DB as Database

    User->>UI: Pick date range & filters (start,end,interval)
    UI->>Hook: Update search params
    Hook->>API: GET /api/logs?start=...&end=...&interval=...&filters...
    API->>Range: getApiLogsDateRange({plan, start, end, interval})
    Range-->>API: {startDate, endDate}
    API->>Service: getApiLogs({ start: startDate, end: endDate, filters, workspaceId })
    Service->>DB: Query logs within date range + filters
    DB-->>Service: Return results
    Service-->>API: Logs/count or list
    API-->>UI: JSON response
    UI-->>User: Render table / counts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Request logs #3719: Updates API log route handlers and call sites to the new getApiLogsDateRange signature and startDate/endDate keys.
  • Stacked usage charts #3150: Related changes around adding start/end/interval handling and date-range plumbing across UI and hooks.

Suggested reviewers

  • steven-tey
  • pepeladeira

Poem

🐰 I hopped through ranges, clipped the night,
start and end now bounded right.
Pickers hum and filters sing,
DELETE hides its silent thing,
Logs kept snug beneath retention light.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the two main changes: adding a date range filter to request logs and hiding DELETE request bodies in log details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch request-logs-2

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@devkiran
devkiran requested a review from steven-tey April 13, 2026 05:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 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/lib/api-logs/api-log-retention.ts`:
- Around line 7-17: The helper getApiLogsDateRange signature/return shape
changed but the [logId] route still calls getApiLogsDateRange(workspace.plan)
and destructures { start }, causing a mismatch; update the caller in the [logId]
route to call getApiLogsDateRange({ plan: workspace.plan, start?:..., end?:...,
interval?:... }) (or pass the appropriate properties you expect) and destructure
the returned shape that the function now returns, or alternatively restore a
backward-compatible overload in getApiLogsDateRange that accepts a PlanProps
single-arg and returns the previous shape so the existing destructuring of {
start } continues to work. Ensure you reference getApiLogsDateRange and the
[logId] route when making the change.
- Around line 29-36: The current logic only clamps startDate (clampedStart) to
retentionBoundary, which can yield startDate > endDate when endDate is also
older than retention; update the function to clamp both startDate and endDate
against retentionBoundary (e.g., compute clampedStart = max(startDate,
retentionBoundary) and clampedEnd = max(endDate, retentionBoundary)), then
ensure the returned range uses clampedStart and clampedEnd and is normalized so
clampedStart <= clampedEnd before calling formatUTCDateTimeClickhouse.

In `@apps/web/lib/api-logs/schemas.ts`:
- Around line 88-90: Tighten validation for the date-range schema fields by
replacing the loose z.string().optional() entries for start, end, and interval
with strict checks: ensure start and end are valid ISO/RFC date strings (use
z.string().refine or z.string().datetime to reject malformed dates) and validate
interval against the exact set of supported interval values used by
getIntervalData (use z.enum or z.union of literal values) so malformed dates or
unsupported intervals are rejected at the schema boundary (update the schema
symbols start, end, and interval accordingly).

In `@apps/web/ui/logs/logs-table.tsx`:
- Around line 360-376: The parent container currently forces a single-row layout
and causes the two full-width children (Filter.Select and SimpleDateRangePicker)
to clash on small screens; change the wrapper div's classes to use a responsive
column-to-row layout (e.g., replace "flex items-center gap-2" with "flex
flex-col md:flex-row items-start md:items-center gap-2") so the Filter.Select
and SimpleDateRangePicker stack vertically on mobile and return to a row on md+
screens while preserving existing "w-full md:w-fit" behavior on each component.
- Around line 372-374: The defaultInterval prop is hardcoded to "30d" which may
not exist for some plans; change the initialization in logs-table.tsx so
defaultInterval is derived from the available presets (use presets to select a
plan-aware default, e.g., prefer a configured plan default if present, otherwise
fall back to the first valid entry in presets) and ensure the chosen value is
valid before passing it to the DateRangePicker; keep the fromDate logic using
subDays(new Date(), retentionDays) as-is and guard against an empty presets
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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0dbec214-fa0d-4d72-a351-d9658c77e43d

📥 Commits

Reviewing files that changed from the base of the PR and between 6929fae and 3d47959.

📒 Files selected for processing (10)
  • apps/web/app/api/logs/count/route.ts
  • apps/web/app/api/logs/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx
  • apps/web/lib/api-logs/api-log-retention.ts
  • apps/web/lib/api-logs/constants.ts
  • apps/web/lib/api-logs/schemas.ts
  • apps/web/lib/swr/use-api-logs-count.ts
  • apps/web/ui/logs/logs-table.tsx
  • apps/web/ui/logs/use-log-filters.ts
  • apps/web/ui/shared/simple-date-range-picker.tsx

Comment thread apps/web/lib/api-logs/api-log-retention.ts
Comment thread apps/web/lib/api-logs/api-log-retention.ts
Comment thread apps/web/lib/api-logs/schemas.ts Outdated
Comment thread apps/web/ui/logs/logs-table.tsx Outdated
Comment thread apps/web/ui/logs/logs-table.tsx
@devkiran

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review please

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

I'll gather the file contents for a thorough review.


🏁 Script executed:

#!/bin/bash
echo "=== api-log-retention.ts ==="
fd "api-log-retention.ts" --exec cat {}

echo "=== constants.ts (api-logs) ==="
fd "constants.ts" --search-path apps/web/lib/api-logs --exec cat {}

echo "=== schemas.ts (api-logs) ==="
fd "schemas.ts" --search-path apps/web/lib/api-logs --exec cat {}

echo "=== logs/route.ts ==="
fd "route.ts" --search-path "apps/web/app/api/logs" --exec sh -c 'echo "FILE: $1"; cat "$1"' _ {}

echo "=== use-log-filters.ts ==="
fd "use-log-filters.ts" --exec cat {}

echo "=== use-api-logs-count.ts ==="
fd "use-api-logs-count.ts" --exec cat {}

echo "=== logs-table.tsx ==="
fd "logs-table.tsx" --exec cat {}

echo "=== simple-date-range-picker.tsx ==="
fd "simple-date-range-picker.tsx" --exec cat {}

echo "=== page-client.tsx (logId) ==="
fd "page-client.tsx" --search-path "apps/web/app/app.dub.co" --exec cat {}

Length of output: 264380


The PR description image shows the Logs page UI with a dual-month calendar date picker (April–May 2026) open, April 13 highlighted as the current date, and a quick-presets panel on the right listing "Last 24 hours", "Last 7 days", "Last 30 days", and "Last 3 months". The UI matches the described implementation well.


@devkiran Here is the full review for PR #3742:


Summary

The PR is well-structured and the core logic (date range clamping to plan retention, hiding DELETE request bodies, schema/hook extensions) is sound. A few issues are worth addressing before merge.


Issues Found

1. 🐛 Missing 60d preset for Business plan (60-day retention)

File: apps/web/lib/api-logs/constants.ts

export const API_LOGS_PRESETS_BY_RETENTION: Record<number, string[]> = {
  30: ["24h", "7d", "30d"],
  60: ["24h", "7d", "30d"],   // ← no "60d" preset
  90: ["24h", "7d", "30d", "90d"],
};

Business plan workspaces have 60-day retention but are offered the same presets as 30-day plans — there's no way for these users to select the full retention window via a preset shortcut. Consider adding "60d" and the corresponding interval value to the schema enum:

- 60: ["24h", "7d", "30d"],
+ 60: ["24h", "7d", "30d", "60d"],

And updating getApiLogsQuerySchema:

- interval: z.enum(["24h", "7d", "30d", "90d"]).optional(),
+ interval: z.enum(["24h", "7d", "30d", "60d", "90d"]).optional(),

2. 🐛 Zero-width date range when both dates are before the retention boundary

File: apps/web/lib/api-logs/api-log-retention.ts

const clampedStart = startDate < retentionBoundary ? retentionBoundary : startDate;
const clampedEnd   = endDate   < retentionBoundary ? retentionBoundary : endDate;   // ← also clamped to boundary

If a user manually passes a custom date range entirely older than the retention window (e.g., 2 years ago), both clampedStart and clampedEnd are set to retentionBoundary, producing a zero-width query window (startDate === endDate). This would return an empty result set with no explanation to the user.

Suggestion: Return the full retention window as a fallback, or detect this case and return an error:

if (clampedStart >= clampedEnd) {
  // Both dates are beyond the retention limit; return empty window
  // or fall back to the full retention range
  return {
    startDate: formatUTCDateTimeClickhouse(retentionBoundary),
    endDate: formatUTCDateTimeClickhouse(new Date()),
  };
}

3. ⚠️ Partial date input silently falls back to full retention window

File: apps/web/lib/api-logs/api-log-retention.ts

The condition if (interval || (start && end)) means that if only start is provided (without end), or only end is provided, the function silently falls back to the full retention window. While this is unlikely via normal UI, it's exploitable via URL manipulation (e.g., ?start=2026-01-01).

Suggestion: Enforce the constraint at the Zod schema level using .refine():

export const getApiLogsQuerySchema = z.object({ ... })
  .refine(
    ({ start, end }) => (start == null) === (end == null),
    { message: "`start` and `end` must both be provided or both omitted" }
  )
  .extend(getPaginationQuerySchema({ pageSize: API_LOGS_MAX_PAGE_SIZE }));

4. ⚠️ onRemoveAll doesn't clear date range parameters

File: apps/web/ui/logs/use-log-filters.ts

const onRemoveAll = useCallback(
  () =>
    queryParams({
      del: ["method", "statusCode", "routePattern", "tokenId", "requestType"],
      //   ↑ "start", "end", "interval" are missing
    }),
  [queryParams],
);

When a user clicks the "Remove all" button on the active filter list, the date range (start, end, interval) is not cleared. If this is intentional (treating date range as independent from the filter chips), it should be consistent with the UI affordance — currently there's no separate "clear date" button in the picker, so users have no one-click way to reset to the default interval.

If date range should reset alongside filters:

- del: ["method", "statusCode", "routePattern", "tokenId", "requestType"],
+ del: ["method", "statusCode", "routePattern", "tokenId", "requestType", "start", "end", "interval"],

5. 💡 Minor: interval=90d accepted by schema for 30-day retention workspaces

File: apps/web/lib/api-logs/schemas.ts

The Zod schema accepts interval: "90d" regardless of the workspace plan. Server-side clamping correctly limits the data returned, but the UI SimpleDateRangePicker will display the "Last 3 months" preset label while silently returning only 30 days of data — potentially misleading.

This is a cosmetic concern since the clamping is correct, but you may want to either surface a notice or filter out unavailable presets on the client (which you already partially do via API_LOGS_PRESETS_BY_RETENTION, but URL manipulation bypasses that).


Positive Observations ✅

  • The retention-boundary clamping and normalization logic (normalizedStart/normalizedEnd swap) correctly handles inverted ranges.
  • Hiding the request body for DELETE requests (log.method !== "DELETE") is clean and correctly placed in LogDetailContent.
  • The fromDate prop propagation to DateRangePicker ensures the calendar UI prevents selecting dates before the retention boundary — good UX.
  • The HTTP_MUTATION_METHODS derivation from HTTP_METHODS.filter(...) is a nice DRY improvement over the old hardcoded array.
  • SWR hooks correctly include all new date params in the query string dependency list.
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/ui/logs/logs-table.tsx (1)

369-375: Consider defining a stricter type for presets to avoid as any.

The presets as any cast works at runtime because SimpleDateRangePicker internally handles string arrays (per the component's filtering logic). However, the component's prop type expects preset objects, causing the mismatch. A dedicated type or updating SimpleDateRangePicker's type to accept string[] would improve type safety.

🤖 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 369 - 375, The current cast
presets as any hides a type mismatch between the presets value and the
SimpleDateRangePicker prop type; update the presets definition to a stricter
type (e.g., declare a Preset type matching the actual values you pass—string[]
or an array of {label: string, value: string} objects) and remove the as any
cast, or alternatively update SimpleDateRangePicker's prop type to accept
string[] if it truly only needs strings; adjust the variable named presets
and/or the SimpleDateRangePicker prop signature so the types align and the
explicit cast is no longer needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/ui/logs/logs-table.tsx`:
- Around line 369-375: The current cast presets as any hides a type mismatch
between the presets value and the SimpleDateRangePicker prop type; update the
presets definition to a stricter type (e.g., declare a Preset type matching the
actual values you pass—string[] or an array of {label: string, value: string}
objects) and remove the as any cast, or alternatively update
SimpleDateRangePicker's prop type to accept string[] if it truly only needs
strings; adjust the variable named presets and/or the SimpleDateRangePicker prop
signature so the types align and the explicit cast is no longer needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84d7445f-5830-4155-af0b-12ef3f4eef2c

📥 Commits

Reviewing files that changed from the base of the PR and between 0fdcc0a and 5da41c1.

📒 Files selected for processing (3)
  • apps/web/lib/api-logs/api-log-retention.ts
  • apps/web/lib/api-logs/schemas.ts
  • apps/web/ui/logs/logs-table.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/api-logs/schemas.ts

…reset dates on clear

- Add 60d/90d presets per retention tier and validate interval from presets\n- Use DEFAULT_RETENTION_DAYS for preset fallback in logs table\n- Clear start, end, and interval when removing all log filters
…on queries

- getApiLogsDateRange start/end accept string | Date | null\n- get-api-logs and get-api-logs-count params omit schema start/end and require resolved strings

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
apps/web/ui/logs/use-log-filters.ts (1)

135-141: onRemoveAll does not clear date range parameters.

The onRemoveAll callback clears filter params (method, statusCode, etc.) but omits start, end, and interval. If "Remove All" is intended to reset all filters, consider adding these keys. If the date range is meant to persist separately, this is fine as-is.

♻️ Optional: Include date params in onRemoveAll
 const onRemoveAll = useCallback(
   () =>
     queryParams({
-      del: ["method", "statusCode", "routePattern", "tokenId", "requestType"],
+      del: ["method", "statusCode", "routePattern", "tokenId", "requestType", "start", "end", "interval"],
     }),
   [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 - 141, The onRemoveAll
callback currently calls queryParams with del set to
["method","statusCode","routePattern","tokenId","requestType"] but omits date
range keys; update the del array in onRemoveAll (the function using queryParams)
to also include "start", "end", and "interval" so the date range is cleared when
Remove All is invoked (unless you intentionally want date range persistence).
apps/web/lib/api-logs/api-log-retention.ts (1)

22-22: Partial date inputs silently fall back to full retention window.

When only start or only end is provided (but not both), the condition (start && end) is false, so the function returns the default full retention window. While the UI always provides both or neither, this could be surprising if the API is called directly with partial params.

Consider documenting this behavior or enforcing both-or-none at the schema level with a .refine().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/lib/api-logs/api-log-retention.ts` at line 22, The condition if
(interval || (start && end)) silently treats partial date inputs as no-dates;
update validation to enforce both-or-none for start and end: in
apps/web/lib/api-logs/api-log-retention.ts, add a guard that detects a partial
date pair (start && !end) || (!start && end) and return/throw a validation error
(or return a 400) instead of falling back to full retention, or enforce this at
the input schema using a .refine() rule that requires both start and end
together.
apps/web/lib/api-logs/constants.ts (1)

85-89: Consider whether 60-day plans should have a "60d" preset option.

Business plans have 60-day retention but the presets for 60 only include ["24h", "7d", "30d"]. Users on these plans can still select custom date ranges spanning their full retention, but might expect a "Last 60 days" preset. If this is intentional (to keep the UI simpler), this is fine. Otherwise, add "60d" to both the presets and the schema enum.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/lib/api-logs/constants.ts` around lines 85 - 89,
API_LOGS_PRESETS_BY_RETENTION currently omits a "60d" preset for 60-day
retention plans; update the presets to include "60d" for the key 60 (i.e., add
"60d" to the array in API_LOGS_PRESETS_BY_RETENTION) and also add "60d" to the
corresponding schema enum that validates preset values so the new preset is
accepted by validation (update the enum where preset strings like
"24h","7d","30d","90d" are defined).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/lib/api-logs/api-log-retention.ts`:
- Line 22: The condition if (interval || (start && end)) silently treats partial
date inputs as no-dates; update validation to enforce both-or-none for start and
end: in apps/web/lib/api-logs/api-log-retention.ts, add a guard that detects a
partial date pair (start && !end) || (!start && end) and return/throw a
validation error (or return a 400) instead of falling back to full retention, or
enforce this at the input schema using a .refine() rule that requires both start
and end together.

In `@apps/web/lib/api-logs/constants.ts`:
- Around line 85-89: API_LOGS_PRESETS_BY_RETENTION currently omits a "60d"
preset for 60-day retention plans; update the presets to include "60d" for the
key 60 (i.e., add "60d" to the array in API_LOGS_PRESETS_BY_RETENTION) and also
add "60d" to the corresponding schema enum that validates preset values so the
new preset is accepted by validation (update the enum where preset strings like
"24h","7d","30d","90d" are defined).

In `@apps/web/ui/logs/use-log-filters.ts`:
- Around line 135-141: The onRemoveAll callback currently calls queryParams with
del set to ["method","statusCode","routePattern","tokenId","requestType"] but
omits date range keys; update the del array in onRemoveAll (the function using
queryParams) to also include "start", "end", and "interval" so the date range is
cleared when Remove All is invoked (unless you intentionally want date range
persistence).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f0b7e6f-3edc-48df-9776-689a561b0d36

📥 Commits

Reviewing files that changed from the base of the PR and between 6929fae and 5da41c1.

📒 Files selected for processing (11)
  • apps/web/app/api/logs/[logId]/route.ts
  • apps/web/app/api/logs/count/route.ts
  • apps/web/app/api/logs/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx
  • apps/web/lib/api-logs/api-log-retention.ts
  • apps/web/lib/api-logs/constants.ts
  • apps/web/lib/api-logs/schemas.ts
  • apps/web/lib/swr/use-api-logs-count.ts
  • apps/web/ui/logs/logs-table.tsx
  • apps/web/ui/logs/use-log-filters.ts
  • apps/web/ui/shared/simple-date-range-picker.tsx

@devkiran

Copy link
Copy Markdown
Collaborator Author

@coderabbitai made some changes mentioned in review, please do another full review

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

I'll fetch all the relevant files to check what's been updated.


🏁 Script executed:

#!/bin/bash
echo "=== api-log-retention.ts ==="
fd "api-log-retention.ts" --exec cat {}

echo ""
echo "=== constants.ts (api-logs) ==="
fd "constants.ts" --search-path apps/web/lib/api-logs --exec cat {}

echo ""
echo "=== schemas.ts (api-logs) ==="
fd "schemas.ts" --search-path apps/web/lib/api-logs --exec cat {}

echo ""
echo "=== use-log-filters.ts ==="
fd "use-log-filters.ts" --exec cat {}

echo ""
echo "=== use-api-logs-count.ts ==="
fd "use-api-logs-count.ts" --exec cat {}

echo ""
echo "=== logs-table.tsx ==="
fd "logs-table.tsx" --exec cat {}

echo ""
echo "=== simple-date-range-picker.tsx ==="
fd "simple-date-range-picker.tsx" --exec cat {}

echo ""
echo "=== logs/route.ts ==="
fd "route.ts" --search-path "apps/web/app/api/logs" --exec sh -c 'echo "FILE: $1"; cat "$1"' _ {}

echo ""
echo "=== page-client.tsx (logId) ==="
fd "page-client.tsx" --search-path "apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs" --exec cat {}

Length of output: 39249


The PR description image shows the Logs dashboard with the SimpleDateRangePicker open, displaying a dual-month calendar (April–May 2026) with April 13 selected and a right-side panel listing Last 24 hours, Last 7 days, Last 30 days, and Last 3 months presets.


@devkiran Great progress! Issues #1 (missing 60d preset) and #4 (onRemoveAll not clearing date params) are fully resolved. Here's the updated state of the remaining items:


✅ Fixed

Issue #160d preset for Business plan

API_LOGS_PRESETS_BY_RETENTION now correctly has:

60: ["24h", "7d", "30d", "60d"],
90: ["24h", "7d", "30d", "60d", "90d"],

And the schema uses API_LOGS_PRESETS_BY_RETENTION[90] as the superset enum — a clean approach.

Issue #4onRemoveAll now clears date range

del: ["method", "statusCode", "routePattern", "tokenId", "requestType", "start", "end", "interval"],

✅ All three date params are now included.


Still Outstanding

🐛 Issue #2 — Zero-width date range (unchanged)

File: apps/web/lib/api-logs/api-log-retention.ts

The clamping logic is unchanged:

const clampedStart = startDate < retentionBoundary ? retentionBoundary : startDate;
const clampedEnd   = endDate   < retentionBoundary ? retentionBoundary : endDate;

If a user passes dates entirely outside the retention window (e.g., both 2 years ago), both values are clamped to retentionBoundary, yielding startDate === endDate — a zero-width query returning no results with no feedback. Suggested fix:

const clampedStart = startDate < retentionBoundary ? retentionBoundary : startDate;
const clampedEnd   = endDate   < retentionBoundary ? retentionBoundary : endDate;

// If clamping collapsed the range, fall back to the full retention window
if (clampedStart >= clampedEnd) {
  return {
    startDate: formatUTCDateTimeClickhouse(retentionBoundary),
    endDate: formatUTCDateTimeClickhouse(new Date()),
  };
}

⚠️ Issue #3 — Partial date input silently falls back (unchanged)

File: apps/web/lib/api-logs/api-log-retention.ts + schemas.ts

if (interval || (start && end)) means ?start=2026-01-01 alone still silently returns the full retention window. Adding a Zod .refine() to getApiLogsQuerySchema would prevent this at the API boundary:

export const getApiLogsQuerySchema = z
  .object({ ... })
  .refine(
    ({ start, end }) => (start == null) === (end == null),
    { message: "`start` and `end` must both be provided or both omitted" },
  )
  .extend(getPaginationQuerySchema({ pageSize: API_LOGS_MAX_PAGE_SIZE }));

New Observation

💡 z.enum(API_LOGS_PRESETS_BY_RETENTION[90]) — potential type issue

File: apps/web/lib/api-logs/schemas.ts

interval: z.enum(API_LOGS_PRESETS_BY_RETENTION[90]).optional(),

API_LOGS_PRESETS_BY_RETENTION is typed as Record<number, string[]>, so [90] resolves to string[] (mutable, not a const tuple). In Zod v4, z.enum accepts string[] at runtime, but TypeScript may infer a loose z.ZodEnum<string[]> type rather than a tight literal union. This is a minor type-safety gap — if you want strict inference, you can cast:

interval: z.enum(API_LOGS_PRESETS_BY_RETENTION[90] as [string, ...string[]]).optional(),

Or define the 90-day list as a const:

const ALL_LOG_INTERVALS = ["24h", "7d", "30d", "60d", "90d"] as const;
interval: z.enum(ALL_LOG_INTERVALS).optional(),

Everything Else ✅

Area Status
DELETE body hidden (log.method !== "DELETE") ✅ Correct
Retention clamping + normalization (inverted ranges) ✅ Correct
fromDate propagation to DateRangePicker ✅ Correct
plan flowing from useWorkspace()LogsFilters → presets/fromDate ✅ Correct
SWR hooks include start/end/interval ✅ Correct
[logId]/route.ts retention check uses startDate key ✅ Correct
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/ui/logs/logs-table.tsx (1)

374-374: Remove the as any cast by properly typing the presets constant.

presets={presets as any} bypasses compile-time type checking. API_LOGS_PRESETS_BY_RETENTION is typed as Record<number, string[]>, but SimpleDateRangePicker expects presets?: (typeof DATE_RANGE_INTERVAL_PRESETS)[number][]. Either type API_LOGS_PRESETS_BY_RETENTION with the specific preset union type (instead of generic string[]), or explicitly type the presets variable in logs-table.tsx to match the picker's expected type.

🤖 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` at line 374, The prop is using an any cast;
fix by giving the presets the exact union type expected by
SimpleDateRangePicker: change the type of API_LOGS_PRESETS_BY_RETENTION (or the
local presets variable in logs-table.tsx) from string[] to (typeof
DATE_RANGE_INTERVAL_PRESETS)[number][], e.g. make API_LOGS_PRESETS_BY_RETENTION:
Record<number, (typeof DATE_RANGE_INTERVAL_PRESETS)[number][]> or declare const
presets: (typeof DATE_RANGE_INTERVAL_PRESETS)[number][] =
API_LOGS_PRESETS_BY_RETENTION[retention]; then remove the "as any" on presets
when passing to SimpleDateRangePicker.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/ui/logs/logs-table.tsx`:
- Line 374: The prop is using an any cast; fix by giving the presets the exact
union type expected by SimpleDateRangePicker: change the type of
API_LOGS_PRESETS_BY_RETENTION (or the local presets variable in logs-table.tsx)
from string[] to (typeof DATE_RANGE_INTERVAL_PRESETS)[number][], e.g. make
API_LOGS_PRESETS_BY_RETENTION: Record<number, (typeof
DATE_RANGE_INTERVAL_PRESETS)[number][]> or declare const presets: (typeof
DATE_RANGE_INTERVAL_PRESETS)[number][] =
API_LOGS_PRESETS_BY_RETENTION[retention]; then remove the "as any" on presets
when passing to SimpleDateRangePicker.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db5ea65a-1fac-462f-aa80-bab1b93247bc

📥 Commits

Reviewing files that changed from the base of the PR and between 5da41c1 and 8f9b2ef.

📒 Files selected for processing (10)
  • apps/web/lib/api-logs/api-log-retention.ts
  • apps/web/lib/api-logs/constants.ts
  • apps/web/lib/api-logs/get-api-logs-count.ts
  • apps/web/lib/api-logs/get-api-logs.ts
  • apps/web/lib/api-logs/schemas.ts
  • apps/web/lib/api/payouts/get-eligible-payouts.ts
  • apps/web/ui/logs/logs-table.tsx
  • apps/web/ui/logs/use-log-filters.ts
  • apps/web/ui/partners/confirm-payouts-sheet.tsx
  • apps/web/ui/shared/inline-badge-popover.tsx
✅ Files skipped from review due to trivial changes (4)
  • apps/web/lib/api-logs/get-api-logs-count.ts
  • apps/web/ui/shared/inline-badge-popover.tsx
  • apps/web/lib/api/payouts/get-eligible-payouts.ts
  • apps/web/ui/partners/confirm-payouts-sheet.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/ui/logs/use-log-filters.ts
  • apps/web/lib/api-logs/schemas.ts
  • apps/web/lib/api-logs/constants.ts
  • apps/web/lib/api-logs/api-log-retention.ts

@steven-tey
steven-tey merged commit d772304 into main Apr 13, 2026
11 checks passed
@steven-tey
steven-tey deleted the request-logs-2 branch April 13, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants