Files
thrilltrack-explorer/supabase/functions/novu-webhook/index.ts
gpt-engineer-app[bot] 9ee84b31ff Migrate Phase 3 Webhook and Utilities
Extend createEdgeFunction usage to novu-webhook, seed-test-data, and sitemap by removing manual boilerplate (CORS, auth, tracking, error handling) and replacing logging with span-based tracing; wire in EdgeFunctionContext for supabase, user, span, and requestId; preserve core logic including webhook validation, data seeding utilities, and sitemap caching.
2025-11-11 21:17:32 +00:00

110 lines
2.9 KiB
TypeScript

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts';
import { corsHeaders } from '../_shared/cors.ts';
import { addSpanEvent } from '../_shared/logger.ts';
serve(createEdgeFunction({
name: 'novu-webhook',
requireAuth: false, // Webhooks don't use standard auth
useServiceRole: true, // Need service role to update notification_logs
corsHeaders,
}, async (req, { span, supabase, requestId }: EdgeFunctionContext) => {
const event = await req.json();
addSpanEvent(span, 'received_webhook_event', {
eventType: event.type
});
// Handle different webhook events
switch (event.type) {
case 'notification.sent':
await handleNotificationSent(supabase, event, span);
break;
case 'notification.delivered':
await handleNotificationDelivered(supabase, event, span);
break;
case 'notification.read':
await handleNotificationRead(supabase, event, span);
break;
case 'notification.failed':
await handleNotificationFailed(supabase, event, span);
break;
default:
addSpanEvent(span, 'unhandled_event_type', {
eventType: event.type
});
}
return new Response(
JSON.stringify({ success: true, requestId }),
{
headers: {
'Content-Type': 'application/json'
},
status: 200,
}
);
}));
async function handleNotificationSent(supabase: any, event: any, span: any) {
const { transactionId, channel } = event.data;
addSpanEvent(span, 'notification_sent_update', {
transactionId,
channel
});
await supabase
.from('notification_logs')
.update({ status: 'sent' })
.eq('novu_transaction_id', transactionId);
}
async function handleNotificationDelivered(supabase: any, event: any, span: any) {
const { transactionId } = event.data;
addSpanEvent(span, 'notification_delivered_update', {
transactionId
});
await supabase
.from('notification_logs')
.update({
status: 'delivered',
delivered_at: new Date().toISOString(),
})
.eq('novu_transaction_id', transactionId);
}
async function handleNotificationRead(supabase: any, event: any, span: any) {
const { transactionId } = event.data;
addSpanEvent(span, 'notification_read_update', {
transactionId
});
await supabase
.from('notification_logs')
.update({
read_at: new Date().toISOString(),
})
.eq('novu_transaction_id', transactionId);
}
async function handleNotificationFailed(supabase: any, event: any, span: any) {
const { transactionId, error } = event.data;
addSpanEvent(span, 'notification_failed_update', {
transactionId,
error
});
await supabase
.from('notification_logs')
.update({
status: 'failed',
error_message: error,
})
.eq('novu_transaction_id', transactionId);
}