Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Dec 5, 2025

Summary by CodeRabbit

  • New Features

    • Paginated fraud events UI with client-side pagination hooks and a new events count endpoint.
    • Partner application risk evaluation API added and used in auto-approval flow.
  • Bug Fixes

    • Fraud events now default to newest-first ordering.
    • Filtering and query parameters consistently support customerId and type filters.
  • Refactor

    • Simplified auto-approve partner UI and removed related server action; legacy program fields cleaned up.
  • Chores / Migrations

    • Backfills and cleanup scripts to reconcile fraud group event counts; obsolete migration removed.
  • Enhancements

    • Event metadata sanitization, deduplication, program-scoped events, and operation timing/logging.
  • Tests

    • New comprehensive tests for fraud group queries and pagination.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Dec 5, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Dec 5, 2025 5:09pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Walkthrough

Added pagination, filtering, counting, sanitization, deduplication, and logging to fraud APIs and hooks; updated Prisma fraud/program schemas; introduced migration/backfill scripts and cleanup changes; replaced high-risk signal logic with a new risk helper; removed auto-approve UI/action; added tests and wired UI pagination.

Changes

Cohort / File(s) Summary
Fraud events API & count
apps/web/app/(ee)/api/fraud/events/route.ts, apps/web/app/(ee)/api/fraud/events/count/route.ts
Parse page/pageSize and remaining queryParams; read filters (groupId, customerId, type) from queryParams; use Prisma skip/take and orderBy: { id: "desc" }; add GET /api/fraud/events/count with group ownership validation and partnerCrossProgramBan early-return.
Client hooks & UI pagination
apps/web/lib/swr/use-fraud-events.ts, apps/web/lib/swr/use-fraud-events-paginated.ts, apps/web/lib/swr/use-fraud-events-count.ts, apps/web/ui/partners/fraud-risks/fraud-events-tables/*
useFraudEvents now accepts { page, pageSize } and exposes isValidating; new useFraudEventsPaginated and useFraudEventsCount added; UI tables switched to paginated hook and wire pagination, setPagination, and fraudEventsCount.
Zod schemas & validation
apps/web/lib/zod/schemas/fraud.ts
Coerce date fields (z.coerce.date()), merge pagination schema into query shapes, and export new fraudEventCountQuerySchema.
Prisma schema changes
packages/prisma/schema/program.prisma, packages/prisma/schema/fraud.prisma
Removed legacy Program fields; added non-nullable programId to FraudEvent (replaced deprecated), and made partner relation non-nullable.
Backfill / migration scripts
apps/web/scripts/migrations/backfill-fraud-group-events-count.ts, apps/web/scripts/migrations/backfill-group-settings.ts, (deleted) apps/web/scripts/migrations/revert-due-commission-aggregation.ts, apps/web/scripts/migrations/cleanup-fraud-events.ts
New backfill script deletes empty fraud groups and reconciles eventCount; added // @ts-nocheck`` to an existing migration; removed a long-running revert script; cleanup script simplified to transactional deletion of single-event groups.
Create/resolve fraud events: dedupe, sanitization, logging
apps/web/lib/api/fraud/create-fraud-events.ts, apps/web/lib/api/fraud/resolve-fraud-groups.ts
Sanitize metadata, include programId in created events, add deduplication against pending groups, compute/create necessary groups, and add duration/prettyPrint logging.
Fraud detection & event emission
apps/web/lib/api/fraud/detect-record-fraud-application.ts, apps/web/lib/api/fraud/get-partner-application-risks.ts
Replace triggeredRules with explicit fraudEvents array; include programId/partnerId for cross-program bans; emit per-conflict duplicate-payout events; added getPartnerApplicationRisks helper returning risksDetected and riskSeverity.
Risk & auto-approve flow changes
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts, apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts, (deleted) apps/web/lib/api/fraud/get-partner-high-risk-signals.ts
Replaced old high-risk signals with getPartnerApplicationRisks, added plan-capability gating for fraud checks in cron and route, simplified partner selection, and removed legacy high-risk helper.
Auto‑approve removal (UI + server action)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/applications-menu.tsx, (deleted) apps/web/lib/actions/partners/update-auto-approve-partners.ts
Removed auto‑approve UI flows, confirm modals, SWR mutation/toast/loading logic, and deleted the server action.
Partner commission & Rewardful import
apps/web/lib/partners/create-partner-commission.ts, apps/web/lib/rewardful/import-campaigns.ts
Use default partner-group fallback when partner group missing; stop updating holdingPeriodDays during Rewardful import (only minPayoutAmount updated).
Tests
apps/web/tests/fraud/fraud-groups.test.ts
New integration tests for fraud groups endpoint covering filters, pagination, retrieval, and error cases.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (UI / hooks)
    participant API as Next.js API (/api/fraud/events, /api/fraud/events/count)
    participant DB as Prisma / Database

    Client->>API: GET /api/fraud/events?page=&pageSize=&groupId=&customerId=&type=
    note right of API: parse page/pageSize + remaining queryParams\napply filters, skip/take, order by id desc
    API->>DB: prisma.fraudEvent.findMany(skip/take, where: queryParams, orderBy: {id: "desc"})
    DB-->>API: events list
    API-->>Client: paginated events JSON

    Client->>API: GET /api/fraud/events/count?groupId=
    API->>DB: prisma.fraudEvent.count(where: { groupId })
    DB-->>API: count
    API-->>Client: { count }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay close attention to Prisma schema updates and migration/backfill ordering.
  • Review deduplication and grouping logic in create-fraud-events.ts for correctness and edge cases.
  • Verify removal of the auto‑approve action and UI has no remaining references.
  • Confirm plan-capability gating and new risk helper are used consistently in cron and API routes.

Possibly related PRs

Suggested reviewers

  • steven-tey

Poem

"I nibbled through logs beneath the moonlight,
paged and counted every furtive byte,
stitched groups and pruned the empty burrow,
hopped through tests until they all said 'go!',
a rabbit cheers — the pipeline's running bright."

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Fraud quick fixes' is vague and generic. While it indicates the changes relate to fraud, it does not specify what the actual fixes are or highlight the primary changes across multiple files. Use a more specific title that highlights the main change, such as 'Refactor fraud event pagination and remove legacy program fields' or 'Add fraud event count API and paginated hooks' to clearly communicate the primary objective.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fraud-quick-fixes

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/scripts/migrations/backfill-fraud-group-events-count.ts (1)

73-73: Consider adding error handling to the main invocation.

The unhandled promise could be caught to provide better error reporting.

Apply this diff:

-main();
+main().catch(console.error);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bed2391 and c79db4b.

📒 Files selected for processing (3)
  • apps/web/app/(ee)/api/fraud/events/route.ts (1 hunks)
  • apps/web/scripts/migrations/backfill-fraud-group-events-count.ts (1 hunks)
  • packages/prisma/schema/program.prisma (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/prisma/schema/program.prisma
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
  • apps/web/scripts/migrations/backfill-fraud-group-events-count.ts
📚 Learning: 2025-07-17T06:41:45.620Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
🧬 Code graph analysis (1)
apps/web/scripts/migrations/backfill-fraud-group-events-count.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
🔇 Additional comments (3)
apps/web/app/(ee)/api/fraud/events/route.ts (1)

112-114: LGTM! Sorting by newest events first.

Changing to descending order by ID is a sensible improvement, as it surfaces the most recent fraud events first in the API response.

apps/web/scripts/migrations/backfill-fraud-group-events-count.ts (2)

5-34: LGTM! Clean deletion of orphaned fraud groups.

The logic correctly identifies and removes fraud event groups that have no associated events using Prisma's none filter. The logging provides good visibility into what's being cleaned up.


36-70: LGTM! Count reconciliation logic is correct.

The script accurately identifies and fixes mismatches between the stored eventCount and the actual number of related fraudEvents. The individual updates in the loop are reasonable given Prisma's API constraints.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/lib/partners/create-partner-commission.ts (1)

335-336: Consider preserving a final fallback if default group is missing

The new fallback

group: programEnrollment.partnerGroup ?? program.groups[0],

is an improvement over the old hard-coded { holdingPeriodDays: 0 }, because it now uses the real default group config. However, if the default group is ever missing or not selected (e.g., legacy data or a failed backfill), program.groups[0] will be undefined, and notifyPartnerCommission would receive group: undefined, whereas previously it always got at least { holdingPeriodDays: 0 }.

If you want a bit more defensive behavior while still preferring the default group, you could keep the old behavior as a final fallback:

-            notifyPartnerCommission({
-              program,
-              // fallback to default group if no partner group is found
-              group: programEnrollment.partnerGroup ?? program.groups[0],
+            notifyPartnerCommission({
+              program,
+              // prefer partner group, then default group, then hard-coded safe fallback
+              group:
+                programEnrollment.partnerGroup ??
+                program.groups?.[0] ??
+                { holdingPeriodDays: 0 },

This preserves safety for any anomalous programs while still using the real default group in normal cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 812dc4e and 394dc34.

📒 Files selected for processing (4)
  • apps/web/lib/partners/create-partner-commission.ts (3 hunks)
  • apps/web/lib/rewardful/import-campaigns.ts (0 hunks)
  • apps/web/scripts/migrations/backfill-group-settings.ts (1 hunks)
  • apps/web/scripts/migrations/revert-due-commission-aggregation.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/lib/rewardful/import-campaigns.ts
  • apps/web/scripts/migrations/revert-due-commission-aggregation.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/web/scripts/migrations/backfill-group-settings.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.

Applied to files:

  • apps/web/lib/partners/create-partner-commission.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/lib/actions/partners/delete-reward.ts:33-41
Timestamp: 2025-08-14T05:17:51.825Z
Learning: In the partner groups system, a rewardId can only belong to one group, establishing a one-to-one relationship between rewards and groups. This means using Prisma's `update` method (rather than `updateMany`) is appropriate when updating groups by rewardId.

Applied to files:

  • apps/web/lib/partners/create-partner-commission.ts
📚 Learning: 2025-08-14T05:00:23.224Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/app/api/og/program/route.tsx:63-64
Timestamp: 2025-08-14T05:00:23.224Z
Learning: In Dub's partner program system, the default partner group will always exist. When programs are created, a default partner group is automatically upserted using DEFAULT_PARTNER_GROUP constant, so accessing program.groups[0] in contexts where the default group is queried is safe.

Applied to files:

  • apps/web/lib/partners/create-partner-commission.ts
🧬 Code graph analysis (1)
apps/web/lib/partners/create-partner-commission.ts (1)
apps/web/lib/zod/schemas/groups.ts (1)
  • DEFAULT_PARTNER_GROUP (16-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
apps/web/lib/partners/create-partner-commission.ts (1)

25-25: Default partner group import & conditional program.groups select look consistent

Using DEFAULT_PARTNER_GROUP.slug in the program select and only loading groups when !programEnrollment.partnerGroup keeps the query lean while providing the needed fallback data. This matches the existing invariant that every program has a default group and aligns with other usages of program.groups[0] in the codebase. Based on learnings, this change looks correct as-is.

Also applies to: 298-308

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/web/app/(ee)/api/fraud/events/count/route.ts (1)

14-23: Consider removing unused fields from select.

The query selects partnerId and type (lines 20-21) but only uses programId (line 32). Consider removing the unused fields to optimize the query.

Apply this diff:

     const fraudGroup = await prisma.fraudEventGroup.findUnique({
       where: {
         id: groupId,
       },
       select: {
         programId: true,
-        partnerId: true,
-        type: true,
       },
     });
apps/web/lib/api/fraud/create-fraud-events.ts (1)

111-121: Sanitizer call mutates metadata in-place; clone if callers expect original data

sanitizeFraudEventMetadata casts the input to an object and deletes fields directly, and you’re passing e.metadata straight into it here. That means any caller holding onto the original fraudEvents array will observe a mutated metadata object after createFraudEvents runs.

If that side effect isn’t intentional, consider shallow‑cloning before sanitizing:

-      metadata: e.metadata ? sanitizeFraudEventMetadata(e.metadata) : undefined,
+      metadata: e.metadata
+        ? sanitizeFraudEventMetadata({ ...(e.metadata as any) })
+        : undefined,

(or alternatively move the clone into sanitizeFraudEventMetadata itself).

apps/web/lib/swr/use-fraud-events.ts (1)

6-12: Pagination + isValidating wiring looks good; consider explicit checks for page / pageSize

This upgrade makes the hook much more flexible, and exposing isValidating is useful for showing revalidation states. One minor thing:

...(page && { page }),
...(pageSize && { pageSize }),

will quietly drop a value of 0. If you ever decide to use zero‑based pages or 0 as a sentinel, the params won’t be sent. You can future‑proof this with explicit undefined checks:

-    ...(page && { page }),
-    ...(pageSize && { pageSize }),
+    ...(page !== undefined && { page }),
+    ...(pageSize !== undefined && { pageSize }),

Also applies to: 18-22, 24-25, 35-35

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 394dc34 and f6a50b1.

📒 Files selected for processing (12)
  • apps/web/app/(ee)/api/fraud/events/count/route.ts (1 hunks)
  • apps/web/app/(ee)/api/fraud/events/route.ts (3 hunks)
  • apps/web/lib/api/fraud/create-fraud-events.ts (4 hunks)
  • apps/web/lib/api/fraud/resolve-fraud-groups.ts (2 hunks)
  • apps/web/lib/swr/use-fraud-events-count.ts (1 hunks)
  • apps/web/lib/swr/use-fraud-events-paginated.ts (1 hunks)
  • apps/web/lib/swr/use-fraud-events.ts (2 hunks)
  • apps/web/lib/zod/schemas/fraud.ts (2 hunks)
  • apps/web/tests/fraud/fraud-groups.test.ts (1 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx (2 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (2 hunks)
  • packages/prisma/schema/fraud.prisma (2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/lib/api/fraud/resolve-fraud-groups.ts
  • apps/web/app/(ee)/api/fraud/events/route.ts
  • apps/web/lib/api/fraud/create-fraud-events.ts
  • apps/web/app/(ee)/api/fraud/events/count/route.ts
  • apps/web/tests/fraud/fraud-groups.test.ts
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
  • apps/web/lib/zod/schemas/fraud.ts
  • apps/web/lib/api/fraud/create-fraud-events.ts
  • apps/web/tests/fraud/fraud-groups.test.ts
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx
  • packages/prisma/schema/fraud.prisma
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
  • apps/web/lib/zod/schemas/fraud.ts
  • apps/web/app/(ee)/api/fraud/events/count/route.ts
  • packages/prisma/schema/fraud.prisma
📚 Learning: 2025-07-17T06:41:45.620Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.

Applied to files:

  • apps/web/app/(ee)/api/fraud/events/route.ts
📚 Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.

Applied to files:

  • apps/web/lib/swr/use-fraud-events-count.ts
🧬 Code graph analysis (10)
apps/web/lib/api/fraud/resolve-fraud-groups.ts (1)
packages/utils/src/functions/pretty-print.ts (1)
  • prettyPrint (1-15)
apps/web/app/(ee)/api/fraud/events/route.ts (1)
apps/web/lib/zod/schemas/fraud.ts (1)
  • fraudEventQuerySchema (66-79)
apps/web/lib/swr/use-fraud-events-count.ts (1)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/lib/zod/schemas/fraud.ts (1)
apps/web/lib/zod/schemas/misc.ts (1)
  • getPaginationQuerySchema (32-55)
apps/web/lib/swr/use-fraud-events-paginated.ts (2)
apps/web/lib/swr/use-fraud-events.ts (1)
  • useFraudEvents (6-38)
apps/web/lib/swr/use-fraud-events-count.ts (1)
  • useFraudEventsCount (6-33)
apps/web/lib/api/fraud/create-fraud-events.ts (4)
apps/web/lib/types.ts (1)
  • CreateFraudEventInput (723-727)
apps/web/lib/api/fraud/utils.ts (3)
  • createGroupCompositeKey (134-138)
  • getPartnerIdForFraudEvent (142-152)
  • sanitizeFraudEventMetadata (118-131)
packages/prisma/index.ts (1)
  • prisma (3-9)
packages/utils/src/functions/pretty-print.ts (1)
  • prettyPrint (1-15)
apps/web/app/(ee)/api/fraud/events/count/route.ts (6)
apps/web/app/(ee)/api/fraud/events/route.ts (1)
  • GET (14-126)
apps/web/app/(ee)/api/fraud/groups/route.ts (1)
  • GET (12-81)
apps/web/lib/auth/workspace.ts (1)
  • withWorkspace (55-488)
apps/web/lib/zod/schemas/fraud.ts (1)
  • fraudEventCountQuerySchema (81-83)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/api/errors.ts (1)
  • DubApiError (58-75)
apps/web/lib/swr/use-fraud-events.ts (1)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/tests/fraud/fraud-groups.test.ts (4)
apps/web/lib/zod/schemas/fraud.ts (1)
  • fraudGroupSchema (12-28)
apps/web/tests/utils/integration.ts (1)
  • IntegrationHarness (14-118)
packages/prisma/client.ts (2)
  • FraudEventStatus (16-16)
  • FraudRuleType (17-17)
apps/web/tests/utils/resource.ts (1)
  • E2E_FRAUD_PARTNER (210-231)
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (2)
apps/web/lib/swr/use-fraud-events-paginated.ts (1)
  • useFraudEventsPaginated (5-30)
packages/ui/src/table/table.tsx (1)
  • useTable (51-243)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
apps/web/lib/api/fraud/resolve-fraud-groups.ts (1)

2-2: LGTM: Observability improvement.

The addition of structured logging with prettyPrint provides useful visibility into fraud group resolution operations.

Also applies to: 27-28

apps/web/tests/fraud/fraud-groups.test.ts (1)

1-177: Excellent test coverage.

The test suite comprehensively covers the fraud groups endpoint functionality including filtering, pagination, and edge cases. The early return pattern at lines 103-105 appropriately handles scenarios where test data may not exist.

apps/web/app/(ee)/api/fraud/events/route.ts (1)

17-18: LGTM: Pagination implementation.

The changes correctly implement server-side pagination by:

  • Extracting page and pageSize from the validated schema
  • Applying skip and take to the Prisma query
  • Changing the default order to descending (newest first), which is more intuitive for event lists

Also applies to: 115-118

apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (1)

3-3: LGTM: Pagination integration.

The component correctly integrates paginated data by:

  • Switching to useFraudEventsPaginated hook
  • Wiring pagination state and handlers to the table
  • Providing rowCount from fraudEventsCount for pagination controls

Also applies to: 19-32

apps/web/lib/zod/schemas/fraud.ts (2)

17-18: LGTM: Date coercion for API serialization.

Using z.coerce.date() appropriately handles date strings from API responses that are serialized as ISO strings.


66-83: LGTM: Schema updates for pagination and count.

The changes correctly:

  • Merge pagination schemas into both branches of fraudEventQuerySchema
  • Add a dedicated fraudEventCountQuerySchema for the count endpoint
apps/web/app/(ee)/api/fraud/events/count/route.ts (1)

1-50: LGTM: Count endpoint implementation.

The endpoint correctly:

  • Validates input with fraudEventCountQuerySchema
  • Verifies group existence and program ownership
  • Returns the fraud event count
  • Enforces appropriate access control
apps/web/lib/swr/use-fraud-events-count.ts (1)

14-16: The code correctly preserves groupId in the request.

The getQueryString implementation starts by copying all existing searchParams into newParams, then merges in the provided key-value pairs. Since no include or exclude options are specified, all existing search parameters—including groupId—are preserved. The final query string will contain both groupId and workspaceId, satisfying the API requirement.

Likely an incorrect or invalid review comment.

packages/prisma/schema/fraud.prisma (1)

58-58: No migration or code updates needed for programId and partner fields.

The programId and partnerId fields are already enforced as required at the application layer through the CreateFraudEventInput type definition, which mandates both fields are provided from FraudEventGroup (non-nullable). The create-fraud-events.ts implementation always assigns values to these fields and never handles null cases. The schema change simply formalizes an existing constraint that was already in place.

Note: The schema includes TODOs for making fraudEventGroupId and hash required after migration, but programId and partnerId are not subject to these constraints.

Likely an incorrect or invalid review comment.

apps/web/lib/swr/use-fraud-events-paginated.ts (1)

1-30: Clean composition of events + count into a paginated hook

This is a nice, minimal wrapper: local pagination state, forwarding page/pageSize into useFraudEvents, combining loading || isValidating, and exposing fraudEventsCount gives consumers everything they need for a paginated table without duplicating logic. No issues from my side here.

apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx (1)

4-4: Paginated hook integration looks solid; double‑check pageIndex convention with useTable

Switching to useFraudEventsPaginated and wiring pagination, onPaginationChange: setPagination, and rowCount: fraudEventsCount ?? 0 into useTable is a clean upgrade and matches the pattern used in the other fraud tables.

One thing to verify: useFraudEventsPaginated currently initializes pageIndex to 1. If useTable (or the underlying table library) assumes a 0‑based pageIndex, this could cause an off‑by‑one where the first page of results is skipped or misaligned. If the table is 0‑based, update the hook's initial pageIndex (and any related offset logic) to start at 0.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/lib/api/fraud/detect-record-fraud-application.ts (1)

67-81: Consider skipping self-pairs in duplicate payout method events

This nested loop currently generates events for duplicatePartners, including self‑pairs like (sourcePartner.id === conflictingPartner.id), which will produce “duplicate payout method” events where a partner is flagged as a duplicate of themselves, and doubles the number of cross‑partner edges (A→B and B→A). That’s probably just noisy rather than harmful, but you can tighten it up by skipping self‑pairs:

-      for (const sourcePartner of duplicatePartners) {
-        for (const conflictingPartner of duplicatePartners) {
-          fraudEvents.push({
+      for (const sourcePartner of duplicatePartners) {
+        for (const conflictingPartner of duplicatePartners) {
+          if (sourcePartner.id === conflictingPartner.id) continue;
+
+          fraudEvents.push({
             programId: program.id,
             partnerId: sourcePartner.id,
             type: FraudRuleType.partnerDuplicatePayoutMethod,
             metadata: {
               payoutMethodHash: partner.payoutMethodHash,
               duplicatePartnerId: conflictingPartner.id,
             },
           });
         }
       }

If the intent is to only flag the applying partner against existing duplicates, you could further restrict sourcePartner to partner instead of all entries in duplicatePartners.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6a50b1 and 93da35e.

📒 Files selected for processing (1)
  • apps/web/lib/api/fraud/detect-record-fraud-application.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/lib/api/fraud/detect-record-fraud-application.ts
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/lib/api/fraud/detect-record-fraud-application.ts
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.

Applied to files:

  • apps/web/lib/api/fraud/detect-record-fraud-application.ts
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.

Applied to files:

  • apps/web/lib/api/fraud/detect-record-fraud-application.ts
🧬 Code graph analysis (1)
apps/web/lib/api/fraud/detect-record-fraud-application.ts (3)
apps/web/lib/types.ts (1)
  • CreateFraudEventInput (723-727)
packages/prisma/client.ts (1)
  • FraudRuleType (17-17)
apps/web/lib/api/fraud/create-fraud-events.ts (1)
  • createFraudEvents (14-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
apps/web/lib/api/fraud/detect-record-fraud-application.ts (3)

1-3: Type-safe imports for partner-scoped fraud rules look correct

Using CreateFraudEventInput and FraudRuleType here cleanly aligns this partner-application flow with the shared createFraudEvents pipeline and the partner-scoped rules (partnerCrossProgramBan, partnerDuplicatePayoutMethod) that are stubbed in the registry but realized in this file.

Based on learnings, this matches the intended separation between registry stubs and partner-onboarding enforcement.


26-26: Centralized accumulation and single createFraudEvents call is a good pattern

Collecting CreateFraudEventInput[] in fraudEvents and invoking createFraudEvents once at the end keeps the flow simple, leverages the dedup/grouping logic in createFraudEvents, and relies on its empty-array short‑circuit, so no extra guard is needed.

Also applies to: 85-85


41-46: Cross-program ban fraud event construction looks consistent

The event for partnerCrossProgramBan correctly sets programId and partnerId for the applying partner and uses the scoped FraudRuleType enum, which matches the CreateFraudEventInput contract and how groups are keyed in createFraudEvents.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93da35e and 4892d0b.

📒 Files selected for processing (1)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (2)
apps/web/lib/swr/use-fraud-events-paginated.ts (1)
  • useFraudEventsPaginated (5-30)
packages/ui/src/table/table.tsx (1)
  • useTable (51-243)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (1)

3-32: Paginated hook integration into useTable looks solid

Switching to useFraudEventsPaginated and wiring pagination, onPaginationChange: setPagination, and rowCount: fraudEventsCount ?? 0 into useTable matches the shared table utility’s manual‑pagination contract and should work well with the new count endpoint.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/lib/api/fraud/create-fraud-events.ts (1)

137-147: Dev‑only payload logging is safer, but consider trimming payload or dropping to debug

Gating the payload log on NODE_ENV === "development" addresses production noise/privacy, but console.info(prettyPrint(newEventsWithGroup)) can still be quite heavy (large JSON + prettyPrint cost) even in dev. You could keep a concise info log with just counts and move the full payload to console.debug (or behind an explicit debug flag) to keep default dev logs readable and cheaper.

🧹 Nitpick comments (2)
apps/web/lib/api/fraud/create-fraud-events.ts (2)

15-16: Make timing log resilient to errors with a small try/finally wrapper

The performance.now() timing is useful but only runs on the happy path; any thrown Prisma error or unexpected exception will skip the duration log. Consider wrapping the body of createFraudEvents in a try/finally so the duration log always executes, while keeping the instrumentation minimal.

Also applies to: 167-170


111-121: Be aware that sanitizeFraudEventMetadata mutates the original metadata object

At this call site, e.metadata is the same reference coming from the original fraudEvents array (via object spread), and sanitizeFraudEventMetadata deletes keys in-place (duplicatePartnerId, payoutMethodHash) before returning the object or undefined (see apps/web/lib/api/fraud/utils.ts). If callers reuse the same metadata object after calling createFraudEvents, they’ll see the sanitized version.

If that side effect isn’t desired, consider having sanitizeFraudEventMetadata shallow‑clone before deleting keys, or clone e.metadata here before passing it in.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4892d0b and e82bb71.

📒 Files selected for processing (2)
  • apps/web/lib/api/fraud/create-fraud-events.ts (4 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
  • apps/web/lib/api/fraud/create-fraud-events.ts
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.

Applied to files:

  • apps/web/lib/api/fraud/create-fraud-events.ts
🧬 Code graph analysis (2)
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (2)
apps/web/lib/swr/use-fraud-events-paginated.ts (1)
  • useFraudEventsPaginated (5-30)
packages/ui/src/table/table.tsx (1)
  • useTable (51-243)
apps/web/lib/api/fraud/create-fraud-events.ts (4)
apps/web/lib/types.ts (1)
  • CreateFraudEventInput (723-727)
apps/web/lib/api/fraud/utils.ts (3)
  • createGroupCompositeKey (134-138)
  • getPartnerIdForFraudEvent (142-152)
  • sanitizeFraudEventMetadata (118-131)
packages/prisma/index.ts (1)
  • prisma (3-9)
packages/utils/src/functions/pretty-print.ts (1)
  • prettyPrint (1-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx (2)

84-98: URL encoding fix looks good.

The href now uses Next.js object syntax with pathname and query, ensuring proper URL encoding of the referer parameter. The conditional spread ...(referer && { referer }) correctly omits the parameter when undefined.


28-32: LGTM on table configuration.

The pagination props (pagination, onPaginationChange, rowCount) are correctly wired to the table, and the fallback fraudEventsCount ?? 0 appropriately handles the loading state.

Also applies to: 111-118

Comment on lines +19 to +32
const {
fraudEvents,
loading,
error,
pagination,
setPagination,
fraudEventsCount,
} = useFraudEventsPaginated<EventDataProps>();

const table = useTable({
data: fraudEvents || [],
pagination,
onPaginationChange: setPagination,
rowCount: fraudEventsCount ?? 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /root && find . -name "*useFraudEventsPaginated*" -o -name "*fraud-events*hook*" 2>/dev/null | head -10

Repository: dubinc/dub

Length of output: 109


🏁 Script executed:

fd -e ts -e tsx 'fraud' --type f

Repository: dubinc/dub

Length of output: 3243


🏁 Script executed:

ls -la apps/web/ui/partners/fraud-risks/

Repository: dubinc/dub

Length of output: 832


🏁 Script executed:

git ls-files "*/fraud*" | head -30

