User Created Email
This commit is contained in:
parent
8a840152e4
commit
bd8231912c
5 changed files with 72 additions and 93 deletions
|
@ -1,52 +0,0 @@
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Container,
|
|
||||||
Head,
|
|
||||||
Heading,
|
|
||||||
Hr,
|
|
||||||
Html,
|
|
||||||
Img,
|
|
||||||
Preview,
|
|
||||||
Section,
|
|
||||||
Tailwind,
|
|
||||||
Text,
|
|
||||||
} from "@react-email/components";
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
interface GoodbyeTemplateProps {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseUrl = process.env.VERCEL_URL
|
|
||||||
? `https://${process.env.VERCEL_URL}`
|
|
||||||
: "http://localhost:3000";
|
|
||||||
|
|
||||||
export const Goodbye = ({ name }: GoodbyeTemplateProps) => (
|
|
||||||
<Html>
|
|
||||||
<Head />
|
|
||||||
<Preview>Sorry to see you go... 😭</Preview>
|
|
||||||
<Tailwind>
|
|
||||||
<Body className="bg-white my-auto mx-auto font-sans">
|
|
||||||
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] w-[465px]">
|
|
||||||
<Section className="mt-[32px]">
|
|
||||||
<Img
|
|
||||||
src={`${baseUrl}/logo.webp`}
|
|
||||||
width="40"
|
|
||||||
height="37"
|
|
||||||
alt={`Sprint Padawan Logo`}
|
|
||||||
className="my-0 mx-auto"
|
|
||||||
/>
|
|
||||||
</Section>
|
|
||||||
<Heading className="text-4xl">Farewell, {name}...</Heading>
|
|
||||||
<Text>{"Were sorry to see you go."}</Text>
|
|
||||||
<Text>
|
|
||||||
Your data has been deleted, including all room history, user data,
|
|
||||||
votes, etc.
|
|
||||||
</Text>
|
|
||||||
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
|
|
||||||
<Text>— The Sprint Padawan team</Text>
|
|
||||||
</Container>
|
|
||||||
</Body>
|
|
||||||
</Tailwind>
|
|
||||||
</Html>
|
|
||||||
);
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
Img,
|
Img,
|
||||||
Preview,
|
Preview,
|
||||||
Section,
|
Section,
|
||||||
Tailwind,
|
// Tailwind,
|
||||||
Text,
|
Text,
|
||||||
} from "@react-email/components";
|
} from "@react-email/components";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
@ -25,7 +25,7 @@ export const Welcome = ({ name }: WelcomeTemplateProps) => (
|
||||||
<Html>
|
<Html>
|
||||||
<Head />
|
<Head />
|
||||||
<Preview>🎉 Welcome to Sprint Padawan! 🎉</Preview>
|
<Preview>🎉 Welcome to Sprint Padawan! 🎉</Preview>
|
||||||
<Tailwind>
|
{/* <Tailwind> */}
|
||||||
<Body className="bg-white my-auto mx-auto font-sans">
|
<Body className="bg-white my-auto mx-auto font-sans">
|
||||||
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] w-[465px]">
|
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] w-[465px]">
|
||||||
<Section className="mt-[32px]">
|
<Section className="mt-[32px]">
|
||||||
|
@ -52,6 +52,6 @@ export const Welcome = ({ name }: WelcomeTemplateProps) => (
|
||||||
<Text>— The Sprint Padawan team</Text>
|
<Text>— The Sprint Padawan team</Text>
|
||||||
</Container>
|
</Container>
|
||||||
</Body>
|
</Body>
|
||||||
</Tailwind>
|
{/* </Tailwind> */}
|
||||||
</Html>
|
</Html>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,11 @@ import {
|
||||||
onUserCreatedHandler,
|
onUserCreatedHandler,
|
||||||
onUserDeletedHandler,
|
onUserDeletedHandler,
|
||||||
} from "~/server/webhookHelpers";
|
} from "~/server/webhookHelpers";
|
||||||
import { WebhookEventBodySchema, WebhookEvents } from "~/utils/types";
|
import {
|
||||||
|
WebhookEventBody,
|
||||||
|
WebhookEventBodySchema,
|
||||||
|
WebhookEvents,
|
||||||
|
} from "~/utils/types";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
runtime: "edge",
|
runtime: "edge",
|
||||||
|
@ -12,12 +16,17 @@ export const config = {
|
||||||
|
|
||||||
export default async function handler(req: NextRequest) {
|
export default async function handler(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const requestBody = WebhookEventBodySchema.parse(await req.json());
|
const eventBody = (await req.json()) as WebhookEventBody;
|
||||||
|
const { data, type } = WebhookEventBodySchema.parse(eventBody);
|
||||||
let success = false;
|
let success = false;
|
||||||
|
|
||||||
switch (requestBody.type) {
|
switch (type) {
|
||||||
case WebhookEvents.USER_CREATED:
|
case WebhookEvents.USER_CREATED:
|
||||||
success = await onUserCreatedHandler(requestBody.data.id);
|
success = await onUserCreatedHandler(
|
||||||
|
data.id,
|
||||||
|
`${data.first_name} ${data.last_name}`,
|
||||||
|
data.email_addresses?.map((email) => email.email_address) || []
|
||||||
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ result: "USER CREATED" },
|
{ result: "USER CREATED" },
|
||||||
|
@ -31,7 +40,7 @@ export default async function handler(req: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case WebhookEvents.USER_DELETED:
|
case WebhookEvents.USER_DELETED:
|
||||||
success = await onUserDeletedHandler(requestBody.data.id);
|
success = await onUserDeletedHandler(data.id);
|
||||||
|
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ result: "USER DELETED" },
|
{ result: "USER DELETED" },
|
||||||
|
@ -44,7 +53,8 @@ export default async function handler(req: NextRequest) {
|
||||||
{ status: 400, statusText: "INVALID WEBHOOK EVENT TYPE" }
|
{ status: 400, statusText: "INVALID WEBHOOK EVENT TYPE" }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ result: "INVALID WEBHOOK EVENT BODY" },
|
{ result: "INVALID WEBHOOK EVENT BODY" },
|
||||||
{ status: 400, statusText: "INVALID WEBHOOK EVENT BODY" }
|
{ status: 400, statusText: "INVALID WEBHOOK EVENT BODY" }
|
||||||
|
|
|
@ -2,6 +2,10 @@ import { eq } from "drizzle-orm";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { rooms } from "./schema";
|
import { rooms } from "./schema";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
import { Welcome } from "~/components/templates/Welcome";
|
||||||
|
import { Resend } from "resend";
|
||||||
|
|
||||||
|
const resend = new Resend(env.RESEND_API_KEY);
|
||||||
|
|
||||||
export const onUserDeletedHandler = async (userId: string) => {
|
export const onUserDeletedHandler = async (userId: string) => {
|
||||||
try {
|
try {
|
||||||
|
@ -13,7 +17,11 @@ export const onUserDeletedHandler = async (userId: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onUserCreatedHandler = async (userId: string) => {
|
export const onUserCreatedHandler = async (
|
||||||
|
userId: string,
|
||||||
|
userName: string,
|
||||||
|
userEmails: string[]
|
||||||
|
) => {
|
||||||
const userUpdateResponse = await fetch(
|
const userUpdateResponse = await fetch(
|
||||||
`https://api.clerk.com/v1/users/${userId}/metadata`,
|
`https://api.clerk.com/v1/users/${userId}/metadata`,
|
||||||
{
|
{
|
||||||
|
@ -33,5 +41,14 @@ export const onUserCreatedHandler = async (userId: string) => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
userEmails.forEach((userEmail) => {
|
||||||
|
void resend.sendEmail({
|
||||||
|
from: "no-reply@sprintpadawan.dev",
|
||||||
|
to: userEmail,
|
||||||
|
subject: "🎉 Welcome to Sprint Padawan! 🎉",
|
||||||
|
react: Welcome({ name: userName }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return userUpdateResponse.ok;
|
return userUpdateResponse.ok;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,14 +23,18 @@ export const WebhookEventBodySchema = z.object({
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
email_address: z.string().email(),
|
email_address: z.string().email(),
|
||||||
id: z.string(),
|
id: z.string().optional(),
|
||||||
verification: z.object({
|
verification: z
|
||||||
status: z.string(),
|
.object({
|
||||||
strategy: z.string(),
|
status: z.string().optional(),
|
||||||
}),
|
strategy: z.string().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
first_name: z.string().optional(),
|
||||||
|
last_name: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue