Skip to content

Improve project reset#581

Open
Blaumaus wants to merge 5 commits into
mainfrom
feature/project-reset
Open

Improve project reset#581
Blaumaus wants to merge 5 commits into
mainfrom
feature/project-reset

Conversation

@Blaumaus

@Blaumaus Blaumaus commented Jun 25, 2026

Copy link
Copy Markdown
Member

Changes

If applicable, please describe what changes were made in this pull request.

Community Edition support

  • Your feature is implemented for the Swetrix Community Edition
  • This PR only updates the Cloud (Enterprise) Edition code (e.g. Paddle webhooks, blog, payouts, etc.)

Database migrations

  • Clickhouse / MySQL migrations added for this PR
  • No table schemas changed in this PR

Documentation

  • You have updated the documentation according to your PR
  • This PR did not change any publicly documented endpoints

Summary by CodeRabbit

  • New Features
    • Added “delete project data” with a preview step (sparkline timeline + per-event counts) and confirm-before-delete flow.
    • Users can choose date ranges and matching filters, then select which event types to delete.
  • Bug Fixes
    • Improved authorization and validation for deletion/preview requests.
    • Enhanced tab error handling by reliably resetting error boundaries when query state changes (and added render error tracking).
  • UI/UX
    • Replaced the previous reset/delete modal with a dedicated Delete Data modal.
    • Updated project settings copy and related locale strings.

@Blaumaus Blaumaus self-assigned this Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 02673bf5-fd93-428b-b965-a1e4729afa91

📥 Commits

Reviewing files that changed from the base of the PR and between 7470041 and f550463.

📒 Files selected for processing (7)
  • web/app/pages/Project/tabs/Errors/ErrorsView.tsx
  • web/app/pages/Project/tabs/Funnels/FunnelsView.tsx
  • web/app/pages/Project/tabs/Performance/PerformanceView.tsx
  • web/app/pages/Project/tabs/Replays/ReplaysView.tsx
  • web/app/pages/Project/tabs/SEO/SEOView.tsx
  • web/app/pages/Project/tabs/Sessions/SessionsView.tsx
  • web/app/pages/Project/tabs/Traffic/TrafficView.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/app/pages/Project/tabs/Sessions/SessionsView.tsx

📝 Walkthrough

Walkthrough

Analytics data-deletion preview and delete paths were added across backend and web app. Project settings now uses a dedicated delete-data modal, with updated proxy routing, action handling, and locale strings. The shared tab error boundary now resets on key changes and logs caught errors.

Changes

Analytics Deletion Flow

Layer / File(s) Summary
Deletion contracts and backend endpoints
backend/apps/cloud/src/analytics/..., backend/apps/community/src/analytics/...
The analytics DTOs define allowed event types and request fields, and both backend controllers expose preview and delete POST endpoints that authorize by project id.
Preview service and proxy wiring
backend/apps/cloud/src/analytics/analytics.service.ts, backend/apps/community/src/analytics/analytics.service.ts, web/app/api/api.server.ts, web/app/routes/api.analytics.ts, web/app/hooks/useAnalyticsProxy.ts
The analytics services add deletion preview counting, date-range normalization, timeline bucketing, and a preview API client plus proxy route for fetching preview data.
Delete modal and input support
web/app/providers/CurrentProjectProvider.tsx, web/app/pages/Project/View/components/FilterRowsEditor.tsx, web/app/pages/Project/Settings/components/DeleteDataModal.tsx
The delete modal computes ranges, fetches previews, renders the timeline, and the filter editor can derive its project id outside the current project provider.
Settings integration and copy
web/app/pages/Project/Settings/ProjectSettings.tsx, web/app/pages/Project/Settings/tabs/DangerZone.tsx, web/app/routes/projects.settings.$id.tsx, web/public/locales/en.json
Project settings replace the inline reset modal with DeleteDataModal, the settings action now posts delete-data, DangerZone uses the updated reset state prop, and related locale strings are renamed or removed.

Shared Tab Error Boundary

