-
-
Notifications
You must be signed in to change notification settings - Fork 173
feat(newsletter): migrate to Beehiiv API for subscription management #1321
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
feat(newsletter): migrate to Beehiiv API for subscription management #1321
Conversation
|
@NiallJoeMaher is attempting to deploy a commit to the Codú Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. WalkthroughMigrates newsletter handling to Beehiiv: removes client signup UI and server Email API action, adds Next.js redirects to the external newsletter site, updates privacy/terms with newsletter details, and implements Beehiiv-based subscribe/unsubscribe/status checks with error/Sentry handling. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as Client (Browser)
participant Next as Next.js Server
participant Lib as server/lib/newsletter
participant Bee as Beehiiv API
participant Sentry as Sentry
Client->>Next: POST /newsletter/subscribe (email)
Note right of Next: Next.js route/action receives form data
Next->>Lib: call subscribe(email)
Lib->>Bee: POST /v2/subscriptions (email, reactivate_existing, send_welcome_email)
alt 200 OK
Bee-->>Lib: 200 OK
Lib-->>Next: { success }
Next-->>Client: 200 (subscribed)
else error (4xx/5xx)
Bee-->>Lib: error
Lib->>Sentry: capture error
Lib-->>Next: { error }
Next-->>Client: 500/4xx (error)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
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 (3)
app/(app)/(tsandcs)/privacy/page.mdx (1)
7-7: Consider using HTTPS for internal links.The link to Terms of Service uses
http://while other links in the codebase usehttps://. For consistency and security best practices, consider updating to HTTPS:-This Policy supplements and is governed by our [Terms of Service](http://www.codu.co/terms) ("Terms") and [Newsletter Terms of Use](http://www.codu.co/tou). +This Policy supplements and is governed by our [Terms of Service](https://www.codu.co/terms) ("Terms") and [Newsletter Terms of Use](https://www.codu.co/tou).server/lib/newsletter.ts (2)
70-98: Inconsistent error logging between subscribe and unsubscribe paths.The subscribe path (line 54) logs failures to Sentry before throwing, but the unsubscribe path throws errors without Sentry logging. Consider adding Sentry capture for consistency and better observability:
Suggested improvement
if (!getResponse.ok) { if (getResponse.status === 404) { return { message: "Successfully unsubscribed from the newsletter." }; } + const errorData = await getResponse.text(); + Sentry.captureMessage(`Beehiiv get subscription failed: ${errorData}`); throw new Error("Failed to find subscription"); } // ... if (updateResponse.ok) { return { message: "Successfully unsubscribed from the newsletter." }; } else { + const errorData = await updateResponse.text(); + Sentry.captureMessage(`Beehiiv unsubscribe failed: ${errorData}`); throw new Error("Failed to unsubscribe from the newsletter"); }
34-48: Consider adding request timeouts for external API calls.The fetch calls to Beehiiv have no timeout configured. If the Beehiiv API becomes slow or unresponsive, these calls could hang indefinitely. While the caller in
auth.tswraps this in try/catch so it won't block user creation, adding timeouts improves resilience:Example using AbortSignal timeout
const response = await fetch( `${BEEHIIV_API_BASE}/publications/${BEEHIIV_PUBLICATION_ID}/subscriptions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${BEEHIIV_API_KEY}`, }, body: JSON.stringify({ email, reactivate_existing: true, send_welcome_email: false, }), signal: AbortSignal.timeout(10000), // 10 second timeout }, );
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
app/(app)/(tsandcs)/privacy/page.mdxapp/(app)/(tsandcs)/tou/page.mdxapp/(standalone)/newsletter/_form.tsxapp/(standalone)/newsletter/actions.tsapp/(standalone)/newsletter/confirmed/page.tsxapp/(standalone)/newsletter/page.tsxapp/(standalone)/newsletter/unsubscribed/page.tsxnext.config.jsserver/lib/newsletter.ts
💤 Files with no reviewable changes (5)
- app/(standalone)/newsletter/page.tsx
- app/(standalone)/newsletter/unsubscribed/page.tsx
- app/(standalone)/newsletter/confirmed/page.tsx
- app/(standalone)/newsletter/_form.tsx
- app/(standalone)/newsletter/actions.ts
🧰 Additional context used
🧬 Code graph analysis (1)
server/lib/newsletter.ts (2)
app/(standalone)/newsletter/actions.ts (1)
subscribeToNewsletter(15-63)auth.ts (1)
createUser(85-115)
⏰ 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: e2e
🔇 Additional comments (6)
next.config.js (1)
30-34: Wildcard path is discarded in the redirect destination.The
:path*parameter is captured but not used in the destination URL. All sub-paths like/newsletter/archiveor/newsletter/confirmedwill redirect to the roothttps://newsletter.codu.coinstead of preserving the path.If this is intentional (e.g., the external newsletter service handles all routing from its root), this is fine. Otherwise, consider appending the path:
{ source: "/newsletter/:path*", - destination: "https://newsletter.codu.co", + destination: "https://newsletter.codu.co/:path*", permanent: true, },app/(app)/(tsandcs)/tou/page.mdx (1)
1-106: Well-structured Terms of Use document.The document comprehensively covers essential ToU sections including eligibility, intellectual property, disclaimers, and governing law. The 16-year age requirement aligns with GDPR requirements.
app/(app)/(tsandcs)/privacy/page.mdx (1)
95-106: Good addition documenting newsletter data collection.The new "Newsletter Subscriber Data" section clearly documents the data collected through Beehiiv, including engagement metrics and geographic location, with appropriate disclosure about the third-party platform and unsubscribe mechanism.
server/lib/newsletter.ts (3)
5-25: Clean type definitions and config helper.The interfaces properly model the Beehiiv API response structure, and
getBeehiivConfig()fails fast with a clear error message when required environment variables are missing.
33-56: Subscribe flow looks solid.Good use of
reactivate_existing: trueto handle re-subscription cases, and proper Sentry logging before throwing on failure. Thesend_welcome_email: falsealigns with the existing welcome email flow inauth.ts.
119-126: Verify subscription status handling logic.The function only returns
truefor"active"status. Based on the interface, subscriptions can also be"validating"or"pending".If a user is in
"validating"or"pending"state (e.g., double opt-in flow), this will returnfalse. Verify this is the intended behavior for your use case.Also, consider adding Sentry logging on line 125 for consistency with the subscribe path:
} else { + const errorData = await response.text(); + Sentry.captureMessage(`Beehiiv subscription check failed: ${errorData}`); throw new Error("Failed to check newsletter subscription"); }
…nd Newsletter Terms of Use
And update privacy policy