Repository: dubinc/dub

Length of output: 2066


🏁 Script executed:

rg 'export.*useFraudEventsPaginated' --type=ts --type=tsx -A15

Repository: dubinc/dub

Length of output: 81


🏁 Script executed:

rg 'const useFraudEventsPaginated|function useFraudEventsPaginated' --type=ts --type=tsx -A25

Repository: dubinc/dub

Length of output: 81


🏁 Script executed:

rg 'useState.*pageIndex' --type=ts --type=tsx -B2 -A2

Repository: dubinc/dub

Length of output: 81


🏁 Script executed:

# Try to find and read the specific file
find . -name "fraud-referral-source-banned-table.tsx" -type f 2>/dev/null

Repository: dubinc/dub

Length of output: 147


🏁 Script executed:

git ls-files | grep -E '\.tsx?$' | grep -i 'paginated|pagination' | head -20

Repository: dubinc/dub

Length of output: 36


🏁 Script executed:

rg 'onPaginationChange' --type=ts --type=tsx -B5 -A5

Repository: dubinc/dub

Length of output: 81


🏁 Script executed:

rg 'export.*useTable|const.*useTable.*=' --type=ts --type=tsx -A10 | head -50

Repository: dubinc/dub

Length of output: 81


🏁 Script executed:

# Check if the fraud-risks path exists
git ls-files | grep -i fraud

