From 0d247d9fdd6c1ad9630236a520345b9c8cc0d72b Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 12:56:53 +0000 Subject: [PATCH] Refactor: Continue implementation plan --- src/lib/edgeFunctionTracking.ts | 4 +- src/lib/notificationService.ts | 55 ++++++++++------ supabase/functions/detect-location/index.ts | 26 +++++--- .../functions/manage-moderator-topic/index.ts | 25 ++++++-- .../functions/migrate-novu-users/index.ts | 32 ++++++++-- .../notify-moderators-report/index.ts | 24 ++++++- .../process-scheduled-deletions/index.ts | 40 ++++++++++-- .../functions/trigger-notification/index.ts | 25 ++++++-- .../functions/update-novu-subscriber/index.ts | 25 ++++++-- supabase/functions/validate-email/index.ts | 63 +++++++++++++++---- 10 files changed, 254 insertions(+), 65 deletions(-) diff --git a/src/lib/edgeFunctionTracking.ts b/src/lib/edgeFunctionTracking.ts index 6bf10ca5..22f6eda3 100644 --- a/src/lib/edgeFunctionTracking.ts +++ b/src/lib/edgeFunctionTracking.ts @@ -21,7 +21,7 @@ import { getErrorMessage } from './errorHandler'; */ export async function invokeWithTracking( functionName: string, - payload: Record = {}, + payload: any = {}, userId?: string, parentRequestId?: string, traceId?: string @@ -71,7 +71,7 @@ export async function invokeWithTracking( export async function invokeBatchWithTracking( operations: Array<{ functionName: string; - payload: Record; + payload: any; }>, userId?: string ): Promise< diff --git a/src/lib/notificationService.ts b/src/lib/notificationService.ts index 9418773a..a5af5836 100644 --- a/src/lib/notificationService.ts +++ b/src/lib/notificationService.ts @@ -1,4 +1,5 @@ import { supabase } from "@/integrations/supabase/client"; +import { invokeWithTracking } from "@/lib/edgeFunctionTracking"; import { logger } from "@/lib/logger"; import { AppError } from "@/lib/errorHandler"; import { z } from "zod"; @@ -53,14 +54,16 @@ class NotificationService { return { success: false, error: 'Novu not configured' }; } - const { data, error } = await supabase.functions.invoke('update-novu-subscriber', { - body: validated, - }); + const { data, error, requestId } = await invokeWithTracking( + 'update-novu-subscriber', + validated + ); if (error) { logger.error('Edge function error updating Novu subscriber', { action: 'update_novu_subscriber', userId: validated.subscriberId, + requestId, error: error.message }); throw new AppError( @@ -72,7 +75,8 @@ class NotificationService { logger.info('Novu subscriber updated successfully', { action: 'update_novu_subscriber', - userId: validated.subscriberId + userId: validated.subscriberId, + requestId }); return { success: true }; @@ -107,14 +111,16 @@ class NotificationService { return { success: false, error: 'Novu not configured' }; } - const { data, error } = await supabase.functions.invoke('create-novu-subscriber', { - body: validated, - }); + const { data, error, requestId } = await invokeWithTracking( + 'create-novu-subscriber', + validated + ); if (error) { logger.error('Edge function error creating Novu subscriber', { action: 'create_novu_subscriber', userId: validated.subscriberId, + requestId, error: error.message }); throw new AppError( @@ -151,7 +157,8 @@ class NotificationService { logger.info('Novu subscriber created successfully', { action: 'create_novu_subscriber', - userId: validated.subscriberId + userId: validated.subscriberId, + requestId }); return { success: true }; @@ -191,17 +198,19 @@ class NotificationService { // Update Novu preferences if enabled if (novuEnabled) { - const { error: novuError } = await supabase.functions.invoke('update-novu-preferences', { - body: { + const { error: novuError, requestId } = await invokeWithTracking( + 'update-novu-preferences', + { userId, preferences: validated, - }, - }); + } + ); if (novuError) { logger.error('Failed to update Novu preferences', { action: 'update_novu_preferences', userId, + requestId, error: novuError.message }); throw novuError; @@ -360,15 +369,17 @@ class NotificationService { return { success: false, error: 'Novu not configured' }; } - const { data, error } = await supabase.functions.invoke('trigger-notification', { - body: payload - }); + const { data, error, requestId } = await invokeWithTracking( + 'trigger-notification', + payload + ); if (error) { logger.error('Failed to trigger notification', { action: 'trigger_notification', workflowId: payload.workflowId, subscriberId: payload.subscriberId, + requestId, error: error.message }); throw error; @@ -378,7 +389,8 @@ class NotificationService { action: 'trigger_notification', workflowId: payload.workflowId, subscriberId: payload.subscriberId, - transactionId: data?.transactionId + transactionId: data?.transactionId, + requestId }); return { success: true }; @@ -407,14 +419,16 @@ class NotificationService { action: string; }): Promise { try { - const { error } = await supabase.functions.invoke('notify-moderators-submission', { - body: payload - }); + const { error, requestId } = await invokeWithTracking( + 'notify-moderators-submission', + payload + ); if (error) { logger.error('Failed to notify moderators', { action: 'notify_moderators', submissionId: payload.submission_id, + requestId, error: error.message }); throw error; @@ -422,7 +436,8 @@ class NotificationService { logger.info('Moderators notified successfully', { action: 'notify_moderators', - submissionId: payload.submission_id + submissionId: payload.submission_id, + requestId }); } catch (error) { logger.error('Error notifying moderators', { diff --git a/supabase/functions/detect-location/index.ts b/supabase/functions/detect-location/index.ts index 2fc9a90b..18a56217 100644 --- a/supabase/functions/detect-location/index.ts +++ b/supabase/functions/detect-location/index.ts @@ -1,8 +1,9 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; interface IPLocationResponse { @@ -181,6 +182,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('detect-location'); + try { // Get the client's IP address const forwarded = req.headers.get('x-forwarded-for'); @@ -252,15 +255,19 @@ serve(async (req) => { console.log('[Location] Location detected:', { country: result.country, countryCode: result.countryCode, - measurementSystem: result.measurementSystem + measurementSystem: result.measurementSystem, + requestId: tracking.requestId }); + endRequest(tracking, 200); + return new Response( - JSON.stringify(result), + JSON.stringify({ ...result, requestId: tracking.requestId }), { headers: { ...corsHeaders, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId } } ); @@ -273,9 +280,12 @@ serve(async (req) => { console.error('[Location Detection Error]', { error: errorMessage, stack: errorStack, - hasIP: true // IP removed for PII protection + hasIP: true, // IP removed for PII protection + requestId: tracking.requestId }); + endRequest(tracking, 500, errorMessage); + // Return default (metric) with 500 status to indicate error occurred // This allows proper error monitoring while still providing fallback data const defaultResult: IPLocationResponse = { @@ -288,12 +298,14 @@ serve(async (req) => { JSON.stringify({ ...defaultResult, error: errorMessage, - fallback: true + fallback: true, + requestId: tracking.requestId }), { headers: { ...corsHeaders, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId }, status: 500 } diff --git a/supabase/functions/manage-moderator-topic/index.ts b/supabase/functions/manage-moderator-topic/index.ts index 6b33190b..5a99ae2e 100644 --- a/supabase/functions/manage-moderator-topic/index.ts +++ b/supabase/functions/manage-moderator-topic/index.ts @@ -1,10 +1,11 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; import { Novu } from "npm:@novu/api@1.6.0"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; const TOPICS = { @@ -17,6 +18,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('manage-moderator-topic'); + try { const novuApiKey = Deno.env.get('NOVU_API_KEY'); if (!novuApiKey) { @@ -35,7 +38,7 @@ serve(async (req) => { throw new Error('Action must be either "add" or "remove"'); } - console.log(`${action === 'add' ? 'Adding' : 'Removing'} user ${userId} ${action === 'add' ? 'to' : 'from'} moderator topics`); + console.log(`${action === 'add' ? 'Adding' : 'Removing'} user ${userId} ${action === 'add' ? 'to' : 'from'} moderator topics`, { requestId: tracking.requestId }); const topics = [TOPICS.MODERATION_SUBMISSIONS, TOPICS.MODERATION_REPORTS]; const results = []; @@ -70,28 +73,42 @@ serve(async (req) => { const allSuccess = results.every(r => r.success); + endRequest(tracking, allSuccess ? 200 : 207); + return new Response( JSON.stringify({ success: allSuccess, userId, action, results, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: allSuccess ? 200 : 207, // 207 = Multi-Status (partial success) } ); } catch (error: any) { console.error('Error managing moderator topic:', error); + endRequest(tracking, 500, error.message); + return new Response( JSON.stringify({ success: false, error: error.message, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); diff --git a/supabase/functions/migrate-novu-users/index.ts b/supabase/functions/migrate-novu-users/index.ts index ad91a608..f58a9382 100644 --- a/supabase/functions/migrate-novu-users/index.ts +++ b/supabase/functions/migrate-novu-users/index.ts @@ -1,10 +1,11 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; import { Novu } from "npm:@novu/api@1.6.0"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; serve(async (req) => { @@ -12,6 +13,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('migrate-novu-users'); + try { const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; @@ -53,14 +56,21 @@ serve(async (req) => { if (profilesError) throw profilesError; if (!profiles || profiles.length === 0) { + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, message: 'No users to migrate', results: [], + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); @@ -127,27 +137,41 @@ serve(async (req) => { await new Promise(resolve => setTimeout(resolve, 100)); } + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, total: profiles.length, results, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); } catch (error: any) { console.error('Error migrating Novu users:', error); + endRequest(tracking, 500, error.message); + return new Response( JSON.stringify({ success: false, error: error.message, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); diff --git a/supabase/functions/notify-moderators-report/index.ts b/supabase/functions/notify-moderators-report/index.ts index 5d9922aa..3d55c726 100644 --- a/supabase/functions/notify-moderators-report/index.ts +++ b/supabase/functions/notify-moderators-report/index.ts @@ -1,9 +1,10 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; interface NotificationPayload { @@ -22,6 +23,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('notify-moderators-report'); + try { const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; @@ -34,6 +37,7 @@ serve(async (req) => { reportId: payload.reportId, reportType: payload.reportType, reportedEntityType: payload.reportedEntityType, + requestId: tracking.requestId }); // Calculate relative time @@ -127,27 +131,41 @@ serve(async (req) => { console.log('Notification triggered successfully:', result); + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, transactionId: result?.transactionId, payload: notificationPayload, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); } catch (error: any) { console.error('Error in notify-moderators-report:', error); + endRequest(tracking, 500, error.message); + return new Response( JSON.stringify({ success: false, error: error.message, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); diff --git a/supabase/functions/process-scheduled-deletions/index.ts b/supabase/functions/process-scheduled-deletions/index.ts index f8d5f390..24516d1f 100644 --- a/supabase/functions/process-scheduled-deletions/index.ts +++ b/supabase/functions/process-scheduled-deletions/index.ts @@ -1,9 +1,10 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; serve(async (req) => { @@ -11,6 +12,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('process-scheduled-deletions'); + try { // Use service role for admin operations const supabaseAdmin = createClient( @@ -18,7 +21,7 @@ serve(async (req) => { Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' ); - console.log('Processing scheduled account deletions...'); + console.log('Processing scheduled account deletions...', { requestId: tracking.requestId }); // Find confirmed deletion requests that are past their scheduled date const { data: pendingDeletions, error: fetchError } = await supabaseAdmin @@ -33,15 +36,23 @@ serve(async (req) => { if (!pendingDeletions || pendingDeletions.length === 0) { console.log('No deletions to process'); + + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, message: 'No deletions to process', processed: 0, + requestId: tracking.requestId }), { status: 200, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } @@ -170,25 +181,42 @@ serve(async (req) => { console.log(`Processed ${successCount} deletion(s) successfully, ${errorCount} error(s)`); + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, message: `Processed ${successCount} deletion(s)`, processed: successCount, errors: errorCount, + requestId: tracking.requestId }), { status: 200, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } catch (error) { console.error('Error processing scheduled deletions:', error); + + endRequest(tracking, 500, error.message); + return new Response( - JSON.stringify({ error: error.message }), + JSON.stringify({ + error: error.message, + requestId: tracking.requestId + }), { status: 500, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } diff --git a/supabase/functions/trigger-notification/index.ts b/supabase/functions/trigger-notification/index.ts index dd6935ca..275fde1e 100644 --- a/supabase/functions/trigger-notification/index.ts +++ b/supabase/functions/trigger-notification/index.ts @@ -1,9 +1,10 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { Novu } from "npm:@novu/api@1.6.0"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; serve(async (req) => { @@ -11,6 +12,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('trigger-notification'); + try { const novuApiKey = Deno.env.get('NOVU_API_KEY'); @@ -39,7 +42,7 @@ serve(async (req) => { ? { subscriberId } : { topicKey: topicKey! }; - console.log('Triggering notification:', { workflowId, recipient }); + console.log('Triggering notification:', { workflowId, recipient, requestId: tracking.requestId }); const result = await novu.trigger({ to: recipient, @@ -50,13 +53,20 @@ serve(async (req) => { console.log('Notification triggered successfully:', result.data); + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, transactionId: result.data.transactionId, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); @@ -64,13 +74,20 @@ serve(async (req) => { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; console.error('Error triggering notification:', errorMessage); + endRequest(tracking, 500, errorMessage); + return new Response( JSON.stringify({ success: false, error: errorMessage, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); diff --git a/supabase/functions/update-novu-subscriber/index.ts b/supabase/functions/update-novu-subscriber/index.ts index 01877ce7..45a74055 100644 --- a/supabase/functions/update-novu-subscriber/index.ts +++ b/supabase/functions/update-novu-subscriber/index.ts @@ -1,9 +1,10 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { Novu } from "npm:@novu/api@1.6.0"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; serve(async (req) => { @@ -11,6 +12,8 @@ serve(async (req) => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('update-novu-subscriber'); + try { const novuApiKey = Deno.env.get('NOVU_API_KEY'); @@ -32,7 +35,7 @@ serve(async (req) => { data?: Record; }; - console.log('Updating Novu subscriber:', { subscriberId, email, firstName }); + console.log('Updating Novu subscriber:', { subscriberId, email, firstName, requestId: tracking.requestId }); const subscriber = await novu.subscribers.update(subscriberId, { email, @@ -45,13 +48,20 @@ serve(async (req) => { console.log('Subscriber updated successfully:', subscriber.data); + endRequest(tracking, 200); + return new Response( JSON.stringify({ success: true, subscriberId: subscriber.data._id, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); @@ -59,13 +69,20 @@ serve(async (req) => { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; console.error('Error updating Novu subscriber:', errorMessage); + endRequest(tracking, 500, errorMessage); + return new Response( JSON.stringify({ success: false, error: errorMessage, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); diff --git a/supabase/functions/validate-email/index.ts b/supabase/functions/validate-email/index.ts index fb30c592..ed1c7c57 100644 --- a/supabase/functions/validate-email/index.ts +++ b/supabase/functions/validate-email/index.ts @@ -1,8 +1,9 @@ import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; +import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; // Comprehensive list of disposable email domains @@ -74,18 +75,27 @@ const handler = async (req: Request): Promise => { return new Response(null, { headers: corsHeaders }); } + const tracking = startRequest('validate-email'); + try { const { email }: ValidateEmailRequest = await req.json(); if (!email || typeof email !== 'string') { + endRequest(tracking, 400, 'Email address is required'); + return new Response( JSON.stringify({ valid: false, - reason: 'Email address is required' + reason: 'Email address is required', + requestId: tracking.requestId } as ValidationResult), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' } + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } } ); } @@ -93,14 +103,21 @@ const handler = async (req: Request): Promise => { // Basic email format validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { + endRequest(tracking, 400, 'Invalid email format'); + return new Response( JSON.stringify({ valid: false, - reason: 'Invalid email format' + reason: 'Invalid email format', + requestId: tracking.requestId } as ValidationResult), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' } + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } } ); } @@ -111,6 +128,9 @@ const handler = async (req: Request): Promise => { // Check if domain is disposable if (DISPOSABLE_DOMAINS.has(domain)) { console.log(`Blocked disposable email domain: ${domain}`); + + endRequest(tracking, 400, 'Disposable email domain blocked'); + return new Response( JSON.stringify({ valid: false, @@ -119,37 +139,58 @@ const handler = async (req: Request): Promise => { 'Use a personal email (Gmail, Outlook, Yahoo, etc.)', 'Use your work or school email address', 'Use an email from your own domain' - ] + ], + requestId: tracking.requestId } as ValidationResult), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' } + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } } ); } // Email is valid console.log(`Email validated successfully: ${email}`); + + endRequest(tracking, 200); + return new Response( JSON.stringify({ - valid: true + valid: true, + requestId: tracking.requestId } as ValidationResult), { status: 200, - headers: { ...corsHeaders, 'Content-Type': 'application/json' } + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } } ); } catch (error: any) { console.error('Error in validate-email function:', error); + + endRequest(tracking, 500, error.message); + return new Response( JSON.stringify({ valid: false, - reason: 'Internal server error during email validation' + reason: 'Internal server error during email validation', + requestId: tracking.requestId } as ValidationResult), { status: 500, - headers: { ...corsHeaders, 'Content-Type': 'application/json' } + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } } ); }