124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
#!/usr/bin/env tsx
|
|
/**
|
|
* Test Resend Webhook Locally
|
|
*
|
|
* This script simulates a Resend webhook event to test your webhook handler.
|
|
*
|
|
* Usage:
|
|
* npx tsx scripts/test-resend-webhook.ts [emailId] [eventType]
|
|
*
|
|
* Examples:
|
|
* npx tsx scripts/test-resend-webhook.ts # Uses first email from DB
|
|
* npx tsx scripts/test-resend-webhook.ts abc123 delivered # Specific email and event
|
|
*/
|
|
|
|
import { prisma } from '../src/lib/db';
|
|
|
|
const BASE_URL = process.env.APP_URL || 'http://localhost:3001';
|
|
|
|
async function testWebhook() {
|
|
const args = process.argv.slice(2);
|
|
let emailId = args[0];
|
|
const eventType = args[1] || 'email.delivered';
|
|
|
|
console.log('🔍 Testing Resend Webhook Locally\n');
|
|
|
|
// If no emailId provided, get the most recent one from DB
|
|
if (!emailId) {
|
|
const recentEmail = await prisma.emailLog.findFirst({
|
|
where: { resendMessageId: { not: null } },
|
|
orderBy: { createdAt: 'desc' },
|
|
select: { id: true, resendMessageId: true, recipientEmail: true, status: true },
|
|
});
|
|
|
|
if (recentEmail?.resendMessageId) {
|
|
emailId = recentEmail.resendMessageId;
|
|
console.log(`📧 Found recent email:`);
|
|
console.log(` ID: ${recentEmail.id}`);
|
|
console.log(` Resend ID: ${recentEmail.resendMessageId}`);
|
|
console.log(` Recipient: ${recentEmail.recipientEmail}`);
|
|
console.log(` Current Status: ${recentEmail.status}\n`);
|
|
} else {
|
|
console.log('❌ No emails found in database with resendMessageId');
|
|
console.log(' Send an email first, then run this script again.\n');
|
|
|
|
// Show all emails for debugging
|
|
const allEmails = await prisma.emailLog.findMany({
|
|
take: 5,
|
|
orderBy: { createdAt: 'desc' },
|
|
select: { id: true, resendMessageId: true, recipientEmail: true, status: true, createdAt: true },
|
|
});
|
|
|
|
if (allEmails.length > 0) {
|
|
console.log('📋 Recent emails in database:');
|
|
allEmails.forEach(e => {
|
|
console.log(` - ${e.recipientEmail} | Status: ${e.status} | ResendID: ${e.resendMessageId || 'null'}`);
|
|
});
|
|
}
|
|
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Create webhook payload (simulating Resend's format)
|
|
const payload = {
|
|
type: eventType,
|
|
created_at: new Date().toISOString(),
|
|
data: {
|
|
email_id: emailId,
|
|
from: 'noreply@themoyos.co.za',
|
|
to: ['test@example.com'],
|
|
subject: 'Test Email',
|
|
created_at: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
console.log(`📤 Sending ${eventType} webhook for email: ${emailId}`);
|
|
console.log(` URL: ${BASE_URL}/api/webhooks/resend\n`);
|
|
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/webhooks/resend`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
// Note: Not including svix headers since we're testing without signature verification
|
|
// In production, Resend sends: svix-id, svix-timestamp, svix-signature
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let responseData;
|
|
try {
|
|
responseData = JSON.parse(responseText);
|
|
} catch {
|
|
responseData = responseText;
|
|
}
|
|
|
|
console.log(`📥 Response: ${response.status} ${response.statusText}`);
|
|
console.log(` Body:`, responseData);
|
|
|
|
if (response.ok) {
|
|
// Check if the status was updated in the database
|
|
const updatedEmail = await prisma.emailLog.findFirst({
|
|
where: { resendMessageId: emailId },
|
|
select: { id: true, status: true, lastEventAt: true },
|
|
});
|
|
|
|
if (updatedEmail) {
|
|
console.log(`\n✅ Email status updated:`);
|
|
console.log(` New Status: ${updatedEmail.status}`);
|
|
console.log(` Last Event: ${updatedEmail.lastEventAt}`);
|
|
}
|
|
} else {
|
|
console.log(`\n❌ Webhook failed`);
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Error calling webhook:', error);
|
|
}
|
|
|
|
}
|
|
|
|
// Run the test
|
|
testWebhook().catch(console.error);
|