Repository: dubinc/dub

Length of output: 4719


🏁 Script executed:

# List files in apps directory
git ls-files "apps/*" 2>/dev/null | head -40

Repository: dubinc/dub

Length of output: 2302


🏁 Script executed:

wc -l apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 103


🏁 Script executed:

wc -l apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx

Repository: dubinc/dub

Length of output: 149


🏁 Script executed:

grep -n "useState\|pageIndex\|pagination" apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 254


🏁 Script executed:

# Check the size first
wc -l apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 103


🏁 Script executed:

head -200 apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx

Repository: dubinc/dub

Length of output: 3258


🏁 Script executed:

# Get table component
sed -n '1,50p' apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx

Repository: dubinc/dub

Length of output: 1448


🏁 Script executed:

# Try to get complete hook file with line numbers
cat -n apps/web/lib/swr/use-fraud-events-paginated.ts | head -60

Repository: dubinc/dub

Length of output: 957


🏁 Script executed:

# Get complete files with less output
python3 << 'PYTHON_EOF'
import os

hook_file = "apps/web/lib/swr/use-fraud-events-paginated.ts"
table_file = "apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx"

if os.path.exists(hook_file):
    with open(hook_file) as f:
        content = f.read()
    print("=== HOOK FILE ===")
    print(content[:2000])
    if len(content) > 2000:
        print(f"\n... ({len(content)} total chars) ...\n")
        print(content[-500:])

