import { Webhook } from "svix"; import type { Route } from "./+types/clerk"; import { findOrCreateUser, getUserDisplayName } from "~/models/user"; export async function action({ request }: Route.ActionArgs) { // Get the webhook secret from environment const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET; if (!WEBHOOK_SECRET) { throw new Error("Please add CLERK_WEBHOOK_SECRET to your .env file"); } // Get the headers const svix_id = request.headers.get("svix-id"); const svix_timestamp = request.headers.get("svix-timestamp"); const svix_signature = request.headers.get("svix-signature"); // If there are no headers, error out if (!svix_id || !svix_timestamp || !svix_signature) { return new Response("Error: Missing svix headers", { status: 400 }); } // Get the body const payload = await request.text(); // Create a new Svix instance with your webhook secret const wh = new Webhook(WEBHOOK_SECRET); let evt: { type: string; data: { id: string; email_addresses?: Array<{ email_address: string }>; username?: string; first_name?: string; last_name?: string; image_url?: string } }; // Verify the webhook try { evt = wh.verify(payload, { "svix-id": svix_id, "svix-timestamp": svix_timestamp, "svix-signature": svix_signature, }) as typeof evt; } catch (err) { console.error("Error verifying webhook:", err); return new Response("Error: Verification failed", { status: 400 }); } // Handle the webhook const eventType = evt.type; console.log(`Webhook received: ${eventType}`); if (eventType === "user.created" || eventType === "user.updated") { const { id, email_addresses, username, first_name, last_name, image_url } = evt.data; try { if (eventType === "user.created") { // Create new user const user = await findOrCreateUser({ id, emailAddresses: email_addresses?.map((e: { email_address: string }) => ({ emailAddress: e.email_address, })) || [], username, firstName: first_name, lastName: last_name, imageUrl: image_url, }); console.log( `User created in database: ${user.id} (${getUserDisplayName(user)})` ); } else { // Update existing user (or create if doesn't exist) const user = await findOrCreateUser({ id, emailAddresses: email_addresses?.map((e: { email_address: string }) => ({ emailAddress: e.email_address, })) || [], username, firstName: first_name, lastName: last_name, imageUrl: image_url, }); console.log( `User updated in database: ${user.id} (${getUserDisplayName(user)})` ); } } catch (error) { console.error(`Error handling ${eventType}:`, error); return new Response( `Error: Failed to ${eventType === "user.created" ? "create" : "update"} user`, { status: 500, } ); } } return new Response("Webhook processed successfully", { status: 200 }); }