-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Improve connect payout reminders #3256
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe PR refactors a partner payout reminder cron endpoint to use a unified GET/POST handler with Qstash-verified POST support, batch processing of pending payouts, queue-based email dispatching, and recursive batch scheduling for scalability. Changes
Sequence DiagramsequenceDiagram
participant Cron as Cron/Client
participant Handler as Handler
participant Verify as Verification
participant DB as Database
participant Queue as Email Queue
participant Qstash as Qstash
Cron->>Handler: GET/POST request
Handler->>Verify: Verify request (Vercel or Qstash)
alt Verification fails
Verify-->>Handler: Error
Handler-->>Cron: 401/403 response
else Verification succeeds
Verify-->>Handler: OK
Handler->>DB: Fetch pending payouts (BATCH_SIZE limit)
DB-->>Handler: Batch of pending partner-programs
loop For each partner in batch
Handler->>Queue: queueBatchEmail(ConnectPayoutReminder)
end
Handler->>DB: Update lastRemindedAt for batch
DB-->>Handler: Updated
Handler->>Handler: Check if more batches remain
alt More batches exist
Handler->>Qstash: Schedule next batch (re-invoke endpoint)
Qstash-->>Handler: Scheduled
Handler-->>Cron: "Next batch scheduled" message
else No more batches
Handler-->>Cron: "Complete" message
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used🧠 Learnings (4)📓 Common learnings📚 Learning: 2025-05-29T04:45:18.504ZApplied to files:
📚 Learning: 2025-12-09T12:54:41.818ZApplied to files:
📚 Learning: 2025-12-15T16:45:51.667ZApplied to files:
🔇 Additional comments (6)
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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/app/(ee)/api/cron/payouts/reminders/partners/route.ts (1)
129-134: Remove the non-null assertion onprogram.logoor provide a fallback value.The
logofield is nullable in the schema (String?). Usingprogram.logo!silently allows null to pass to the email template. Either filter programs without logos before pushing to the array, or provide a fallback likelogo: program.logo ?? "".
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(ee)/api/cron/payouts/reminders/partners/route.ts(3 hunks)apps/web/lib/email/email-templates-map.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
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:46:01.529Z
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.
Learnt from: steven-tey
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-06-25T21:20:59.837Z
Learning: In the Dub codebase, payout limit validation uses a two-stage pattern: server actions perform quick sanity checks (payoutsUsage > payoutsLimit) for immediate user feedback, while the cron job (/cron/payouts) performs authoritative validation (payoutsUsage + payoutAmount > payoutsLimit) with actual calculated amounts before processing. This design provides fast user feedback while ensuring accurate limit enforcement at transaction time.
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/lib/email/email-templates-map.ts
📚 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/app/(ee)/api/cron/payouts/reminders/partners/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/payouts/reminders/partners/route.ts
🔇 Additional comments (6)
apps/web/lib/email/email-templates-map.ts (1)
2-2: LGTM!The
ConnectPayoutRemindertemplate is correctly imported and registered in the email templates map, following the existing naming and ordering conventions.Also applies to: 14-14
apps/web/app/(ee)/api/cron/payouts/reminders/partners/route.ts (5)
19-29: LGTM!The dual verification approach (Vercel for scheduled GET, QStash for recursive POST) is correctly implemented, properly reading the raw body once for signature verification.
144-170: LGTM!The batch email queuing and timestamp update logic is correctly implemented. The order of operations (queue first, update timestamp after) is appropriate—if the update fails, the 3-day reminder window provides reasonable protection against duplicate emails.
172-194: Good use of recursive batch scheduling.The QStash-based recursive processing pattern handles large partner sets gracefully without timing out. The defensive error logging for missing
messageIdis appropriate.
207-208: LGTM!Exporting the handler as both GET and POST correctly supports both Vercel's scheduled cron invocation and QStash's recursive batch calls.
197-204: The error handling in this file is correct and follows the established pattern used by all cron endpoints in the codebase.handleAndReturnErrorResponseis the appropriate error handler for cron routes, not a function calledhandleCronErrorResponse.Regarding
error.message: While this assumeserroris an Error instance, this pattern is consistent across the codebase. Some newer cron endpoints use safer checks likeerror instanceof Error ? error.message : String(error), but this is an optional improvement, not a requirement specific to cron endpoints.Likely an incorrect or invalid review comment.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.