if os.path.exists(table_file):
    with open(table_file) as f:
        lines = f.readlines()
    print("\n=== TABLE FILE (first 80 lines) ===")
    for i, line in enumerate(lines[:80], 1):
        print(f"{i:3d}: {line}", end="")
PYTHON_EOF

Repository: dubinc/dub

Length of output: 140


🏁 Script executed:

# Check if there's any offset calculation or index conversion
grep -r "pageIndex.*1\|offset.*page\|page.*offset" apps/web/lib/swr --include="*.ts" | head -20

Repository: dubinc/dub

Length of output: 118


🏁 Script executed:

# Search for any other pagination tables to see if there's a pattern
grep -l "useFraudEventsPaginated" apps/web/ui/partners/fraud-risks/fraud-events-tables/*.tsx

Repository: dubinc/dub

Length of output: 236


🏁 Script executed:

# Check if there's any middleware or conversion in useTable
grep -A10 "const table = useTable" apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx

Repository: dubinc/dub

Length of output: 309


🏁 Script executed:

# Get raw file content with absolute line numbers
cat -n apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 957


🏁 Script executed:

# Try with sed to get line ranges
sed -n '1,60p' apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 747


🏁 Script executed:

sed -n '1,100p' apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx

Repository: dubinc/dub

Length of output: 2755


🏁 Script executed:

# Get complete small files
if [ $(wc -l < apps/web/lib/swr/use-fraud-events-paginated.ts) -lt 100 ]; then
  cat apps/web/lib/swr/use-fraud-events-paginated.ts
fi

Repository: dubinc/dub

Length of output: 747


🏁 Script executed:

# Get lines 1-50 of hook
awk 'NR<=50' apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 747


🏁 Script executed:

# Use head command directly
head -60 apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 747


🏁 Script executed:

# Extract key lines
grep -n "pageIndex\|useState.*page\|pageSize\|offset\|skip" apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 206


🏁 Script executed:

# Try to understand the structure by checking imports and exports
grep -n "export\|import\|useState\|useFraudEventsPaginated" apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 319


🏁 Script executed:

# Check file type
file apps/web/lib/swr/use-fraud-events-paginated.ts

Repository: dubinc/dub

Length of output: 98


🏁 Script executed:

# Try direct output with less restrictions
cat apps/web/lib/swr/use-fraud-events-paginated.ts 2>&1 | head -150

Repository: dubinc/dub

Length of output: 747


🏁 Script executed:

# Python alternative with error handling
python3 -c "
with open('apps/web/lib/swr/use-fraud-events-paginated.ts', 'r', encoding='utf-8', errors='ignore') as f:
    lines = f.readlines()
    for i, line in enumerate(lines[:100], 1):
        print(f'{i:3d}: {line}', end='')
"

Repository: dubinc/dub

Length of output: 140


🏁 Script executed:

# Check API endpoint for pagination handling
grep -A20 "searchParams\|pageIndex\|offset" apps/web/app/\(ee\)/api/fraud/events/route.ts

Repository: dubinc/dub

Length of output: 716


🏁 Script executed:

grep -n "pageIndex\|skip\|offset" apps/web/app/\(ee\)/api/fraud/events/route.ts

Repository: dubinc/dub

Length of output: 92


Fix pagination index mismatch between hook and TanStack Table

The useFraudEventsPaginated hook initializes pageIndex: 1 (1-based), but TanStack Table expects 0-based indexing. When TanStack Table triggers onPaginationChange with 0-based indices (0, 1, 2...), these are passed directly to useFraudEvents as page numbers, causing off-by-one pagination errors. Adjust the hook to either initialize pageIndex: 0 or convert incoming indices from TanStack Table by subtracting 1 before storing them.

🤖 Prompt for AI Agents
In
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
around lines 19 to 32, the table passes TanStack Table's 0-based pagination
indices directly into the hook which expects 1-based pages, causing off-by-one
pages; update the hook usage to align indices by converting incoming pagination
indices from the table before calling setPagination (subtract 1) or change the
hook to initialize and consume pageIndex as 0-based; ensure any place that
reads/writes pagination normalizes between TanStack (0-based) and your API/hook
(if it stays 1-based) so stored page numbers match the hook's expectation.

…ng events and refining group identification based on programId/partnerId/type combinations.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/lib/api/fraud/create-fraud-events.ts (2)

116-136: Consider adding a safeguard for missing group lookups.

finalGroupLookup.get(createGroupCompositeKey(e)) could return undefined if the composite key is somehow not found. While the current logic ensures all events have corresponding groups (via existingGroups + newGroups), adding a defensive check or assertion would make this more robust against future refactoring.

-      fraudEventGroupId: finalGroupLookup.get(createGroupCompositeKey(e)),
+      fraudEventGroupId: finalGroupLookup.get(createGroupCompositeKey(e)) ?? 
+        (() => { throw new Error(`Missing fraud event group for event: ${e.hash}`); })(),

Alternatively, if undefined is acceptable for fraudEventGroupId, this can be safely ignored.


150-166: Consider logging failed group updates.

Promise.allSettled results are discarded, so any failed eventCount updates will go unnoticed. While this resilience pattern avoids blocking, silently ignoring failures could lead to stale eventCount values.

Consider logging failures for observability:

-  await Promise.allSettled(
+  const updateResults = await Promise.allSettled(
     finalGroups.map((group) =>
       prisma.fraudEventGroup.update({
         ...
       }),
     ),
   );
+
+  const failedUpdates = updateResults.filter((r) => r.status === "rejected");
+  if (failedUpdates.length > 0) {
+    console.warn(`Failed to update ${failedUpdates.length} fraud event group(s)`);
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e82bb71 and 82b7922.

📒 Files selected for processing (1)
  • apps/web/lib/api/fraud/create-fraud-events.ts (6 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/lib/api/fraud/create-fraud-events.ts
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.

Applied to files:

  • apps/web/lib/api/fraud/create-fraud-events.ts
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/lib/api/fraud/create-fraud-events.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
apps/web/lib/api/fraud/create-fraud-events.ts (4)

1-16: LGTM on imports and timing instrumentation.

The new imports are well-scoped, and the performance.now() instrumentation is a lightweight approach for measuring function execution time.


31-57: Solid deduplication logic.

The approach of checking for existing events with matching hashes in pending groups before inserting is correct. The early return when no new events exist avoids unnecessary database operations.


138-148: Logging improvements address prior feedback.

Gating the detailed payload logging behind NODE_ENV === "development" resolves the earlier concern about noisy and potentially sensitive logging in production. The development-time visibility is retained for debugging purposes.


167-171: Duration logging is appropriate for observability.

The unconditional timing log provides useful performance metrics without exposing sensitive data. This aligns with the prior feedback that the duration log is suitable as-is.

…ith application risk evaluations, introducing a new function to consolidate risk detection logic, and updating related API routes and UI components accordingly.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
apps/web/scripts/migrations/cleanup-fraud-events.ts (4)

6-16: Consider batching findMany/deletes if this can touch a large number of groups

Right now all matching fraudEventGroup rows are loaded and then deleted in a single transaction based on groupIds. If the number of partnerDuplicatePayoutMethod groups with eventCount = 1 is large, this can put pressure on memory and create a very large delete in one go.

If this migration might run against big datasets, consider processing in batches (e.g., limit/skip or cursor-based pagination) and deleting per batch to reduce risk of timeouts / lock contention.

Also applies to: 28-28


18-25: Tighten logging around the “no-op” case

console.table(fraudGroups) executes even when there are no results, which is harmless but a bit noisy.

You could early-return before tabling in the zero-length case, and log a dedicated “nothing to clean up” message so it’s clearer in CI logs when the script is a no-op.

-  console.log(
-    `Found ${fraudGroups.length} fraud groups with one event for partnerDuplicatePayoutMethod.`,
-  );
-
-  console.table(fraudGroups);
-
-  if (fraudGroups.length === 0) {
-    return;
-  }
+  if (fraudGroups.length === 0) {
+    console.log(
+      "Found 0 fraud groups with one event for partnerDuplicatePayoutMethod. Nothing to clean up.",
+    );
+    return;
+  }
+
+  console.log(
+    `Found ${fraudGroups.length} fraud groups with one event for partnerDuplicatePayoutMethod.`,
+  );
+
+  console.table(fraudGroups);

51-55: Add error handling around main() for better CLI/CI behavior

main() is invoked without a .catch, so failures will surface as unhandled promise rejections rather than a clean non‑zero exit code with a stack trace.

For migration scripts, it’s usually better to fail loudly and exit(1):

-async function main() {
-  await cleanupSingleEventGroups();
-}
-
-main();
+async function main() {
+  await cleanupSingleEventGroups();
+}
+
+main().catch((err) => {
+  console.error(err);
+  process.exit(1);
+});

30-48: Remove redundant fraudEvent.deleteMany call—the onDelete: Cascade FK constraint handles deletion automatically

The fraudEventfraudEventGroup relation has onDelete: Cascade, so the database automatically deletes child fraudEvent rows when the parent fraudEventGroup is deleted. The explicit fraudEvent.deleteMany call is redundant. You can simplify the transaction to only delete the groups:

  await prisma.$transaction(async (tx) => {
-    // Delete the group
-    await tx.fraudEventGroup.deleteMany({
-      where: {
-        id: {
-          in: groupIds,
-        },
-      },
-    });
-
-    // Delete the raw events
-    await tx.fraudEvent.deleteMany({
-      where: {
-        fraudEventGroupId: {
-          in: groupIds,
-        },
-      },
-    });
+    // Delete the groups (cascade will delete child fraudEvents automatically)
+    await tx.fraudEventGroup.deleteMany({
+      where: {
+        id: {
+          in: groupIds,
+        },
+      },
+    });
  });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f9fc2d and 35c4c98.

📒 Files selected for processing (1)
  • apps/web/scripts/migrations/cleanup-fraud-events.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/scripts/migrations/cleanup-fraud-events.ts
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/scripts/migrations/cleanup-fraud-events.ts
🧬 Code graph analysis (1)
apps/web/scripts/migrations/cleanup-fraud-events.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/scripts/migrations/cleanup-fraud-events.ts (1)

28-51: Consider chunking deletions to avoid very large IN clauses and transactions

Building a single groupIds array and then running two deleteMany calls with in: groupIds inside one transaction is straightforward, but if there are many matching single‑event groups this can turn into a very large IN list and a big interactive transaction, which may hit DB parameter limits or hurt performance.

You might want to:

  • Process groupIds in reasonably sized chunks (e.g., a few thousand ids per batch) and run deleteMany per chunk, or
  • Keep the same filters but push more of the logic into the DB (e.g., multiple deleteMany calls keyed by the original where condition rather than a huge IN set), even if that means giving up a single all‑or‑nothing transaction for the entire dataset.

Given this is a one‑off cleanup script, the current approach is workable, but chunking would make it more robust on large datasets.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35c4c98 and 743f623.

📒 Files selected for processing (1)
  • apps/web/scripts/migrations/cleanup-fraud-events.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/scripts/migrations/cleanup-fraud-events.ts
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/scripts/migrations/cleanup-fraud-events.ts
🧬 Code graph analysis (1)
apps/web/scripts/migrations/cleanup-fraud-events.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
🔇 Additional comments (1)
apps/web/scripts/migrations/cleanup-fraud-events.ts (1)

6-25: Targeted selection and early‑exit behavior look solid

The findMany query scoped to type: "partnerDuplicatePayoutMethod" and eventCount: 1, combined with the minimal select and fraudGroups.length === 0 early return, makes this cleanup very targeted and avoids unnecessary DB work when there’s nothing to do. The console.log/console.table outputs are also safe here since they only include ids and aggregate metadata.

@steven-tey steven-tey merged commit 7d8918e into main Dec 5, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the fraud-quick-fixes branch December 5, 2025 17:29
@coderabbitai coderabbitai bot mentioned this pull request Dec 9, 2025
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.

3 participants