Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/dashboard/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const env = () =>

WORKOS_API_KEY: z.string().optional(),
WORKOS_CLIENT_ID: z.string().optional(),
WORKOS_WEBHOOK_SECRET: z.string().optional(),
NEXT_PUBLIC_WORKOS_REDIRECT_URI: z
.string()
.default("http://localhost:3000/auth/sso-callback"),
Expand Down
3 changes: 3 additions & 0 deletions apps/dashboard/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export default async function (req: NextRequest, _evt: NextFetchEvent) {
"/auth/oauth-sign-in",
"/auth/join",
"/favicon.ico",
"/api/webhooks/stripe",
"/api/v1/workos/webhooks",
"/api/v1/github/verify",
"/_next",
],
})(req);
Expand Down
111 changes: 0 additions & 111 deletions apps/dashboard/pages/api/v1/clerk/webhooks.ts

This file was deleted.

102 changes: 102 additions & 0 deletions apps/dashboard/pages/api/v1/workos/webhooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { WorkOS } from '@workos-inc/node';
import { env } from "@/lib/env";
import { Resend } from "@unkey/resend";
import freeDomains from "free-email-domains";
import type { NextApiRequest, NextApiResponse } from 'next';

export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const payload = req.body;
const sigHeader = req.headers['workos-signature'] as string | undefined;
const { RESEND_API_KEY, RESEND_AUDIENCE_ID, WORKOS_API_KEY, WORKOS_WEBHOOK_SECRET } = env();
if (!WORKOS_API_KEY || !WORKOS_WEBHOOK_SECRET) {
return res.status(400).json({ Error: "Missing environment variables" });
}

console.info("payload",payload);
console.info(req.headers);

if (!payload || !sigHeader) {
return res.status(400).json({ Error: "Nope" });
}
const workos = new WorkOS(WORKOS_API_KEY);

const webhook = await workos.webhooks.constructEvent({
payload: payload,
sigHeader: sigHeader,
secret: WORKOS_WEBHOOK_SECRET,
})

if (!webhook) {
return res.status(400).json({ Error: "Invalid payload" });
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated


Comment thread
perkinsjr marked this conversation as resolved.
if (webhook.event === "user.created") {
const webhookData = webhook.data;

const resend = new Resend({ apiKey: RESEND_API_KEY! });
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (!webhookData.email) {
return res.status(400).json({ Error: "No email address found" });
}
try {
await alertSlack(webhookData.email);
await resend.client.contacts.create({
audienceId: RESEND_AUDIENCE_ID!,
email: webhookData.email,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
});
await resend.sendWelcomeEmail({
email: webhookData.email,
});
return res.status(200).json({});
} catch (err) {
return res.status(400).json({
error: (err as Error).message,
});
}
}
return res.status(200).json({});
}};


async function alertSlack(email: string): Promise<void> {
const url = process.env.SLACK_WEBHOOK_URL_SIGNUP;
if (!url) {
return;
}
const domain = email.split("@").at(-1);
if (!domain) {
return;
}
if (freeDomains.includes(domain)) {
return;
}

await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text: `${email} signed up`,
blocks: [
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `${email} signed up`,
},
{
type: "mrkdwn",
text: `<https://${domain}>`,
},
],
},
],
}),
}).catch((err: Error) => {
console.error(err);
});
}