Skip to content

Conversation

@NiallJoeMaher
Copy link
Contributor

And update privacy policy

@NiallJoeMaher NiallJoeMaher requested a review from a team as a code owner January 2, 2026 13:59
@vercel
Copy link

vercel bot commented Jan 2, 2026

@NiallJoeMaher is attempting to deploy a commit to the Codú Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

Migrates 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

Cohort / File(s) Summary
Legal documentation
app/(app)/(tsandcs)/privacy/page.mdx, app/(app)/(tsandcs)/tou/page.mdx
Updated Privacy "Last updated" date and added "Newsletter Subscriber Data" subsection; added new Newsletter Terms of Use document and linked it from privacy.
Removed newsletter pages
app/(standalone)/newsletter/page.tsx, app/(standalone)/newsletter/confirmed/page.tsx, app/(standalone)/newsletter/unsubscribed/page.tsx
Deleted main newsletter page, confirmation page, and unsubscribed page along with their metadata and UI components.
Removed client form & server action
app/(standalone)/newsletter/_form.tsx, app/(standalone)/newsletter/actions.ts
Removed client-side SignupForm and the server-side subscribeToNewsletter action (previous Email API/Zod validation flow).
Redirects config
next.config.js
Added async redirects() returning permanent redirects from /newsletter and /newsletter/:path* to https://newsletter.codu.co.
Beehiiv integration (server)
server/lib/newsletter.ts
Replaced Email API logic with Beehiiv API calls: subscribe, unsubscribe, and check-by-email; added isUserSubscribedToNewsletter(email: string): Promise<boolean); added error handling and Sentry logging.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped from form to Beehiiv's door,
Bye-bye inputs I tended before.
Redirects guide the curious hare,
Terms and privacy now all fair.
A tiny hop, a bigger buzz—newsletter lore! ✨📬

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is largely incomplete and fails to follow the template structure, providing only a minimal two-sentence comment without required sections. Add proper description sections: fill 'Pull Request details' with comprehensive info about the migration, specify any breaking changes, and include 'Associated Screenshots' section even if marked 'None'.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: migrating newsletter subscription management to Beehiiv API, which aligns with the primary code changes across multiple files.

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2662a3d and 57e8819.

📒 Files selected for processing (2)
  • app/(app)/(tsandcs)/privacy/page.mdx
  • app/(app)/(tsandcs)/tou/page.mdx

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 use https://. 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.ts wraps 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

📥 Commits

Reviewing files that changed from the base of the PR and between ead81ed and 2662a3d.

📒 Files selected for processing (9)
  • app/(app)/(tsandcs)/privacy/page.mdx
  • app/(app)/(tsandcs)/tou/page.mdx
  • app/(standalone)/newsletter/_form.tsx
  • app/(standalone)/newsletter/actions.ts
  • app/(standalone)/newsletter/confirmed/page.tsx
  • app/(standalone)/newsletter/page.tsx
  • app/(standalone)/newsletter/unsubscribed/page.tsx
  • next.config.js
  • server/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/archive or /newsletter/confirmed will redirect to the root https://newsletter.codu.co instead 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: true to handle re-subscription cases, and proper Sentry logging before throwing on failure. The send_welcome_email: false aligns with the existing welcome email flow in auth.ts.


119-126: Verify subscription status handling logic.

The function only returns true for "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 return false. 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");
   }

@NiallJoeMaher NiallJoeMaher merged commit 1bdefc7 into codu-code:develop Jan 2, 2026
4 of 5 checks passed
@NiallJoeMaher NiallJoeMaher deleted the feat/newsletter-migration branch January 2, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant