-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fraud quick fixes #3195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fraud quick fixes #3195
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdded 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
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 }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this 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
📒 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.tsapps/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
nonefilter. 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
eventCountand the actual number of relatedfraudEvents. The individual updates in the loop are reasonable given Prisma's API constraints.
There was a problem hiding this 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 missingThe 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 beundefined, andnotifyPartnerCommissionwould receivegroup: 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
📒 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 consistentUsing
DEFAULT_PARTNER_GROUP.slugin theprogramselect and only loadinggroupswhen!programEnrollment.partnerGroupkeeps 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 ofprogram.groups[0]in the codebase. Based on learnings, this change looks correct as-is.Also applies to: 298-308
There was a problem hiding this 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
partnerIdandtype(lines 20-21) but only usesprogramId(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 mutatesmetadatain-place; clone if callers expect original data
sanitizeFraudEventMetadatacasts the input to an object and deletes fields directly, and you’re passinge.metadatastraight into it here. That means any caller holding onto the originalfraudEventsarray will observe a mutatedmetadataobject aftercreateFraudEventsruns.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
sanitizeFraudEventMetadataitself).apps/web/lib/swr/use-fraud-events.ts (1)
6-12: Pagination +isValidatingwiring looks good; consider explicit checks forpage/pageSizeThis upgrade makes the hook much more flexible, and exposing
isValidatingis 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 or0as a sentinel, the params won’t be sent. You can future‑proof this with explicitundefinedchecks:- ...(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
📒 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.tsapps/web/app/(ee)/api/fraud/events/route.tsapps/web/lib/api/fraud/create-fraud-events.tsapps/web/app/(ee)/api/fraud/events/count/route.tsapps/web/tests/fraud/fraud-groups.test.tsapps/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.tsapps/web/lib/zod/schemas/fraud.tsapps/web/lib/api/fraud/create-fraud-events.tsapps/web/tests/fraud/fraud-groups.test.tsapps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsxpackages/prisma/schema/fraud.prismaapps/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.tsapps/web/lib/zod/schemas/fraud.tsapps/web/app/(ee)/api/fraud/events/count/route.tspackages/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
prettyPrintprovides 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
pageandpageSizefrom the validated schema- Applying
skipandtaketo 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
useFraudEventsPaginatedhook- Wiring pagination state and handlers to the table
- Providing
rowCountfromfraudEventsCountfor pagination controlsAlso 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
fraudEventCountQuerySchemafor the count endpointapps/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 preservesgroupIdin the request.The
getQueryStringimplementation starts by copying all existingsearchParamsintonewParams, then merges in the provided key-value pairs. Since noincludeorexcludeoptions are specified, all existing search parameters—includinggroupId—are preserved. The final query string will contain bothgroupIdandworkspaceId, 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 forprogramIdandpartnerfields.The
programIdandpartnerIdfields are already enforced as required at the application layer through theCreateFraudEventInputtype definition, which mandates both fields are provided fromFraudEventGroup(non-nullable). Thecreate-fraud-events.tsimplementation 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
fraudEventGroupIdandhashrequired after migration, butprogramIdandpartnerIdare 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 hookThis is a nice, minimal wrapper: local pagination state, forwarding
page/pageSizeintouseFraudEvents, combiningloading || isValidating, and exposingfraudEventsCountgives 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‑checkpageIndexconvention withuseTableSwitching to
useFraudEventsPaginatedand wiringpagination,onPaginationChange: setPagination, androwCount: fraudEventsCount ?? 0intouseTableis a clean upgrade and matches the pattern used in the other fraud tables.One thing to verify:
useFraudEventsPaginatedcurrently initializespageIndexto1. IfuseTable(or the underlying table library) assumes a 0‑basedpageIndex, 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 initialpageIndex(and any related offset logic) to start at0.
There was a problem hiding this 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 eventsThis nested loop currently generates
n²events forduplicatePartners, 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
sourcePartnertopartnerinstead of all entries induplicatePartners.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 correctUsing
CreateFraudEventInputandFraudRuleTypehere cleanly aligns this partner-application flow with the sharedcreateFraudEventspipeline 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 singlecreateFraudEventscall is a good patternCollecting
CreateFraudEventInput[]infraudEventsand invokingcreateFraudEventsonce at the end keeps the flow simple, leverages the dedup/grouping logic increateFraudEvents, 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 consistentThe event for
partnerCrossProgramBancorrectly setsprogramIdandpartnerIdfor the applying partner and uses the scopedFraudRuleTypeenum, which matches theCreateFraudEventInputcontract and how groups are keyed increateFraudEvents.
There was a problem hiding this 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
📒 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 intouseTablelooks solidSwitching to
useFraudEventsPaginatedand wiringpagination,onPaginationChange: setPagination, androwCount: fraudEventsCount ?? 0intouseTablematches the shared table utility’s manual‑pagination contract and should work well with the new count endpoint.
apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsx
Show resolved
Hide resolved
There was a problem hiding this 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 debugGating the payload log on
NODE_ENV === "development"addresses production noise/privacy, butconsole.info(prettyPrint(newEventsWithGroup))can still be quite heavy (large JSON + prettyPrint cost) even in dev. You could keep a conciseinfolog with just counts and move the full payload toconsole.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 wrapperThe
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 ofcreateFraudEventsin atry/finallyso the duration log always executes, while keeping the instrumentation minimal.Also applies to: 167-170
111-121: Be aware thatsanitizeFraudEventMetadatamutates the original metadata objectAt this call site,
e.metadatais the same reference coming from the originalfraudEventsarray (via object spread), andsanitizeFraudEventMetadatadeletes keys in-place (duplicatePartnerId,payoutMethodHash) before returning the object orundefined(see apps/web/lib/api/fraud/utils.ts). If callers reuse the same metadata object after callingcreateFraudEvents, they’ll see the sanitized version.If that side effect isn’t desired, consider having
sanitizeFraudEventMetadatashallow‑clone before deleting keys, or clonee.metadatahere before passing it in.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.tsxapps/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
hrefnow uses Next.js object syntax withpathnameandquery, ensuring proper URL encoding of therefererparameter. 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 fallbackfraudEventsCount ?? 0appropriately handles the loading state.Also applies to: 111-118
| const { | ||
| fraudEvents, | ||
| loading, | ||
| error, | ||
| pagination, | ||
| setPagination, | ||
| fraudEventsCount, | ||
| } = useFraudEventsPaginated<EventDataProps>(); | ||
|
|
||
| const table = useTable({ | ||
| data: fraudEvents || [], | ||
| pagination, | ||
| onPaginationChange: setPagination, | ||
| rowCount: fraudEventsCount ?? 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cd /root && find . -name "*useFraudEventsPaginated*" -o -name "*fraud-events*hook*" 2>/dev/null | head -10Repository: dubinc/dub
Length of output: 109
🏁 Script executed:
fd -e ts -e tsx 'fraud' --type fRepository: 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 -30Repository: dubinc/dub
Length of output: 2066
🏁 Script executed:
rg 'export.*useFraudEventsPaginated' --type=ts --type=tsx -A15Repository: dubinc/dub
Length of output: 81
🏁 Script executed:
rg 'const useFraudEventsPaginated|function useFraudEventsPaginated' --type=ts --type=tsx -A25Repository: dubinc/dub
Length of output: 81
🏁 Script executed:
rg 'useState.*pageIndex' --type=ts --type=tsx -B2 -A2Repository: 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/nullRepository: dubinc/dub
Length of output: 147
🏁 Script executed:
git ls-files | grep -E '\.tsx?$' | grep -i 'paginated|pagination' | head -20Repository: dubinc/dub
Length of output: 36
🏁 Script executed:
rg 'onPaginationChange' --type=ts --type=tsx -B5 -A5Repository: dubinc/dub
Length of output: 81
🏁 Script executed:
rg 'export.*useTable|const.*useTable.*=' --type=ts --type=tsx -A10 | head -50Repository: dubinc/dub
Length of output: 81
🏁 Script executed:
# Check if the fraud-risks path exists
git ls-files | grep -i fraudRepository: dubinc/dub
Length of output: 4719
🏁 Script executed:
# List files in apps directory
git ls-files "apps/*" 2>/dev/null | head -40Repository: dubinc/dub
Length of output: 2302
🏁 Script executed:
wc -l apps/web/lib/swr/use-fraud-events-paginated.tsRepository: dubinc/dub
Length of output: 103
🏁 Script executed:
wc -l apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsxRepository: dubinc/dub
Length of output: 149
🏁 Script executed:
grep -n "useState\|pageIndex\|pagination" apps/web/lib/swr/use-fraud-events-paginated.tsRepository: dubinc/dub
Length of output: 254
🏁 Script executed:
# Check the size first
wc -l apps/web/lib/swr/use-fraud-events-paginated.tsRepository: dubinc/dub
Length of output: 103
🏁 Script executed:
head -200 apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-referral-source-banned-table.tsxRepository: 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.tsxRepository: 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 -60Repository: 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_EOFRepository: 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 -20Repository: 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/*.tsxRepository: 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.tsxRepository: 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.tsRepository: 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.tsRepository: 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.tsxRepository: 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
fiRepository: 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.tsRepository: dubinc/dub
Length of output: 747
🏁 Script executed:
# Use head command directly
head -60 apps/web/lib/swr/use-fraud-events-paginated.tsRepository: 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.tsRepository: 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.tsRepository: dubinc/dub
Length of output: 319
🏁 Script executed:
# Check file type
file apps/web/lib/swr/use-fraud-events-paginated.tsRepository: 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 -150Repository: 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.tsRepository: dubinc/dub
Length of output: 716
🏁 Script executed:
grep -n "pageIndex\|skip\|offset" apps/web/app/\(ee\)/api/fraud/events/route.tsRepository: 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.
There was a problem hiding this 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 returnundefinedif the composite key is somehow not found. While the current logic ensures all events have corresponding groups (viaexistingGroups+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
undefinedis acceptable forfraudEventGroupId, this can be safely ignored.
150-166: Consider logging failed group updates.
Promise.allSettledresults are discarded, so any failedeventCountupdates will go unnoticed. While this resilience pattern avoids blocking, silently ignoring failures could lead to staleeventCountvalues.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
📒 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.
This reverts commit b107c4d.
There was a problem hiding this 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 batchingfindMany/deletes if this can touch a large number of groupsRight now all matching
fraudEventGrouprows are loaded and then deleted in a single transaction based ongroupIds. If the number ofpartnerDuplicatePayoutMethodgroups witheventCount = 1is 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 aroundmain()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 redundantfraudEvent.deleteManycall—theonDelete: CascadeFK constraint handles deletion automaticallyThe
fraudEvent→fraudEventGrouprelation hasonDelete: Cascade, so the database automatically deletes childfraudEventrows when the parentfraudEventGroupis deleted. The explicitfraudEvent.deleteManycall 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
📒 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
There was a problem hiding this 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 largeINclauses and transactionsBuilding a single
groupIdsarray and then running twodeleteManycalls within: groupIdsinside one transaction is straightforward, but if there are many matching single‑event groups this can turn into a very largeINlist and a big interactive transaction, which may hit DB parameter limits or hurt performance.You might want to:
- Process
groupIdsin reasonably sized chunks (e.g., a few thousand ids per batch) and rundeleteManyper chunk, or- Keep the same filters but push more of the logic into the DB (e.g., multiple
deleteManycalls keyed by the original where condition rather than a hugeINset), 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
📒 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 solidThe
findManyquery scoped totype: "partnerDuplicatePayoutMethod"andeventCount: 1, combined with the minimalselectandfraudGroups.length === 0early return, makes this cleanup very targeted and avoids unnecessary DB work when there’s nothing to do. Theconsole.log/console.tableoutputs are also safe here since they only include ids and aggregate metadata.
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores / Migrations
Enhancements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.