import { Webhook } from "svix"; import { headers } from "next/headers"; import { WebhookEvent } from "@clerk/nextjs/server"; import prisma from "@/lib/prisma"; import { UserJSON, DeletedObjectJSON, OrganizationMembershipJSON, OrganizationJSON, } from "@clerk/types"; const webhookSecret = process.env.CLERK_WEBHOOK_SECRET || ``; async function validateRequest(request: Request) { const payloadString = await request.text(); const headerPayload = await headers(); const svixHeaders = { "svix-id": headerPayload.get("svix-id")!, "svix-timestamp": headerPayload.get("svix-timestamp")!, "svix-signature": headerPayload.get("svix-signature")!, }; const wh = new Webhook(webhookSecret); return wh.verify(payloadString, svixHeaders) as WebhookEvent; } async function handleUserCreated(userData: UserJSON) { await prisma.user.create({ data: { id: userData.id, clerkUserId: userData.id, email: userData.email_addresses?.[0]?.email_address, firstName: userData.first_name, lastName: userData.last_name, }, }); } async function handleUserUpdated(userData: UserJSON) { await prisma.user.update({ where: { clerkUserId: userData.id }, data: { email: userData.email_addresses?.[0]?.email_address, firstName: userData.first_name, lastName: userData.last_name, }, }); } async function handleUserDeleted(userData: DeletedObjectJSON) { await prisma.user.delete({ where: { clerkUserId: userData.id }, }); } async function handleOrgMembershipCreated( membershipData: OrganizationMembershipJSON ) { await prisma.organization.upsert({ where: { id: membershipData.organization.id }, create: { id: membershipData.organization.id, name: membershipData.organization.name, }, update: { name: membershipData.organization.name, }, }); await prisma.user.update({ where: { clerkUserId: membershipData.public_user_data.user_id }, data: { organizations: { connect: { id: membershipData.organization.id }, }, }, }); } async function handleOrgMembershipDeleted( membershipData: OrganizationMembershipJSON ) { await prisma.user.update({ where: { clerkUserId: membershipData.public_user_data.user_id }, data: { organizations: { disconnect: { id: membershipData.organization.id }, }, }, }); } async function handleOrganizationCreated(orgData: OrganizationJSON) { await prisma.organization.create({ data: { id: orgData.id, name: orgData.name, }, }); } async function handleOrganizationUpdated(orgData: OrganizationJSON) { await prisma.organization.update({ where: { id: orgData.id }, data: { name: orgData.name, }, }); } export async function POST(request: Request) { const payload = await validateRequest(request); const eventType = payload.type; switch (eventType) { case "user.created": await handleUserCreated(payload.data as unknown as UserJSON); break; case "user.updated": await handleUserUpdated(payload.data as unknown as UserJSON); break; case "user.deleted": await handleUserDeleted(payload.data as DeletedObjectJSON); break; case "organizationMembership.created": await handleOrgMembershipCreated( payload.data as OrganizationMembershipJSON ); break; case "organizationMembership.deleted": await handleOrgMembershipDeleted( payload.data as OrganizationMembershipJSON ); break; case "organization.created": await handleOrganizationCreated( payload.data as unknown as OrganizationJSON ); break; case "organization.updated": await handleOrganizationUpdated( payload.data as unknown as OrganizationJSON ); break; default: console.log(`Unhandled event type: ${eventType}`); } return new Response("ok", { status: 200 }); }