-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Refactor cron routes to use withCron middleware #3238
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
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughTwo cron route handlers ( Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 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: 2
🧹 Nitpick comments (1)
apps/web/app/(ee)/api/cron/partners/ban/process/route.ts (1)
172-193: Empty catch block silently swallows email errors.While it's correct that email failures shouldn't fail the ban process, silently swallowing errors loses observability. Consider logging the error:
- } catch {} + } catch (error) { + console.error(`Failed to send ban notification email to ${partner.email}:`, error); + }Also, the non-null assertion on line 189 (
bannedReason!) is redundant since the ternary already handles the null case.- bannedReason: programEnrollment.bannedReason - ? BAN_PARTNER_REASONS[programEnrollment.bannedReason!] - : "", + bannedReason: programEnrollment.bannedReason + ? BAN_PARTNER_REASONS[programEnrollment.bannedReason] + : "",
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts(2 hunks)apps/web/app/(ee)/api/cron/partners/ban/process/route.ts(2 hunks)apps/web/lib/cron/with-cron.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3207
File: apps/web/lib/cron/with-cron.ts:27-56
Timestamp: 2025-12-09T12:54:41.818Z
Learning: In `apps/web/lib/cron/with-cron.ts`, the `withCron` wrapper extracts the request body once and provides it to handlers via the `rawBody` parameter. Handlers should use this `rawBody` string parameter (e.g., `JSON.parse(rawBody)`) rather than reading from the Request object via `req.json()` or `req.text()`.
Learnt from: devkiran
Repo: dubinc/dub PR: 3213
File: apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts:122-122
Timestamp: 2025-12-15T16:45:51.667Z
Learning: In the Dub codebase, cron endpoints under apps/web/app/(ee)/api/cron/ use handleCronErrorResponse for error handling, which intentionally does NOT detect QStash callbacks or set Upstash-NonRetryable-Error headers. This allows QStash to retry all cron job errors using its native retry mechanism. The selective retry logic (queueFailedRequestForRetry) is only used for specific user-facing API endpoints like /api/track/lead, /api/track/sale, and /api/links to retry only transient Prisma database errors.
📚 Learning: 2025-12-09T12:54:41.818Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3207
File: apps/web/lib/cron/with-cron.ts:27-56
Timestamp: 2025-12-09T12:54:41.818Z
Learning: In `apps/web/lib/cron/with-cron.ts`, the `withCron` wrapper extracts the request body once and provides it to handlers via the `rawBody` parameter. Handlers should use this `rawBody` string parameter (e.g., `JSON.parse(rawBody)`) rather than reading from the Request object via `req.json()` or `req.text()`.
Applied to files:
apps/web/lib/cron/with-cron.tsapps/web/app/(ee)/api/cron/auto-approve-partner/route.tsapps/web/app/(ee)/api/cron/partners/ban/process/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/cron/auto-approve-partner/route.tsapps/web/app/(ee)/api/cron/partners/ban/process/route.ts
📚 Learning: 2025-12-15T16:45:51.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3213
File: apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts:122-122
Timestamp: 2025-12-15T16:45:51.667Z
Learning: In cron endpoints under apps/web/app/(ee)/api/cron, continue using handleCronErrorResponse for error handling. Do not detect QStash callbacks or set Upstash-NonRetryable-Error headers in these cron routes, so QStash can retry cron errors via its native retry mechanism. The existing queueFailedRequestForRetry logic should remain limited to specific user-facing API endpoints (e.g., /api/track/lead, /api/track/sale, /api/links) to retry only transient Prisma/database errors. This pattern should apply to all cron endpoints under the cron directory in this codebase.
Applied to files:
apps/web/app/(ee)/api/cron/auto-approve-partner/route.tsapps/web/app/(ee)/api/cron/partners/ban/process/route.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/app/(ee)/api/cron/auto-approve-partner/route.tsapps/web/app/(ee)/api/cron/partners/ban/process/route.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/app/(ee)/api/cron/partners/ban/process/route.ts
📚 Learning: 2025-12-08T09:44:28.429Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3200
File: apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts:55-73
Timestamp: 2025-12-08T09:44:28.429Z
Learning: In apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts, the fraud event creation logic intentionally generates self-referential fraud events (where partnerId equals duplicatePartnerId) for partners with duplicate payout methods. This is by design to create raw events for all partners involved in a duplicate payout method scenario, regardless of whether they reference themselves.
Applied to files:
apps/web/app/(ee)/api/cron/partners/ban/process/route.ts
🧬 Code graph analysis (3)
apps/web/lib/cron/with-cron.ts (2)
apps/web/lib/cron/verify-vercel.ts (1)
verifyVercelSignature(3-20)apps/web/lib/axiom/server.ts (1)
logger(29-39)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (4)
apps/web/lib/cron/with-cron.ts (1)
withCron(21-75)packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/plan-capabilities.ts (1)
getPlanCapabilities(4-22)apps/web/lib/api/fraud/get-partner-application-risks.ts (1)
getPartnerApplicationRisks(11-68)
apps/web/app/(ee)/api/cron/partners/ban/process/route.ts (6)
apps/web/lib/cron/with-cron.ts (1)
withCron(21-75)apps/web/lib/api/programs/get-program-enrollment-or-throw.ts (1)
getProgramEnrollmentOrThrow(6-67)apps/web/app/(ee)/api/cron/partners/ban/process/cancel-commissions.ts (1)
cancelCommissions(4-71)apps/web/lib/api/partners/sync-total-commissions.ts (1)
syncTotalCommissions(5-50)apps/web/lib/tinybird/record-link.ts (1)
recordLink(75-86)apps/web/lib/api/fraud/create-fraud-events.ts (1)
createFraudEvents(13-160)
⏰ 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 (9)
apps/web/lib/cron/with-cron.ts (2)
39-48: LGTM!The explicit
else-ifstructure with a catch-all throw for unsupported HTTP methods is clean and provides clear error messaging. The thrown error will be properly caught and logged by the surrounding try/catch block.
56-72: LGTM!The error handling properly:
- Logs to console for immediate debugging
- Sends to Axiom for observability (with flush to ensure delivery)
- Uses the existing
logutility for cron-type logging- Returns a 500 status, allowing QStash to retry failed cron jobs per the established pattern
Based on learnings, this approach correctly allows QStash native retry mechanisms.
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (3)
18-19: LGTM!Correctly uses the
rawBodyparameter fromwithCronand parses it withJSON.parse()as per the established pattern in this codebase. This avoids the issue of reading the request body multiple times.Based on learnings, this is the correct approach for
withCronhandlers.
52-76: LGTM!The guard clauses handle expected skip conditions (missing enrollment, no group, auto-approval disabled, non-pending status) with descriptive log messages. Using
logAndRespondwithout a 500 status prevents unnecessary QStash retries for these valid skip scenarios.
78-93: LGTM!The fraud risk evaluation is correctly gated behind the
canManageFraudEventsplan capability check, and high-risk partners are appropriately skipped rather than auto-approved, allowing for manual review.apps/web/app/(ee)/api/cron/partners/ban/process/route.ts (4)
24-25: LGTM!Correctly uses the
withCronwrapper and parsesrawBodywithJSON.parse()as per the established pattern. Based on learnings, this is the correct approach.
55-106: LGTM!The transaction correctly groups related ban operations atomically:
- Links are disabled immediately via
disabledAtandexpiresAt- Only non-approved bounty submissions are rejected (preserving approved ones)
- Discount codes are unlinked (with actual deletion queued separately)
- Only pending payouts are canceled
The logging of counts after the transaction provides useful observability.
114-133: LGTM!The parallel execution of independent cleanup tasks is efficient:
- Commission sync updates totals after cancellation
- Link cache expiration ensures stale data isn't served
- Tinybird metadata is updated with deletion markers
- Discount code deletion is properly queued
The filter correctly handles links without discount codes.
135-157: LGTM!Correctly creates
partnerCrossProgramBanfraud events for other programs where the partner is enrolled and approved. This enables cross-program fraud detection as documented in the learnings about fraud rules.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.