145 lines
4.2 KiB
TypeScript
145 lines
4.2 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";
|
|
|
|
type EmailLogPreview = {
|
|
id: string;
|
|
resendMessageId: string | null;
|
|
recipientEmail: string;
|
|
status: string;
|
|
createdAt?: Date;
|
|
};
|
|
|
|
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: EmailLogPreview) => {
|
|
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: unknown;
|
|
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: unknown) {
|
|
console.error("❌ Error calling webhook:", error);
|
|
}
|
|
}
|
|
|
|
// Run the test
|
|
testWebhook().catch((err: unknown) => console.error(err)); |