clerkauth
implemented, sync with db to be added
This commit is contained in:
1 parent
ca790a7b97
commit
3a58d420d6
49 files changed
+9423
-779
No files matched your search
@@ -0,0 +1,147 @@
|
||||
import { Webhook } from 'svix';
|
||||
import { headers } from 'next/headers';
|
||||
import { WebhookEvent } from '@clerk/nextjs/server';
|
||||
import { db } from '@fitai/database';
|
||||
import { users } from '@fitai/database/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function POST(req: Request) {
|
||||
// Get the webhook secret from environment variables
|
||||
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
|
||||
|
||||
if (!WEBHOOK_SECRET) {
|
||||
throw new Error('Please add CLERK_WEBHOOK_SECRET to your environment variables');
|
||||
}
|
||||
|
||||
// Get the headers
|
||||
const headerPayload = await headers();
|
||||
const svix_id = headerPayload.get('svix-id');
|
||||
const svix_timestamp = headerPayload.get('svix-timestamp');
|
||||
const svix_signature = headerPayload.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 req.json();
|
||||
const body = JSON.stringify(payload);
|
||||
|
||||
// Create a new Svix instance with your webhook secret
|
||||
const wh = new Webhook(WEBHOOK_SECRET);
|
||||
|
||||
let evt: WebhookEvent;
|
||||
|
||||
// Verify the webhook signature
|
||||
try {
|
||||
evt = wh.verify(body, {
|
||||
'svix-id': svix_id,
|
||||
'svix-timestamp': svix_timestamp,
|
||||
'svix-signature': svix_signature,
|
||||
}) as WebhookEvent;
|
||||
} 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(`Received webhook with ID ${evt.data.id} and type ${eventType}`);
|
||||
|
||||
try {
|
||||
switch (eventType) {
|
||||
case 'user.created': {
|
||||
const { id, email_addresses, first_name, last_name, public_metadata } = evt.data;
|
||||
|
||||
// Get primary email
|
||||
const primaryEmail = email_addresses.find(
|
||||
(email) => email.id === evt.data.primary_email_address_id
|
||||
);
|
||||
|
||||
if (!primaryEmail?.email_address) {
|
||||
console.error('No primary email found for user:', id);
|
||||
return new Response('Error: No primary email', { status: 400 });
|
||||
}
|
||||
|
||||
// Determine role from metadata or default to 'client'
|
||||
const role = (public_metadata?.role as 'admin' | 'trainer' | 'client') || 'client';
|
||||
|
||||
// Insert user into database
|
||||
await db.insert(users).values({
|
||||
id,
|
||||
email: primaryEmail.email_address,
|
||||
firstName: first_name || '',
|
||||
lastName: last_name || '',
|
||||
password: '', // Clerk handles authentication, no password needed
|
||||
role,
|
||||
phone: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
console.log(`✅ User ${id} created in database`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'user.updated': {
|
||||
const { id, email_addresses, first_name, last_name, public_metadata } = evt.data;
|
||||
|
||||
// Get primary email
|
||||
const primaryEmail = email_addresses.find(
|
||||
(email) => email.id === evt.data.primary_email_address_id
|
||||
);
|
||||
|
||||
if (!primaryEmail?.email_address) {
|
||||
console.error('No primary email found for user:', id);
|
||||
return new Response('Error: No primary email', { status: 400 });
|
||||
}
|
||||
|
||||
// Determine role from metadata
|
||||
const role = (public_metadata?.role as 'admin' | 'trainer' | 'client') || 'client';
|
||||
|
||||
// Update user in database
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
email: primaryEmail.email_address,
|
||||
firstName: first_name || '',
|
||||
lastName: last_name || '',
|
||||
role,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, id));
|
||||
|
||||
console.log(`✅ User ${id} updated in database`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'user.deleted': {
|
||||
const { id } = evt.data;
|
||||
|
||||
if (!id) {
|
||||
console.error('No user ID provided for deletion');
|
||||
return new Response('Error: No user ID', { status: 400 });
|
||||
}
|
||||
|
||||
// Delete user from database (cascade will handle related records)
|
||||
await db.delete(users).where(eq(users.id, id));
|
||||
|
||||
console.log(`✅ User ${id} deleted from database`);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unhandled webhook event type: ${eventType}`);
|
||||
}
|
||||
|
||||
return new Response('Webhook processed successfully', { status: 200 });
|
||||
} catch (error) {
|
||||
console.error('Error processing webhook:', error);
|
||||
return new Response('Error: Processing failed', { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user