-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Update Resend audience setup #2462
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 Git ↗︎
|
WalkthroughA new API route for sending delayed welcome emails and managing user subscriptions was introduced. Several modules were refactored to centralize and defer welcome email logic, utilize audience mappings, and run post-processing tasks concurrently. Email subscription logic now supports multiple audiences, and a script was added to query partner users without projects. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant AuthSystem
participant QStash
participant WelcomeAPI
participant DB
participant EmailService
User->>AuthSystem: Sign up
AuthSystem->>QStash: Schedule welcome workflow (15 min delay)
QStash-->>WelcomeAPI: POST /api/cron/welcome-user (after delay)
WelcomeAPI->>DB: Fetch user details
alt User found and has email
WelcomeAPI->>EmailService: Subscribe user to audience(s)
alt User is workspace owner
WelcomeAPI->>EmailService: Send welcome email
end
end
WelcomeAPI-->>QStash: 200 OK (confirmation or skip)
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Pull Request Overview
This PR updates the Resend audience setup to support multiple audiences by introducing the RESEND_AUDIENCES constant, and refactors the subscribe and unsubscribe flows accordingly. Key changes include:
- Importing and utilizing the RESEND_AUDIENCES constant in both subscribe and unsubscribe modules.
- Adding support for selecting the appropriate audience in subscribe functions.
- Updating various API endpoints and partner onboarding flows to use the new setup.
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/email/src/resend/unsubscribe.ts | Updated unsubscribe logic to use RESEND_AUDIENCES and a revised error check. |
| packages/email/src/resend/subscribe.ts | Refactored subscribe method to accept an audience parameter and use RESEND_AUDIENCES. |
| packages/email/src/resend/index.ts | Introduced the RESEND_AUDIENCES constant. |
| apps/web/scripts/update-subscribers.ts | Minor updates importing dotenv-flow; no major functional change. |
| apps/web/lib/auth/options.ts | Updated the welcome flow, replacing direct subscribe/sendEmail calls with qstash publishing. |
| apps/web/lib/actions/partners/onboard-partner.ts | Added a subscribe call for partners when onboarding. |
| apps/web/app/api/workspaces/route.ts | Integrated subscribe to assign default workspaces; updated Promise.allSettled usage. |
| apps/web/app/(ee)/api/cron/welcome-user/route.ts | Enhanced the welcome-user API to subscribe users based on their account type. |
Comments suppressed due to low confidence (1)
apps/web/app/api/workspaces/route.ts:147
- [nitpick] Using the '&&' operator directly within the array passed to Promise.allSettled may reduce code clarity by introducing non-promise values. Consider restructuring this logic to explicitly create promises based on conditionals for better readability.
session.user["defaultWorkspace"] === null && prisma.user.update({
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: 3
🧹 Nitpick comments (3)
packages/email/src/resend/index.ts (1)
52-55: Good centralization of audience management.The
RESEND_AUDIENCESconstant centralizes audience ID management, which improves maintainability. However, consider making these audience IDs configurable via environment variables for better flexibility across different environments.Consider this approach for environment-based configuration:
export const RESEND_AUDIENCES = { - "app.dub.co": "f5ff0661-4234-43f6-b0ca-a3f3682934e3", - "partners.dub.co": "6caf6898-941a-45b6-a59f-d0780c3004ac", + "app.dub.co": process.env.RESEND_APP_AUDIENCE_ID || "f5ff0661-4234-43f6-b0ca-a3f3682934e3", + "partners.dub.co": process.env.RESEND_PARTNERS_AUDIENCE_ID || "6caf6898-941a-45b6-a59f-d0780c3004ac", };apps/web/scripts/update-subscribers.ts (1)
2-2: Clarify the purpose of commented-out imports.The commented-out imports suggest this script might be intended for future batch subscription operations. Consider adding a comment explaining the script's intended purpose or implementing the functionality if it's needed.
Would you like me to help implement the batch subscription functionality using the commented-out imports, or should these be removed if not needed?
apps/web/lib/auth/options.ts (1)
529-529: Consider adding error handling for QStash publish operation.While the operation is wrapped in
waitUntilandPromise.allSettled, consider adding specific error handling for the QStash publish operation to ensure failed welcome email scheduling is logged appropriately.qstash.publishJSON({ url: `${APP_DOMAIN_WITH_NGROK}/api/cron/welcome-user`, // trigger welcome workflow 15 minutes after the user signed up delay: 15 * 60, body: { userId: user.id }, - }), + }).catch((error) => { + console.error('Failed to schedule welcome email:', error); + }),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/app/(ee)/api/cron/welcome-user/route.ts(1 hunks)apps/web/app/api/workspaces/route.ts(2 hunks)apps/web/lib/actions/partners/onboard-partner.ts(3 hunks)apps/web/lib/auth/options.ts(2 hunks)apps/web/scripts/update-subscribers.ts(1 hunks)packages/email/src/resend/index.ts(1 hunks)packages/email/src/resend/subscribe.ts(1 hunks)packages/email/src/resend/unsubscribe.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
apps/web/app/api/workspaces/route.ts (1)
packages/email/src/resend/subscribe.ts (1)
subscribe(3-27)
packages/email/src/resend/unsubscribe.ts (1)
packages/email/src/resend/index.ts (2)
resend(4-6)RESEND_AUDIENCES(52-55)
packages/email/src/resend/subscribe.ts (1)
packages/email/src/resend/index.ts (2)
RESEND_AUDIENCES(52-55)resend(4-6)
apps/web/lib/auth/options.ts (1)
packages/utils/src/constants/main.ts (1)
APP_DOMAIN_WITH_NGROK(20-25)
apps/web/lib/actions/partners/onboard-partner.ts (2)
apps/web/lib/partners/complete-program-applications.ts (1)
completeProgramApplications(11-130)packages/email/src/resend/subscribe.ts (1)
subscribe(3-27)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (20)
packages/email/src/resend/unsubscribe.ts (2)
1-1: Good alignment with centralized audience management.The import of
RESEND_AUDIENCESaligns well with the centralized approach introduced in the index file.
12-15:Details
✅ Verification successful
Verify removal of optional chaining is safe.
The optional chaining operator was removed from
resend.contacts.remove(). Ensure thatresendis guaranteed to be non-null at this point, given the check at line 4.
🏁 Script executed:
#!/bin/bash # Description: Verify usage patterns of the unsubscribe function to ensure resend is always available # Expected: All calls to unsubscribe should be after resend availability checks rg -A 10 -B 5 "unsubscribe\(" --type tsLength of output: 6908
Optional chaining removal is safe
The
unsubscribefunction includes an early-return guard (if (!resend) return;), so by the time we reachresend.contacts.remove(...),resendis guaranteed non-null. No further changes are needed.apps/web/lib/auth/options.ts (2)
20-22: Good addition of required imports for deferred processing.The imports for
APP_DOMAIN_WITH_NGROKandqstashare appropriate for the new deferred welcome email approach.
528-534:Details
✅ Verification successful
Excellent architectural improvement with deferred welcome emails.
The shift from immediate to deferred welcome email processing using QStash is a good architectural decision that:
- Prevents blocking the sign-in flow
- Allows for more sophisticated welcome workflows
- Provides better error isolation
The 15-minute delay seems reasonable for onboarding workflows.
Verify that the
/api/cron/welcome-userendpoint exists and properly handles the payload:
🏁 Script executed:
#!/bin/bash # Description: Verify the welcome-user API route exists and check its implementation # Expected: Should find the route file and verify it handles the userId payload fd "welcome-user" --type f --extension ts --extension js ast-grep --pattern 'export async function POST($_) { $$$ }'Length of output: 68965
Verified
/api/cron/welcome-userendpoint and payload handling
- Confirmed in
apps/web/app/api/cron/welcome-user/route.ts
- Exports
POST(req: Request)- Calls
verifyQstashSignature, parsesrawBody, and extractsuserId- Fetches the user and early-returns on missing user or email
- Triggers partner and workspace Owner welcome emails based on user data
All verification steps pass—no further action required.
packages/email/src/resend/subscribe.ts (4)
1-1: LGTM: Clean import of new dependenciesThe import statements correctly bring in the
resendclient andRESEND_AUDIENCESmapping needed for the refactored subscription logic.
6-10: LGTM: Well-typed audience parameter with sensible defaultThe function signature enhancement is well-designed:
- Optional
audienceparameter with good default value- Proper TypeScript typing using
keyof typeof RESEND_AUDIENCES- Maintains backward compatibility for existing callers
12-15: LGTM: Improved error checking logicThe error checking has been appropriately updated to check for the
resendclient existence rather than the environment variable. This is more accurate since the client encapsulates the API key dependency.
17-26: LGTM: Clean audience ID resolutionThe audience ID resolution using the mapping is clean and the contact creation logic remains unchanged, ensuring consistency with the existing behavior.
apps/web/app/api/workspaces/route.ts (2)
9-9: LGTM: Appropriate import for subscription functionalityThe import correctly brings in the updated
subscribefunction needed for user subscription logic.
146-165: LGTM: Well-structured concurrent post-creation operationsThe implementation is excellent:
- Uses
waitUntilappropriately for non-blocking async operationsPromise.allSettledensures both operations run concurrently without failing if one errors- Conditional logic for default workspace update makes sense
- Subscription to "app.dub.co" audience is appropriate for workspace owners
- Proper parameter passing with graceful handling of null name
The approach aligns well with the overall refactoring to centralize subscription logic.
apps/web/lib/actions/partners/onboard-partner.ts (3)
7-7: LGTM: Appropriate import for subscription functionalityThe import correctly brings in the updated
subscribefunction for partner subscription logic.
68-93: LGTM: Maintains existing partner creation logicThe partner creation/update logic remains unchanged, which is appropriate since this change focuses on post-creation subscription handling.
95-106: LGTM: Well-implemented concurrent post-onboarding operationsThe implementation follows the same excellent pattern as the workspace route:
- Uses
waitUntilfor non-blocking async operationsPromise.allSettledensures both operations run concurrently- Maintains existing program application completion logic
- Adds appropriate subscription to "partners.dub.co" audience
- Smart name resolution using
user.name || partner.name || undefinedThe approach is consistent with the overall refactoring strategy.
apps/web/app/(ee)/api/cron/welcome-user/route.ts (7)
1-8: LGTM: Appropriate imports and configurationThe imports are correct for the cron job functionality, and
dynamic = "force-dynamic"is appropriate for a cron endpoint that should not be cached.
10-13: LGTM: Clear documentation of route purposeThe comment clearly explains the route's purpose and timing, which is helpful for maintainability.
14-31: LGTM: Proper request handling and user lookupThe implementation correctly:
- Verifies QStash signature for security
- Parses the request body to extract userId
- Fetches user with appropriate related data (partners, projects)
- Selects only needed fields for efficiency
33-40: LGTM: Appropriate early returns for missing dataThe early returns for missing user or email are appropriate and use 200 status codes since these are expected scenarios that shouldn't be retried.
42-43: LGTM: Clear role determination logicThe boolean flags for user roles are clear and easy to understand.
45-70: LGTM: Well-structured subscription and email logicThe implementation is excellent:
- Uses
Promise.allfor concurrent operations- Conditional subscriptions based on user roles
- Appropriate audience selection for each role
- Proper email template usage
- Good parameter passing with fallback handling
The TODO comment for partners welcome email is noted and reasonable.
73-76: LGTM: Proper error handlingThe error handling uses the centralized error handler, which is consistent with the application's error handling patterns.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Summary by CodeRabbit
New Features
Improvements
Bug Fixes