Initial push
This commit is contained in:
53
lib/email/index.ts
Normal file
53
lib/email/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Resend } from "resend";
|
||||
import { render } from "@react-email/render";
|
||||
import { InvitationEmail } from "./templates/invitation";
|
||||
|
||||
function getResend(): Resend {
|
||||
const key = process.env.RESEND_API_KEY;
|
||||
if (!key) throw new Error("RESEND_API_KEY is not configured");
|
||||
return new Resend(key);
|
||||
}
|
||||
|
||||
export async function sendMagicLinkEmail({
|
||||
email,
|
||||
url,
|
||||
token: _token,
|
||||
}: {
|
||||
email: string;
|
||||
url: string;
|
||||
token: string;
|
||||
}): Promise<void> {
|
||||
const { error } = await getResend().emails.send({
|
||||
from: process.env.EMAIL_FROM ?? "CubeAdmin <noreply@example.com>",
|
||||
to: email,
|
||||
subject: "Your CubeAdmin sign-in link",
|
||||
html: `<p>Click the link below to sign in to CubeAdmin. This link expires in 1 hour.</p><p><a href="${url}">${url}</a></p>`,
|
||||
});
|
||||
|
||||
if (error) throw new Error(`Failed to send magic link email: ${error.message}`);
|
||||
}
|
||||
|
||||
export async function sendInvitationEmail({
|
||||
to,
|
||||
invitedByName,
|
||||
inviteUrl,
|
||||
role,
|
||||
}: {
|
||||
to: string;
|
||||
invitedByName: string;
|
||||
inviteUrl: string;
|
||||
role: string;
|
||||
}): Promise<void> {
|
||||
const html = await render(
|
||||
InvitationEmail({ invitedByName, inviteUrl, role }),
|
||||
);
|
||||
|
||||
const { error } = await getResend().emails.send({
|
||||
from: process.env.EMAIL_FROM ?? "CubeAdmin <noreply@example.com>",
|
||||
to,
|
||||
subject: `You've been invited to CubeAdmin`,
|
||||
html,
|
||||
});
|
||||
|
||||
if (error) throw new Error(`Failed to send email: ${error.message}`);
|
||||
}
|
||||
78
lib/email/templates/invitation.tsx
Normal file
78
lib/email/templates/invitation.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
Tailwind,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface InvitationEmailProps {
|
||||
invitedByName: string;
|
||||
inviteUrl: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export function InvitationEmail({
|
||||
invitedByName,
|
||||
inviteUrl,
|
||||
role,
|
||||
}: InvitationEmailProps) {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>
|
||||
{invitedByName} invited you to manage a Minecraft server on CubeAdmin
|
||||
</Preview>
|
||||
<Tailwind>
|
||||
<Body className="bg-zinc-950 font-sans">
|
||||
<Container className="mx-auto max-w-lg py-12 px-6">
|
||||
{/* Logo */}
|
||||
<Section className="mb-8 text-center">
|
||||
<Heading className="text-2xl font-bold text-emerald-500 m-0">
|
||||
⬛ CubeAdmin
|
||||
</Heading>
|
||||
</Section>
|
||||
|
||||
{/* Card */}
|
||||
<Section className="bg-zinc-900 rounded-xl border border-zinc-800 p-8">
|
||||
<Heading className="text-xl font-semibold text-white mt-0 mb-2">
|
||||
You've been invited
|
||||
</Heading>
|
||||
<Text className="text-zinc-400 mt-0 mb-6">
|
||||
<strong className="text-white">{invitedByName}</strong> has
|
||||
invited you to join CubeAdmin as a{" "}
|
||||
<strong className="text-emerald-400">{role}</strong>. Click the
|
||||
button below to create your account and start managing the
|
||||
Minecraft server.
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
href={inviteUrl}
|
||||
className="bg-emerald-600 hover:bg-emerald-500 text-white font-semibold rounded-lg px-6 py-3 text-sm no-underline inline-block"
|
||||
>
|
||||
Accept Invitation
|
||||
</Button>
|
||||
|
||||
<Text className="text-zinc-500 text-xs mt-6 mb-0">
|
||||
This invitation link expires in 48 hours. If you didn't
|
||||
expect this email, you can safely ignore it.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Text className="text-zinc-600 text-xs text-center mt-6">
|
||||
CubeAdmin — Minecraft Server Management
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
|
||||
export default InvitationEmail;
|
||||
Reference in New Issue
Block a user