-
Notifications
You must be signed in to change notification settings - Fork 2.8k
send email notifications on email domain status changes #3300
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.
|
📝 WalkthroughWalkthroughReplaces standalone cron route handlers with withCron-wrapped handlers for email-domain update and verify endpoints; removes QStash signature checks; adds per-domain verification loops, status-change detection, and conditional owner notifications; introduces an EmailDomainStatusChanged email template and removes an explicit return-type annotation from InvalidDomain. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Cron as Cron Scheduler
participant Handler as withCron Handler
participant DB as Prisma (emailDomain)
participant Resend as Resend API
participant Mailer as Email service
Cron->>Handler: trigger verify job
Handler->>DB: findMany(emailDomain where resendDomainId != null, take 10)
loop per domain
Handler->>DB: fetch latest domain record
Handler->>Resend: verify domain (resend.domains.verify)
alt verification returns status
Resend-->>Handler: status result
Handler->>DB: update status, lastChecked
alt status changed
Handler->>DB: get workspace owners
DB-->>Handler: owners list
Handler->>Mailer: sendBatchEmail(EmailDomainStatusChanged)
Mailer-->>Handler: send result
else no change
Handler-->>Handler: log no notification
end
else Resend API error
Resend-->>Handler: error
Handler-->>Handler: log per-domain failure
end
end
Handler-->>Cron: complete
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used🧠 Learnings (3)📓 Common learnings📚 Learning: 2025-12-09T12:54:41.818ZApplied to files:
📚 Learning: 2025-12-15T16:45:51.667ZApplied to files:
🧬 Code graph analysis (2)packages/email/src/templates/email-domain-status-changed.tsx (2)
apps/web/app/(ee)/api/cron/email-domains/verify/route.ts (4)
⏰ 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)
🔇 Additional comments (9)
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 (2)
apps/web/app/(ee)/api/cron/email-domains/verify/route.ts (1)
56-58: Consider logging the error when fetching domain from Resend fails.Silently returning on error makes debugging harder. A log would help diagnose issues with specific domains.
🔎 Suggested improvement
if (error) { + console.error(`Failed to fetch domain ${domain.slug} from Resend:`, error); return; }packages/email/src/templates/email-domain-status-changed.tsx (1)
96-118: Pending status shows both generic and pending-specific messages.When
isPendingis true, the template displays the generic "status changed from X to Y" message (lines 97-109) followed by the pending-specific message (lines 113-117). This might be intentional, but could feel redundant to users.Consider consolidating into the main conditional:
🔎 Possible consolidation
- ) : ( + ) : isPending ? ( + <Text className="text-sm leading-6 text-black"> + Your email domain{" "} + <code className="text-purple-600">{domain}</code> for your Dub + workspace{" "} + <Link + href={`https://app.dub.co/${workspace.slug}`} + className="font-medium text-blue-600 no-underline" + > + {workspace.name}↗ + </Link>{" "} + is pending verification. This process may take a few minutes. + You'll receive another notification once verification is + complete. + </Text> + ) : ( <Text className="text-sm leading-6 text-black"> ... </Text> )} - - {isPending && ( - <Text className="text-sm leading-6 text-black"> - We're still verifying your domain... - </Text> - )}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/(ee)/api/cron/email-domains/update/route.tsapps/web/app/(ee)/api/cron/email-domains/verify/route.tspackages/email/src/templates/email-domain-status-changed.tsxpackages/email/src/templates/invalid-domain.tsx
🧰 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.
📚 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/email-domains/verify/route.tsapps/web/app/(ee)/api/cron/email-domains/update/route.ts
📚 Learning: 2025-11-17T05:19:11.972Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3113
File: apps/web/app/(ee)/api/cron/payouts/charge-succeeded/send-paypal-payouts.ts:65-75
Timestamp: 2025-11-17T05:19:11.972Z
Learning: In the Dub codebase, `sendBatchEmail` (implemented in packages/email/src/send-via-resend.ts) handles filtering of emails with invalid `to` addresses internally. Call sites can safely use non-null assertions on email addresses because the email sending layer will filter out any entries with null/undefined `to` values before sending. This centralized validation pattern is intentional and removes the need for filtering at individual call sites.
Applied to files:
apps/web/app/(ee)/api/cron/email-domains/verify/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/email-domains/verify/route.tsapps/web/app/(ee)/api/cron/email-domains/update/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/email-domains/update/route.ts (3)
apps/web/app/(ee)/api/email-domains/route.ts (1)
POST(39-143)apps/web/lib/cron/with-cron.ts (1)
withCron(21-75)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
🔇 Additional comments (7)
packages/email/src/templates/invalid-domain.tsx (1)
27-27: LGTM!Removing the explicit
JSX.Elementreturn type annotation is fine—TypeScript will correctly infer it.apps/web/app/(ee)/api/cron/email-domains/update/route.ts (2)
15-16: Correct usage ofwithCronandrawBodypattern.The implementation correctly uses
rawBodyfrom thewithCronwrapper and parses it with the zod schema. Based on learnings, this is the expected pattern for QStash-triggered cron endpoints.
49-51: Appropriate error handling for QStash retries.Throwing on Resend API failure allows QStash to retry transient errors, which aligns with the cron endpoint patterns in this codebase. Based on learnings, this is the correct approach.
apps/web/app/(ee)/api/cron/email-domains/verify/route.ts (3)
35-41: Good resilient batch processing pattern.Individual try/catch blocks prevent one domain's failure from blocking others, and updating
lastCheckedensures fair round-robin processing across all domains.
98-121: Email notification flow looks correct.The subject line appropriately reflects the status change, and using
sendBatchEmailwith the notification preference filter ensures only interested owners receive emails. Based on learnings,sendBatchEmailhandles filtering of invalid email addresses internally.
110-113: Thedomain.statusandupdatedDomain.statusare already properly typed. The Prisma schema defines thestatusfield asEmailDomainStatusenum, which matches the TypeScript type used inEmailDomainStatusChanged. Type safety is maintained at both the database and application levels.Likely an incorrect or invalid review comment.
packages/email/src/templates/email-domain-status-changed.tsx (1)
17-36: Well-structured template with proper type exports.The
EmailDomainStatustype export enables type safety for consumers, and the default prop values support email previewing during development. Structure is consistent with other email templates in the codebase.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Summary by CodeRabbit
New Features
Bug Fixes
Style
✏️ Tip: You can customize this high-level summary in your review settings.