Layer / File(s) Summary
Boundary component
web/app/pages/Project/View/components/TabErrorBoundary.tsx
The tab error boundary stores the reset key in state, clears errors when the tracked inputs change, and reports caught errors through analytics tracking.
Tab reset wiring
web/app/pages/Project/tabs/Errors/ErrorsView.tsx, web/app/pages/Project/tabs/Funnels/FunnelsView.tsx, web/app/pages/Project/tabs/Performance/PerformanceView.tsx, web/app/pages/Project/tabs/Replays/ReplaysView.tsx, web/app/pages/Project/tabs/SEO/SEOView.tsx, web/app/pages/Project/tabs/Sessions/SessionsView.tsx, web/app/pages/Project/tabs/Traffic/TrafficView.tsx
The tab wrappers compute reset keys from search parameters and refresh triggers, and pass them into the shared error boundary.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through previews, soft and keen,
and nibbled paths both red and green.
New burrows bloom in tidy light,
with error ears held high tonight.
Hop, hop—data drifts away just right.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only repeats the template and omits any actual change summary or checklist details. Add a brief Changes summary and complete the Community Edition, database migrations, and Documentation sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly refers to the project reset-related changes, though it is broad.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 feature/project-reset

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
web/app/pages/Project/View/components/TabErrorBoundary.tsx (1)

42-53: 📐 Maintainability & Code Quality | 🔵 Trivial

Forward caught errors to your monitoring pipeline.

Although React 19 logs caught errors to console.error by default, these are not automatically forwarded to production error tracking services. To ensure tab-load failures are observable in your telemetry, implement componentDidCatch(error, info) to send error and component stack details to your monitoring provider.

