feat(broker): branded react-email template for mesh invite
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Replaces the plain-text invite email with a standalone react-email
template (apps/broker/src/emails/mesh-invitation.tsx) using
@react-email/components + Tailwind. Rendered on demand in
handleCliMeshInvite and sent as both HtmlBody and TextBody via
Postmark (or html+text via Resend).

Self-contained — no dependency on @turbostarter/email, i18n, or ui
packages. Adds react, react-dom, @react-email/components, @react-email/render
to broker deps. Enables tsconfig jsx: react-jsx and .tsx includes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-15 02:04:28 +01:00
parent 2f27a5eef4
commit 77ee1d0d80
5 changed files with 2331 additions and 1235 deletions

View File

@@ -5139,19 +5139,24 @@ async function handleCliMeshInvite(req: IncomingMessage, slug: string, res: Serv
const fromAddr = process.env.EMAIL_FROM ?? "noreply@claudemesh.com";
if (apiKey) {
try {
const subject = `You've been invited to the "${m.name}" mesh on claudemesh`;
const text = `You've been invited to join the "${m.name}" mesh on claudemesh.\n\nAccept the invite:\n${url}\n\nThis link expires on ${expiresAt.toISOString()}.\n\nIf you didn't expect this, ignore this email.`;
const { render } = await import("@react-email/render");
const { MeshInvitation } = await import("./emails/mesh-invitation");
const React = await import("react");
const subject = `You're invited to join "${m.name}" on claudemesh`;
const element = React.createElement(MeshInvitation, { meshName: m.name, inviteUrl: url, expiresAt: expiresAt.toISOString(), appBaseUrl: baseUrl });
const html = await render(element);
const text = await render(element, { plainText: true });
const res = process.env.POSTMARK_API_KEY
? await fetch("https://api.postmarkapp.com/email", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Postmark-Server-Token": apiKey },
body: JSON.stringify({ From: fromAddr, To: body.email, Subject: subject, TextBody: text }),
body: JSON.stringify({ From: fromAddr, To: body.email, Subject: subject, HtmlBody: html, TextBody: text, MessageStream: "outbound" }),
signal: AbortSignal.timeout(10_000),
})
: await fetch("https://api.resend.com/emails", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
body: JSON.stringify({ from: fromAddr, to: body.email, subject, text }),
body: JSON.stringify({ from: fromAddr, to: body.email, subject, html, text }),
signal: AbortSignal.timeout(10_000),
});
emailed = res.ok;