Fraud alert#3706
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a fraud alert system: DB schema, admin APIs for listing and reviewing alerts, admin UI with filtering/pagination and review sheet, integration with partner ban/unban flows, Zod schemas, and auth handler update to pass session to admin routes. Changes
Sequence Diagram(s)sequenceDiagram
participant Admin as Admin
participant Page as Fraud Alerts Page
participant API as Admin API
participant DB as Database
participant Review as Review Sheet
Admin->>Page: Open fraud alerts page
Page->>API: GET /api/admin/fraud-alerts?status,programId,page
API->>DB: Query fraud alerts (+ count) with filters & pagination
DB-->>API: Return alerts + total
API-->>Page: Respond with { fraudAlerts, total }
Page-->>Admin: Render table & pagination
Admin->>Page: Click alert row
Page->>Review: Open ReviewFraudAlertSheet(alert)
Review->>API: GET /api/admin/partners/:partnerId
API->>DB: Query partner, payouts, commissions, fraudAlerts, enrollments
DB-->>API: Return partner context
API-->>Review: Return partner details
Review-->>Admin: Show context and actions
Admin->>Review: Submit review (confirm/dismiss + note)
Review->>API: PATCH /api/admin/fraud-alerts/:id (status, reviewNote)
API->>DB: Update alert(s) status & review metadata
API->>DB: (on confirm) Update other pending alerts for partner
API-->>Review: { success: true }
Review->>Page: trigger mutate() to refresh list
Review-->>Admin: Close sheet
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/lib/actions/partners/ban-partner.ts (1)
50-57:⚠️ Potential issue | 🟠 MajorAPI route
/api/partners/bandoes not forwardflagForFraudandfraudReasonto the action.The
banPartnerApiSchemaincludesflagForFraudandfraudReasonfields, but the route handler only destructurespartnerId,tenantId, andreasonfrom the parsed request body. These fields are not forwarded tobanPartner(), preventing API consumers from using the fraud flagging feature.Update line 14-15 to also destructure
flagForFraudandfraudReason, and pass them to thebanPartner()call on line 45.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/actions/partners/ban-partner.ts` around lines 50 - 57, The API route handler that parses the request body (using banPartnerApiSchema) currently only extracts partnerId, tenantId and reason; update that destructuring to also extract flagForFraud and fraudReason, and include those two values when invoking the banPartner(...) action so the call matches BanPartnerInput (i.e., pass flagForFraud and fraudReason along with partnerId, tenantId/ workspace, reason, user).
🧹 Nitpick comments (5)
apps/web/lib/zod/schemas/partners.ts (1)
904-915: Encode theflagForFraud→fraudReasondependency in the schema.Right now the conditional requirement is only enforced downstream. Adding it here keeps validation and API contract aligned.
Suggested schema refinement
-export const banPartnerSchema = z.object({ - workspaceId: z.string(), - partnerId: z.string(), - reason: z.enum( - Object.keys(BAN_PARTNER_REASONS) as [ - PartnerBannedReason, - ...PartnerBannedReason[], - ], - ), - flagForFraud: z.boolean().optional().default(false), - fraudReason: z.string().max(MAX_FRAUD_REASON_LENGTH).optional(), -}); +export const banPartnerSchema = z + .object({ + workspaceId: z.string(), + partnerId: z.string(), + reason: z.enum( + Object.keys(BAN_PARTNER_REASONS) as [ + PartnerBannedReason, + ...PartnerBannedReason[], + ], + ), + flagForFraud: z.boolean().optional().default(false), + fraudReason: z.string().max(MAX_FRAUD_REASON_LENGTH).optional(), + }) + .superRefine((data, ctx) => { + if (data.flagForFraud && !data.fraudReason?.trim()) { + ctx.addIssue({ + code: "custom", + path: ["fraudReason"], + message: "Fraud reason is required when flagging for fraud.", + }); + } + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/zod/schemas/partners.ts` around lines 904 - 915, The schema banPartnerSchema should encode the dependency that fraudReason is required when flagForFraud is true; update banPartnerSchema to validate this by adding a zod refinement (e.g., .refine or .superRefine) on the object so that when the parsed flagForFraud === true the fraudReason (bounded by MAX_FRAUD_REASON_LENGTH) must be present/non-empty, and when flagForFraud is false it remains optional (or forbidden) — reference the banPartnerSchema, flagForFraud, fraudReason, MAX_FRAUD_REASON_LENGTH, and PartnerBannedReason symbols to locate and implement the change.apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsx (1)
39-42: Consider parsing JSON error responses for better error messages.The API route returns
NextResponse.json({ success: true })on success, but error responses are plain text vianew Response(...). The current approach works, but if the API ever returns JSON errors with structured messages, this would display raw JSON.♻️ Optional: Parse JSON errors when applicable
if (!response.ok) { - const text = await response.text(); - throw new Error(text); + const text = await response.text(); + let message = text; + try { + const json = JSON.parse(text); + message = json.message || json.error || text; + } catch { + // Keep original text if not JSON + } + throw new Error(message); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsx around lines 39 - 42, The error handling currently reads response.text() and throws that raw string when !response.ok; update the block that checks response.ok (the code using the response variable in review-fraud-alert-menu.tsx) to attempt parsing JSON first and fall back to text: try response.json() and if it contains a message or error field include that in the thrown Error, otherwise use the raw text fallback. Ensure the thrown Error contains a clear description (e.g., "API error: <message>") so consumers get structured error info when the API returns JSON errors and still work for plain-text responses.apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsx (2)
55-69: Program filter options derived from current page data may be incomplete.The program filter options are derived from the current page's
fraudAlertsdata. This means:
- On page 2+, programs from page 1 won't appear in the filter
- If a filter is already applied, only programs matching that filter appear
Consider fetching all unique programs from a separate endpoint or the first API call without filters for a complete filter experience.
This is acceptable for MVP but may cause confusion when users don't see all programs in the filter dropdown.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsx around lines 55 - 69, The current programs list is computed from the paginated fraudAlerts inside the useMemo in page.tsx (programs, useMemo, fraudAlerts) which produces incomplete filter options; fix this by adding a separate data fetch for the full set of programs (e.g., a new fetchAllPrograms or getPrograms endpoint call) and populate the programs variable from that result instead of deriving from fraudAlerts, or call the existing API once without pagination/filters to retrieve all unique programs; implement the fetch in a useEffect (or a custom hook) that stores programs in state and use that state in place of the useMemo so the filter dropdown shows the complete set regardless of current page or applied fraudAlerts filters.
32-38: Consider adding a fallback to the Suspense boundary.The
Suspensewrapper has no fallback, so the component renders nothing briefly during initial load. While the table handles its own loading state, adding a minimal fallback could improve perceived performance.♻️ Optional: Add loading skeleton
export default function FraudAlertsPage() { return ( - <Suspense> + <Suspense fallback={<div className="p-6">Loading...</div>}> <FraudAlertsPageClient /> </Suspense> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsx around lines 32 - 38, The Suspense boundary in FraudAlertsPage wraps FraudAlertsPageClient without a fallback, causing a blank render during initial load; update the Suspense usage in FraudAlertsPage to include a minimal fallback (e.g., a small loading indicator or skeleton) so users see immediate feedback while FraudAlertsPageClient loads. Locate the Suspense wrapper around FraudAlertsPageClient in the FraudAlertsPage component and add an appropriate fallback prop (simple spinner, "Loading..." div, or a lightweight table skeleton) to improve perceived performance.apps/web/lib/actions/partners/ban-partner.ts (1)
142-152: Silent failure for fraud alert creation.The
prisma.fraudAlert.createoperation is insidePromise.allSettledwithinwaitUntil, meaning any errors will be silently swallowed. If the fraud alert creation fails (e.g., constraint violation), there's no indication to the user or logs.Consider logging failures for observability, since fraud alerts are important for the review workflow.
🔍 Optional: Add error logging
waitUntil( - Promise.allSettled([ + Promise.allSettled([ recordAuditLog({ ... }), queue.enqueueJSON({ ... }), flagForFraud && fraudReason - ? prisma.fraudAlert.create({ - data: { - partnerId, - programId, - reason: fraudReason, - }, - }) + ? prisma.fraudAlert.create({ + data: { + partnerId, + programId, + reason: fraudReason, + }, + }).catch((error) => { + console.error("Failed to create fraud alert:", error); + throw error; + }) : undefined, - ]), + ]).then((results) => { + results.forEach((result, index) => { + if (result.status === "rejected") { + console.error(`waitUntil operation ${index} failed:`, result.reason); + } + }); + }), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/actions/partners/ban-partner.ts` around lines 142 - 152, The fraud alert creation (prisma.fraudAlert.create) is being run inside Promise.allSettled within waitUntil so any rejection is silently ignored; update the code to surface failures by either wrapping the fraudAlert.create in its own try/catch that logs the error (including partnerId, programId and fraudReason) or by checking the Promise.allSettled result for the fraudAlert entry and logging any result.status === "rejected" with the rejection reason; reference the flagForFraud/fraudReason logic, prisma.fraudAlert.create, Promise.allSettled and waitUntil when making the change and use the project's error logger (e.g., logger.error) to record the failure for observability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/lib/zod/schemas/partners.ts`:
- Around line 917-920: banPartnerApiSchema now includes flagForFraud and
fraudReason but the route handler isn't passing them through; update the route
code where you call banPartner to parse those fields from parseRequestBody using
banPartnerApiSchema (extract partnerId, tenantId, reason, flagForFraud,
fraudReason) and include flagForFraud and fraudReason in the banPartner call so
the fraud flags are propagated to downstream logic; also confirm the banPartner
function signature accepts these fields and add them if missing.
In `@apps/web/ui/modals/ban-partner-modal.tsx`:
- Around line 163-177: When flagForFraud is true the fraudReason field is
registered without client-side validation; update the registration for
fraudReason (the register call in the BanPartnerModal component) to include
validation rules when flagForFraud is enabled (e.g., required: "Reason is
required", maxLength: { value: MAX_FRAUD_REASON_LENGTH, message: "Too long" })
and surface errors.fraudReason in the UI (add conditional error
styling/message). Ensure the conditional logic references flagForFraud and uses
the existing MAX_FRAUD_REASON_LENGTH and errors objects so invalid payloads are
blocked on the client before submit.
In `@packages/prisma/schema/fraud.prisma`:
- Around line 101-104: The FraudAlert model's programEnrollment relation
currently lacks an onDelete policy causing Restrict behavior; update the
relation definition for programEnrollment in the FraudAlert model to include
onDelete: Cascade (i.e., change the ProgramEnrollment relation line to add
onDelete: Cascade) so deletions of ProgramEnrollment cascade to FraudAlert
entries; reference the FraudAlert model and the programEnrollment relation (and
mirror the style used in FraudEventGroup's programEnrollment relation).
---
Outside diff comments:
In `@apps/web/lib/actions/partners/ban-partner.ts`:
- Around line 50-57: The API route handler that parses the request body (using
banPartnerApiSchema) currently only extracts partnerId, tenantId and reason;
update that destructuring to also extract flagForFraud and fraudReason, and
include those two values when invoking the banPartner(...) action so the call
matches BanPartnerInput (i.e., pass flagForFraud and fraudReason along with
partnerId, tenantId/ workspace, reason, user).
---
Nitpick comments:
In `@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsx:
- Around line 55-69: The current programs list is computed from the paginated
fraudAlerts inside the useMemo in page.tsx (programs, useMemo, fraudAlerts)
which produces incomplete filter options; fix this by adding a separate data
fetch for the full set of programs (e.g., a new fetchAllPrograms or getPrograms
endpoint call) and populate the programs variable from that result instead of
deriving from fraudAlerts, or call the existing API once without
pagination/filters to retrieve all unique programs; implement the fetch in a
useEffect (or a custom hook) that stores programs in state and use that state in
place of the useMemo so the filter dropdown shows the complete set regardless of
current page or applied fraudAlerts filters.
- Around line 32-38: The Suspense boundary in FraudAlertsPage wraps
FraudAlertsPageClient without a fallback, causing a blank render during initial
load; update the Suspense usage in FraudAlertsPage to include a minimal fallback
(e.g., a small loading indicator or skeleton) so users see immediate feedback
while FraudAlertsPageClient loads. Locate the Suspense wrapper around
FraudAlertsPageClient in the FraudAlertsPage component and add an appropriate
fallback prop (simple spinner, "Loading..." div, or a lightweight table
skeleton) to improve perceived performance.
In
`@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsx:
- Around line 39-42: The error handling currently reads response.text() and
throws that raw string when !response.ok; update the block that checks
response.ok (the code using the response variable in
review-fraud-alert-menu.tsx) to attempt parsing JSON first and fall back to
text: try response.json() and if it contains a message or error field include
that in the thrown Error, otherwise use the raw text fallback. Ensure the thrown
Error contains a clear description (e.g., "API error: <message>") so consumers
get structured error info when the API returns JSON errors and still work for
plain-text responses.
In `@apps/web/lib/actions/partners/ban-partner.ts`:
- Around line 142-152: The fraud alert creation (prisma.fraudAlert.create) is
being run inside Promise.allSettled within waitUntil so any rejection is
silently ignored; update the code to surface failures by either wrapping the
fraudAlert.create in its own try/catch that logs the error (including partnerId,
programId and fraudReason) or by checking the Promise.allSettled result for the
fraudAlert entry and logging any result.status === "rejected" with the rejection
reason; reference the flagForFraud/fraudReason logic, prisma.fraudAlert.create,
Promise.allSettled and waitUntil when making the change and use the project's
error logger (e.g., logger.error) to record the failure for observability.
In `@apps/web/lib/zod/schemas/partners.ts`:
- Around line 904-915: The schema banPartnerSchema should encode the dependency
that fraudReason is required when flagForFraud is true; update banPartnerSchema
to validate this by adding a zod refinement (e.g., .refine or .superRefine) on
the object so that when the parsed flagForFraud === true the fraudReason
(bounded by MAX_FRAUD_REASON_LENGTH) must be present/non-empty, and when
flagForFraud is false it remains optional (or forbidden) — reference the
banPartnerSchema, flagForFraud, fraudReason, MAX_FRAUD_REASON_LENGTH, and
PartnerBannedReason symbols to locate and implement the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 22acc688-2455-4374-9985-10137f58c4f9
📒 Files selected for processing (14)
apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsxapps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsxapps/web/app/(ee)/admin.dub.co/(dashboard)/layout-nav-client.tsxapps/web/app/(ee)/api/admin/fraud-alerts/[id]/route.tsapps/web/app/(ee)/api/admin/fraud-alerts/route.tsapps/web/lib/actions/partners/ban-partner.tsapps/web/lib/auth/admin.tsapps/web/lib/zod/schemas/fraud.tsapps/web/lib/zod/schemas/partners.tsapps/web/ui/modals/ban-partner-modal.tsxpackages/prisma/schema/fraud.prismapackages/prisma/schema/partner.prismapackages/prisma/schema/program.prismapackages/prisma/schema/schema.prisma
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/web/app/(ee)/api/admin/fraud-alerts/[id]/route.ts (1)
22-24: Unhandled Zod validation error returns 500 instead of 400.
reviewSchema.parse()throws aZodErroron invalid input, which will result in a 500 response. Consider usingsafeParseor wrapping in try-catch to return a proper 400 response with validation details.♻️ Proposed fix using safeParse
- const { status: newStatus, reviewNote } = reviewSchema.parse( - await req.json(), - ); + const body = await req.json(); + const parsed = reviewSchema.safeParse(body); + + if (!parsed.success) { + return new Response("Invalid request body.", { status: 400 }); + } + + const { status: newStatus, reviewNote } = parsed.data;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/api/admin/fraud-alerts/[id]/route.ts around lines 22 - 24, The current call reviewSchema.parse(await req.json()) can throw a ZodError and produce a 500; change it to use reviewSchema.safeParse(await req.json()) (or wrap parse in try/catch) and if validation fails return a 400 with the validation error details; update the code that reads const { status: newStatus, reviewNote } to only destructure after a successful safeParse (e.g., from result.data) and ensure the response for failures includes the Zod error messages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/app/`(ee)/api/admin/fraud-alerts/[id]/route.ts:
- Around line 22-24: The current call reviewSchema.parse(await req.json()) can
throw a ZodError and produce a 500; change it to use
reviewSchema.safeParse(await req.json()) (or wrap parse in try/catch) and if
validation fails return a 400 with the validation error details; update the code
that reads const { status: newStatus, reviewNote } to only destructure after a
successful safeParse (e.g., from result.data) and ensure the response for
failures includes the Zod error messages.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ed167989-e8c4-4726-aba1-cc79230621a6
📒 Files selected for processing (7)
apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsxapps/web/app/(ee)/api/admin/fraud-alerts/[id]/route.tsapps/web/app/(ee)/api/partners/ban/route.tsapps/web/lib/actions/partners/unban-partner.tsapps/web/lib/zod/schemas/partners.tsapps/web/ui/modals/ban-partner-modal.tsxpackages/prisma/schema/fraud.prisma
✅ Files skipped from review due to trivial changes (1)
- apps/web/ui/modals/ban-partner-modal.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/lib/zod/schemas/partners.ts
- apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-menu.tsx
- packages/prisma/schema/fraud.prisma
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/app/`(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsx:
- Around line 58-72: The programs list is being built from the paginated
fraudAlerts (useMemo computing programs from fraudAlerts), which makes the
dropdown only show programs on the current page; instead fetch or accept a
dedicated full-programs source and use that for the filter options. Replace the
current useMemo that derives programs from fraudAlerts with a call/prop to the
global programs list (e.g., usePrograms(), getAllPrograms(), or a programs prop
returned alongside the paginated alerts) and update any other similar
derivations in the block around the useMemo and lines referenced (the programs
variable and related filter-building code in the same component) to consume that
dedicated source so options are stable regardless of paging or current filters.
In `@apps/web/app/`(ee)/api/admin/fraud-alerts/[id]/route.ts:
- Around line 40-45: The current read-then-write flow in the confirm/reject
paths (in route.ts handling the fraud alert) can race; change to a single
guarded write inside a transaction so the update only succeeds if
fraudAlert.status === "pending" (e.g., use your ORM/DB transaction API to
perform the reporting findMany and the status update together, applying a
WHERE/status guard on the update). If the guarded update returns 0 rows
affected, return HTTP 409. Apply the same guarded transactional update for both
the confirm and reject handlers (the functions/blocks that currently check
fraudAlert.status !== "pending" and then update the record and audit fields) so
audit fields and enqueued reports are consistent.
In `@apps/web/app/`(ee)/api/admin/partners/[id]/route.ts:
- Around line 9-16: The route currently returns the full Partner row via
prisma.partner.findUnique and assigns it to partner, leaking sensitive
payout/identity fields; change the call to use an explicit select that only
fetches the fields the review sheet renders (e.g., id, name, email, status, any
display fields) and for related platforms use include or nested select to return
only platform fields needed; update both occurrences where
prisma.partner.findUnique (and the partner response usage around the other
occurrence mentioned) are used so the response object contains only those
selected fields before sending to the client.
- Around line 100-103: The platforms mapping currently only converts subscribers
and turns 0n into null and leaves posts and views as BigInt, causing JSON
serialization errors; update the partner.platforms mapping (the platforms:
partner.platforms.map((p) => ... ) block) to consistently serialize BigInt
fields by converting subscribers, posts, and views to strings when present
(e.g., if p.subscribers != null then p.subscribers.toString(), otherwise null)
so 0n becomes "0" and posts/views are also safe for JSON.stringify.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 769e18d8-8fdd-496c-b8ef-d21fd404793a
📒 Files selected for processing (5)
apps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/page.tsxapps/web/app/(ee)/admin.dub.co/(dashboard)/fraud-alerts/review-fraud-alert-sheet.tsxapps/web/app/(ee)/admin.dub.co/(dashboard)/layout-nav-client.tsxapps/web/app/(ee)/api/admin/fraud-alerts/[id]/route.tsapps/web/app/(ee)/api/admin/partners/[id]/route.ts
✅ Files skipped from review due to trivial changes (1)
- apps/web/app/(ee)/admin.dub.co/(dashboard)/layout-nav-client.tsx
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/web/app/(ee)/api/admin/partners/[id]/route.ts (1)
107-110:⚠️ Potential issue | 🔴 CriticalBigInt serialization incomplete—
postsandviewswill crashJSON.stringify.The previous review flagged this: only
subscribersis converted, butpostsandviewsfrom theplatformsrelation are alsoBigIntcolumns.NextResponse.json()will throw at runtime when these fields are present.🐛 Proposed fix to serialize all BigInt fields
platforms: partner.platforms.map((p) => ({ ...p, - subscribers: p.subscribers ? Number(p.subscribers) : null, + subscribers: p.subscribers != null ? p.subscribers.toString() : null, + posts: p.posts != null ? p.posts.toString() : null, + views: p.views != null ? p.views.toString() : null, })),Using
.toString()preserves precision for potentially large values that could exceedNumber.MAX_SAFE_INTEGER.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/api/admin/partners/[id]/route.ts around lines 107 - 110, The platforms mapping currently only converts subscribers to a Number, but posts and views are also BigInt and will break JSON serialization; update the mapping (the platforms: partner.platforms.map(...) block) to convert posts and views to string (e.g., p.posts ? p.posts.toString() : null and same for p.views) while preserving the existing handling for subscribers so NextResponse.json() can safely serialize the object.
🧹 Nitpick comments (1)
apps/web/app/(ee)/api/admin/partners/[id]/route.ts (1)
68-102: Consider adding pagination/limits to unbounded queries.The
payoutsandcommissionsqueries have notakelimit. For partners with extensive history, this could return very large datasets impacting response time and memory.Additionally, the
commissionsquery returns all fields viainclude, but the consumer (PartnerDetailtype) only needsid,earnings,currency,status,createdAt, andprogram. Consider usingselectto reduce payload size.♻️ Suggested improvement
prisma.commission.findMany({ where: { partnerId: id, }, - include: { - program: { - select: { - id: true, - name: true, - logo: true, + select: { + id: true, + earnings: true, + currency: true, + status: true, + createdAt: true, + program: { + select: { + id: true, + name: true, + logo: true, + }, }, }, orderBy: { createdAt: "desc", }, + take: 100, }),Apply similar
takelimit topayoutsquery as well.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/`(ee)/api/admin/partners/[id]/route.ts around lines 68 - 102, The payouts and commissions queries (prisma.payout.findMany and prisma.commission.findMany) are unbounded and the commissions include all fields unnecessarily for PartnerDetail; add a sensible limit (e.g., take: 50 or page params) to both queries and replace the commissions include with a select that returns only id, earnings, currency, status, createdAt and program (program: { select: { id, name, logo } }) so the payload is bounded and matches the PartnerDetail shape; if pagination is intended, wire in skip/take or cursor params instead of a hard limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/web/app/`(ee)/api/admin/partners/[id]/route.ts:
- Around line 107-110: The platforms mapping currently only converts subscribers
to a Number, but posts and views are also BigInt and will break JSON
serialization; update the mapping (the platforms: partner.platforms.map(...)
block) to convert posts and views to string (e.g., p.posts ? p.posts.toString()
: null and same for p.views) while preserving the existing handling for
subscribers so NextResponse.json() can safely serialize the object.
---
Nitpick comments:
In `@apps/web/app/`(ee)/api/admin/partners/[id]/route.ts:
- Around line 68-102: The payouts and commissions queries
(prisma.payout.findMany and prisma.commission.findMany) are unbounded and the
commissions include all fields unnecessarily for PartnerDetail; add a sensible
limit (e.g., take: 50 or page params) to both queries and replace the
commissions include with a select that returns only id, earnings, currency,
status, createdAt and program (program: { select: { id, name, logo } }) so the
payload is bounded and matches the PartnerDetail shape; if pagination is
intended, wire in skip/take or cursor params instead of a hard limit.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5bd05a2e-fe83-48ac-a4af-44bd021361a8
📒 Files selected for processing (2)
apps/web/app/(ee)/api/admin/fraud-alerts/[id]/route.tsapps/web/app/(ee)/api/admin/partners/[id]/route.ts
Summary by CodeRabbit