Code example
// Add alongside getDerivedStateFromError
componentDidCatch(error: Error, info: React.ErrorInfo) {
  // Example: logErrorToMonitoring(error, info)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx` around lines 42 -
53, The TabErrorBoundary currently only sets error state via
getDerivedStateFromError, so caught tab-load failures are not forwarded to
monitoring. Add componentDidCatch(error, info) to TabErrorBoundary and send the
error plus React.ErrorInfo component stack to your telemetry/monitoring provider
alongside the existing TabErrorBoundary state handling.
backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts (1)

48-57: 🔒 Security & Privacy | 🔵 Trivial

Recommended: Add @IsIn constraint for types

The service currently filters invalid types and throws a generic error if none remain, preventing unsafe queries, but DTO-level validation offers better developer experience by rejecting invalid inputs immediately.

  `@ApiProperty`({
    required: false,
    type: [String],
    description:
      "Event types to delete. Any of: 'pageview', 'custom_event', 'error', 'performance', 'captcha'.",
  })
  `@IsOptional`()
  `@IsArray`()
+  `@IsString`({ each: true })
+  `@IsIn`(['pageview', 'custom_event', 'error', 'performance', 'captcha'], { each: true })
  types?: string[]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts` around lines 48 -
57, Add DTO-level validation for the data-deletion request types. In
data-deletion.dto.ts, update the DataDeletionDto.types field to validate each
entry against the allowed event names using an `@IsIn` constraint alongside the
existing `@IsOptional`, `@IsArray`, and `@IsString` decorators. Keep the allowed
values aligned with the service’s accepted types so invalid inputs are rejected
during validation instead of only being filtered later.
backend/apps/cloud/src/analytics/analytics.service.ts (1)

1826-1846: 🩺 Stability & Availability | 🔵 Trivial

Reset completes asynchronously — consider waiting for the mutation.

clickhouse.command(ALTER TABLE ... DELETE ...) submits an async mutation and resolves immediately, so this method returns (and the UI reports success) while rows are still being removed in the background. deleteSessionReplay uses clickhouse_settings: { mutations_sync: '2' } for exactly this reason. If a user expects the project to be empty right after a reset, consider adding mutations_sync here (accepting the longer request time) or surfacing that deletion is in progress.

Please confirm the intended UX: is eventual (background) deletion acceptable for this reset flow, or should the request block until the mutation finishes?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/cloud/src/analytics/analytics.service.ts` around lines 1826 -
1846, The reset flow in analytics.service’s delete/reset mutation path is still
asynchronous because the clickhouse.command ALTER TABLE DELETE calls return
before the mutation finishes, so the method can report success too early. Update
the command execution in this reset logic to wait for mutation completion the
same way deleteSessionReplay does, by using the appropriate clickhouse settings
or equivalent synchronous mutation handling on the clickhouse.command calls. If
background deletion is intended instead, make that behavior explicit in the UX;
otherwise block until both the events and error_statuses deletions have
finished.
backend/apps/community/src/analytics/dto/data-deletion.dto.ts (1)

31-57: 🩺 Stability & Availability | 🔵 Trivial

Replace @IsString with stricter validation for from/to and constrain types

Currently from and to only validate string presence, allowing "Invalid Date" to propagate to dayjs and trigger a server error. Update these to use @IsDateString combined with @Length(10, 10) to ensure only strict YYYY-MM-DD formats are accepted, preventing malformed dates from reaching the service layer. Additionally, add a constraint (e.g., @IsIn) on types to reject unsupported event names before they hit the allowlist logic.

import { IsDateString, IsEnum, IsIn, Length } from 'class-validator';

// ...

`@ApiProperty`({ ... }) // existing config
`@IsOptional`()
`@IsDateString`() // Accepts ISO 8601
`@Length`(10, 10) // Enforces YYYY-MM-DD strictly
from?: string;

`@ApiProperty`({ ... }) // existing config
`@IsOptional`()
`@IsDateString`()
`@Length`(10, 10)
to?: string;

`@ApiProperty`({ ... }) // existing config
`@IsOptional`()
`@IsArray`()
`@IsString`({ each: true })
`@IsIn`(['pageview', 'custom_event', 'error', 'performance', 'captcha'], { each: true })
types?: string[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/community/src/analytics/dto/data-deletion.dto.ts` around lines
31 - 57, The DataDeletionDto validation is too permissive for the date range and
event types, letting malformed inputs reach the service layer. In the
DataDeletionDto class, replace the loose `from`/`to` string checks with stricter
date validation using `IsDateString` plus `Length(10, 10)` so only `YYYY-MM-DD`
values are accepted, and keep them optional. Also tighten `types` in the same
DTO by adding an allowlist constraint such as `IsIn` with the supported event
names, alongside the existing array validation, so unsupported values are
rejected before the allowlist logic runs.
backend/apps/community/src/analytics/analytics.controller.ts (1)

1147-1165: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: add a deletion log entry for parity with cloud.

The cloud deleteData logs { uid, pid, types } before deleting, which is useful for auditing destructive actions. The community variant performs the same deletion without any log line.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/community/src/analytics/analytics.controller.ts` around lines
1147 - 1165, The community AnalyticsController.deleteData flow currently
performs the deletion without an audit log, unlike the cloud variant. Add a log
entry in deleteData before calling analyticsService.deleteData, using the same
relevant fields for parity and auditing (uid, pid, types), and place it near the
existing checkManageAccess/deleteData calls so the intent is easy to find even
if lines move.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/apps/cloud/src/analytics/analytics.service.ts`:
- Around line 1807-1824: The date-deletion flow in analytics.service.ts drops a
one-sided range because hasRange only checks for both from and to, so a partial
input can remove the created bound entirely. Update the date handling around the
deletion/preview logic to either reject half-specified ranges up front or
convert them into an open-ended range, and make sure the code path using
dateCondition, params.from/params.to, and getDataDeletionPreview stays
consistent so deletion never runs unscoped.

In `@web/app/pages/Project/Settings/ProjectSettings.tsx`:
- Line 1546: The <DangerZone /> prop named setResetting is misleading because it
is used as a boolean state value, not a setter function. Rename the prop to
isResetting in DangerZoneProps and update the <DangerZone /> usage in
ProjectSettings so the boolean state is passed and consumed consistently,
including the loading and click-guard logic tied to that prop.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx`:
- Around line 51-61: The TabErrorBoundary currently sets hasError to true in
getDerivedStateFromError but never clears it, so the fallback can get stuck
after recoverable updates. Update TabErrorBoundary to reset its error state when
the wrapped content changes, using a caller-provided resetKey or an equivalent
change-detection path tied to the children/titleKey props. Make the reset logic
live in TabErrorBoundary so render can return the fresh children again after the
underlying tab data changes.

---

Nitpick comments:
In `@backend/apps/cloud/src/analytics/analytics.service.ts`:
- Around line 1826-1846: The reset flow in analytics.service’s delete/reset
mutation path is still asynchronous because the clickhouse.command ALTER TABLE
DELETE calls return before the mutation finishes, so the method can report
success too early. Update the command execution in this reset logic to wait for
mutation completion the same way deleteSessionReplay does, by using the
appropriate clickhouse settings or equivalent synchronous mutation handling on
the clickhouse.command calls. If background deletion is intended instead, make
that behavior explicit in the UX; otherwise block until both the events and
error_statuses deletions have finished.

In `@backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts`:
- Around line 48-57: Add DTO-level validation for the data-deletion request
types. In data-deletion.dto.ts, update the DataDeletionDto.types field to
validate each entry against the allowed event names using an `@IsIn` constraint
alongside the existing `@IsOptional`, `@IsArray`, and `@IsString` decorators. Keep the
allowed values aligned with the service’s accepted types so invalid inputs are
rejected during validation instead of only being filtered later.

In `@backend/apps/community/src/analytics/analytics.controller.ts`:
- Around line 1147-1165: The community AnalyticsController.deleteData flow
currently performs the deletion without an audit log, unlike the cloud variant.
Add a log entry in deleteData before calling analyticsService.deleteData, using
the same relevant fields for parity and auditing (uid, pid, types), and place it
near the existing checkManageAccess/deleteData calls so the intent is easy to
find even if lines move.

In `@backend/apps/community/src/analytics/dto/data-deletion.dto.ts`:
- Around line 31-57: The DataDeletionDto validation is too permissive for the
date range and event types, letting malformed inputs reach the service layer. In
the DataDeletionDto class, replace the loose `from`/`to` string checks with
stricter date validation using `IsDateString` plus `Length(10, 10)` so only
`YYYY-MM-DD` values are accepted, and keep them optional. Also tighten `types`
in the same DTO by adding an allowlist constraint such as `IsIn` with the
supported event names, alongside the existing array validation, so unsupported
values are rejected before the allowlist logic runs.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx`:
- Around line 42-53: The TabErrorBoundary currently only sets error state via
getDerivedStateFromError, so caught tab-load failures are not forwarded to
monitoring. Add componentDidCatch(error, info) to TabErrorBoundary and send the
error plus React.ErrorInfo component stack to your telemetry/monitoring provider
alongside the existing TabErrorBoundary state handling.
🪄 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: be42afc5-7f50-4163-91f3-7a3c4bd2df97

📥 Commits

Reviewing files that changed from the base of the PR and between 96e6659 and 76cd59e.

📒 Files selected for processing (23)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/dto/data-deletion.dto.ts
  • web/app/api/api.server.ts
  • web/app/hooks/useAnalyticsProxy.ts
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • web/app/pages/Project/Settings/components/DeleteDataModal.tsx
  • web/app/pages/Project/View/components/FilterRowsEditor.tsx
  • web/app/pages/Project/View/components/TabErrorBoundary.tsx
  • web/app/pages/Project/tabs/Errors/ErrorsView.tsx
  • web/app/pages/Project/tabs/Funnels/FunnelsView.tsx
  • web/app/pages/Project/tabs/Performance/PerformanceView.tsx
  • web/app/pages/Project/tabs/Replays/ReplaysView.tsx
  • web/app/pages/Project/tabs/SEO/SEOView.tsx
  • web/app/pages/Project/tabs/Sessions/SessionsView.tsx
  • web/app/pages/Project/tabs/Traffic/TrafficView.tsx
  • web/app/providers/CurrentProjectProvider.tsx
  • web/app/routes/api.analytics.ts
  • web/app/routes/projects.settings.$id.tsx
  • web/public/locales/en.json

Comment thread backend/apps/cloud/src/analytics/analytics.service.ts Outdated
Comment thread web/app/pages/Project/Settings/ProjectSettings.tsx Outdated
Comment thread web/app/pages/Project/View/components/TabErrorBoundary.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts`:
- Around line 66-70: The DataDeletionDto currently leaves types optional, which
allows POST /data-deletion to silently fall back to a partial delete set. Update
the DTO and the controller/service flow so the destructive path explicitly
requires a delete-type selection, or split the request shapes into separate
preview and delete DTOs. Make sure DataDeletionDto and the handler that applies
the default types no longer permit omission to trigger an incomplete reset.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx`:
- Around line 65-83: The reset logic in
TabErrorBoundary.getDerivedStateFromProps is too broad because it uses children
and titleKey as reset signals. Update the reset condition so the boundary only
clears hasError when resetKey changes, and stop storing/comparing children and
titleKey in state for recovery. Keep the fallback using this.props.titleKey, and
preserve the existing resetKey-based state update behavior.
🪄 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: d2435d5f-58c1-4cd2-8eb2-d48c72bb17ce

📥 Commits

Reviewing files that changed from the base of the PR and between 76cd59e and cdbab36.

📒 Files selected for processing (8)
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/dto/data-deletion.dto.ts
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • web/app/pages/Project/Settings/tabs/DangerZone.tsx
  • web/app/pages/Project/View/components/TabErrorBoundary.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend/apps/community/src/analytics/dto/data-deletion.dto.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts

Comment thread backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts Outdated
Comment thread web/app/pages/Project/View/components/TabErrorBoundary.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/app/pages/Project/View/components/TabErrorBoundary.tsx (1)

32-32: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make resetKey mandatory or wire it from the tab wrappers.

This recovery path is inert for the current callers shown in context: web/app/pages/Project/tabs/Performance/PerformanceView.tsx:121 and web/app/pages/Project/tabs/Traffic/TrafficView.tsx:218 still render <TabErrorBoundary ...> without resetKey. Once hasError flips to true, both props.resetKey and state.resetKey stay undefined, so Line 65 keeps returning null and the tab remains stuck on the fallback until a full reload.

Suggested contract change in this file
 interface TabErrorBoundaryProps {
   children: ReactNode
   /** Translation key for the title, e.g. `dashboard.failedToLoadTraffic`. Falls back to a generic title. */
   titleKey?: string
-  resetKey?: unknown
+  resetKey: unknown
 }

Also applies to: 61-72

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx` at line 32, The
TabErrorBoundary recovery path is inert because resetKey is optional and the
current callers never pass it, so the fallback can stay stuck once hasError is
set. Make resetKey required in TabErrorBoundary and wire it through the tab
wrappers (such as PerformanceView and TrafficView) by passing a stable
tab-specific key into the boundary so the reset logic in the component can
actually clear the error state. Ensure the boundary’s reset handling uses that
prop consistently in TabErrorBoundary.
backend/apps/cloud/src/analytics/analytics.controller.ts (1)

2887-2924: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Throttle data-deletion preview and delete requests.

These routes reach expensive ClickHouse preview queries and synchronous ALTER TABLE ... DELETE mutations. Add per-user/per-project checkRateLimit calls before invoking the service to avoid mutation/query floods from a manager account or compromised token.

Suggested shape
 async getDataDeletionPreview(
   `@Body`() body: DataDeletionPreviewDto,
   `@CurrentUserId`() uid: string,
+  `@Ip`() requestIp: string,
 ) {
+  await checkRateLimit(uid || requestIp || body.pid, 'data-deletion-preview-user', 30, 60)
+  await checkRateLimit(body.pid, 'data-deletion-preview-project', 60, 60)
   await this.analyticsService.checkManageAccess(body.pid, uid)

 async deleteData(
   `@Body`() body: DataDeletionDto,
   `@CurrentUserId`() uid: string,
+  `@Ip`() requestIp: string,
 ) {
+  await checkRateLimit(uid || requestIp || body.pid, 'data-deletion-user', 3, 3600)
+  await checkRateLimit(body.pid, 'data-deletion-project', 10, 3600)
   await this.analyticsService.checkManageAccess(body.pid, uid)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/cloud/src/analytics/analytics.controller.ts` around lines 2887 -
2924, Throttle the expensive data-deletion endpoints by adding
per-user/per-project rate limiting before the service calls in
getDataDeletionPreview and deleteData. Use the existing
analyticsService.checkRateLimit (or the same rate-limit helper used elsewhere in
this controller) with uid and body.pid so both the ClickHouse preview query and
the synchronous delete mutation are protected. Place the check after
checkManageAccess and before getDataDeletionPreview/deleteData, and keep the
rest of the controller flow unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/apps/cloud/src/analytics/analytics.controller.ts`:
- Around line 2887-2924: Throttle the expensive data-deletion endpoints by
adding per-user/per-project rate limiting before the service calls in
getDataDeletionPreview and deleteData. Use the existing
analyticsService.checkRateLimit (or the same rate-limit helper used elsewhere in
this controller) with uid and body.pid so both the ClickHouse preview query and
the synchronous delete mutation are protected. Place the check after
checkManageAccess and before getDataDeletionPreview/deleteData, and keep the
rest of the controller flow unchanged.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx`:
- Line 32: The TabErrorBoundary recovery path is inert because resetKey is
optional and the current callers never pass it, so the fallback can stay stuck
once hasError is set. Make resetKey required in TabErrorBoundary and wire it
through the tab wrappers (such as PerformanceView and TrafficView) by passing a
stable tab-specific key into the boundary so the reset logic in the component
can actually clear the error state. Ensure the boundary’s reset handling uses
that prop consistently in TabErrorBoundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a460785-04d5-4d98-a374-028db207bf7f

📥 Commits

Reviewing files that changed from the base of the PR and between cdbab36 and 637eade.

📒 Files selected for processing (3)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/dto/data-deletion.dto.ts
  • web/app/pages/Project/View/components/TabErrorBoundary.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/app/pages/Project/View/components/TabErrorBoundary.tsx (1)

61-73: 🩺 Stability & Availability | 🟠 Major

Wire resetKey to tab refresh inputs, not a hard-coded tab name. TabErrorBoundary only resets when resetKey changes, but the current callers pass fixed literals ('traffic', 'performance', 'funnels', 'replays', 'seo', 'sessions', 'errors'). That means a recoverable revalidation/filter/date-range update inside the same tab will not clear hasError, so the fallback can stay stuck until a full reload. Use a value that changes with the tab’s reload/recovery path (for example, period/date range/filter or a revalidation token).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx` around lines 61 -
73, The `TabErrorBoundary` reset behavior is too static because callers pass
hard-coded tab names as `resetKey`, so `getDerivedStateFromProps` never clears
`hasError` when the tab’s data refreshes. Update the callers of
`TabErrorBoundary` to pass a `resetKey` that changes with the tab’s recovery
inputs, such as the current date range, filters, period, or a revalidation
token, so the boundary resets whenever `resetKey` changes instead of only when
switching tabs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/app/pages/Project/View/components/TabErrorBoundary.tsx`:
- Around line 61-73: The `TabErrorBoundary` reset behavior is too static because
callers pass hard-coded tab names as `resetKey`, so `getDerivedStateFromProps`
never clears `hasError` when the tab’s data refreshes. Update the callers of
`TabErrorBoundary` to pass a `resetKey` that changes with the tab’s recovery
inputs, such as the current date range, filters, period, or a revalidation
token, so the boundary resets whenever `resetKey` changes instead of only when
switching tabs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 20efe754-5e86-4fe7-9412-8002a5629c0f

📥 Commits

Reviewing files that changed from the base of the PR and between 637eade and 7470041.

📒 Files selected for processing (9)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • web/app/pages/Project/View/components/TabErrorBoundary.tsx
  • web/app/pages/Project/tabs/Errors/ErrorsView.tsx
  • web/app/pages/Project/tabs/Funnels/FunnelsView.tsx
  • web/app/pages/Project/tabs/Performance/PerformanceView.tsx
  • web/app/pages/Project/tabs/Replays/ReplaysView.tsx
  • web/app/pages/Project/tabs/SEO/SEOView.tsx
  • web/app/pages/Project/tabs/Sessions/SessionsView.tsx
  • web/app/pages/Project/tabs/Traffic/TrafficView.tsx
✅ Files skipped from review due to trivial changes (1)
  • web/app/pages/Project/tabs/Sessions/SessionsView.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/cloud/src/analytics/analytics.controller.ts

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.

1 participant