diff --git a/supabase/functions/cancel-email-change/index.ts b/supabase/functions/cancel-email-change/index.ts index ec3feda4..3cd1d0ee 100644 --- a/supabase/functions/cancel-email-change/index.ts +++ b/supabase/functions/cancel-email-change/index.ts @@ -1,4 +1,5 @@ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -6,16 +7,28 @@ const corsHeaders = { }; Deno.serve(async (req) => { + const tracking = startRequest(); + // Handle CORS preflight requests if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { // Get the user from the authorization header const authHeader = req.headers.get('Authorization'); if (!authHeader) { - console.error('Missing authorization header'); + const duration = endRequest(tracking); + edgeLogger.error('Missing authorization header', { + action: 'cancel_email_change', + requestId: tracking.requestId, + duration + }); throw new Error('No authorization header provided. Please ensure you are logged in.'); } @@ -49,12 +62,22 @@ Deno.serve(async (req) => { const { data: { user }, error: authError } = await supabaseAdmin.auth.getUser(token); if (authError || !user) { - console.error('Auth verification failed:', authError); + const duration = endRequest(tracking); + edgeLogger.error('Auth verification failed', { + action: 'cancel_email_change', + requestId: tracking.requestId, + duration, + error: authError + }); throw new Error('Invalid session token. Please refresh the page and try again.'); } const userId = user.id; - console.log(`Cancelling email change for user ${userId}`); + edgeLogger.info('Cancelling email change for user', { + action: 'cancel_email_change', + requestId: tracking.requestId, + userId + }); // Call the database function to clear email change fields // This function has SECURITY DEFINER privileges to access auth.users @@ -85,6 +108,14 @@ Deno.serve(async (req) => { // Don't fail the request if audit logging fails } + const duration = endRequest(tracking); + edgeLogger.info('Successfully cancelled email change', { + action: 'cancel_email_change', + requestId: tracking.requestId, + userId, + duration + }); + return new Response( JSON.stringify({ success: true, @@ -94,21 +125,37 @@ Deno.serve(async (req) => { email: null, new_email: null, }, + requestId: tracking.requestId, }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 200, } ); } catch (error) { - console.error('Error in cancel-email-change function:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error in cancel-email-change function', { + action: 'cancel_email_change', + requestId: tracking.requestId, + duration, + error: error instanceof Error ? error.message : String(error) + }); return new Response( JSON.stringify({ success: false, error: error instanceof Error ? error.message : 'An unknown error occurred', + requestId: tracking.requestId, }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 400, } ); diff --git a/supabase/functions/confirm-account-deletion/index.ts b/supabase/functions/confirm-account-deletion/index.ts index 39f45e43..7af1e17b 100644 --- a/supabase/functions/confirm-account-deletion/index.ts +++ b/supabase/functions/confirm-account-deletion/index.ts @@ -1,5 +1,6 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -7,8 +8,15 @@ const corsHeaders = { }; serve(async (req) => { + const tracking = startRequest(); + if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -35,10 +43,20 @@ serve(async (req) => { } = await supabaseClient.auth.getUser(); if (userError || !user) { + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'confirm_deletion', + requestId: tracking.requestId, + duration + }); throw new Error('Unauthorized'); } - console.log(`Confirming deletion for user: ${user.id}`); + edgeLogger.info('Confirming deletion for user', { + action: 'confirm_deletion', + requestId: tracking.requestId, + userId: user.id + }); // Find deletion request const { data: deletionRequest, error: requestError } = await supabaseClient @@ -133,26 +151,50 @@ serve(async (req) => { } } - console.log('Account deactivated and deletion confirmed'); + const duration = endRequest(tracking); + edgeLogger.info('Account deactivated and deletion confirmed', { + action: 'confirm_deletion', + requestId: tracking.requestId, + userId: user.id, + duration + }); return new Response( JSON.stringify({ success: true, message: 'Deletion confirmed. Account deactivated and scheduled for permanent deletion.', scheduled_deletion_at: deletionRequest.scheduled_deletion_at, + 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 confirming deletion:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error confirming deletion', { + action: 'confirm_deletion', + requestId: tracking.requestId, + duration, + error: error.message + }); return new Response( - JSON.stringify({ error: error.message }), + JSON.stringify({ + error: error.message, + requestId: tracking.requestId + }), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } diff --git a/supabase/functions/export-user-data/index.ts b/supabase/functions/export-user-data/index.ts index 264d2b1a..c78d0ca9 100644 --- a/supabase/functions/export-user-data/index.ts +++ b/supabase/functions/export-user-data/index.ts @@ -1,7 +1,7 @@ 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 { sanitizeError } from '../_shared/errorSanitizer.ts'; -import { edgeLogger } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -17,9 +17,16 @@ interface ExportOptions { } serve(async (req) => { + const tracking = startRequest(); + // Handle CORS preflight requests if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -40,14 +47,34 @@ serve(async (req) => { } = await supabaseClient.auth.getUser(); if (authError || !user) { - edgeLogger.error('Authentication failed', { action: 'export_auth' }); + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'export_auth', + requestId: tracking.requestId, + duration + }); return new Response( - JSON.stringify({ error: 'Unauthorized', success: false }), - { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + JSON.stringify({ + error: 'Unauthorized', + success: false, + requestId: tracking.requestId + }), + { + status: 401, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } - edgeLogger.info('Processing export request', { action: 'export_start', userId: user.id }); + edgeLogger.info('Processing export request', { + action: 'export_start', + requestId: tracking.requestId, + userId: user.id + }); // Check rate limiting - max 1 export per hour const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); @@ -64,16 +91,31 @@ serve(async (req) => { } if (recentExports && recentExports.length > 0) { + const duration = endRequest(tracking); const nextAvailableAt = new Date(new Date(recentExports[0].created_at).getTime() + 60 * 60 * 1000).toISOString(); - console.log(`[Export] Rate limited for user ${user.id}, next available: ${nextAvailableAt}`); + edgeLogger.warn('Rate limit exceeded for export', { + action: 'export_rate_limit', + requestId: tracking.requestId, + userId: user.id, + duration, + nextAvailableAt + }); return new Response( JSON.stringify({ success: false, error: 'Rate limited. You can export your data once per hour.', rate_limited: true, - next_available_at: nextAvailableAt + next_available_at: nextAvailableAt, + requestId: tracking.requestId }), - { status: 429, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + { + status: 429, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } @@ -87,7 +129,11 @@ serve(async (req) => { format: 'json' }; - edgeLogger.info('Export options', { action: 'export_options', userId: user.id }); + edgeLogger.info('Export options', { + action: 'export_options', + requestId: tracking.requestId, + userId: user.id + }); // Fetch profile data const { data: profile, error: profileError } = await supabaseClient @@ -97,7 +143,11 @@ serve(async (req) => { .single(); if (profileError) { - edgeLogger.error('Profile fetch failed', { action: 'export_profile' }); + edgeLogger.error('Profile fetch failed', { + action: 'export_profile', + requestId: tracking.requestId, + userId: user.id + }); throw new Error('Failed to fetch profile data'); } @@ -260,33 +310,61 @@ serve(async (req) => { changes: { export_options: options, timestamp: new Date().toISOString(), - data_size: JSON.stringify(exportData).length + data_size: JSON.stringify(exportData).length, + requestId: tracking.requestId } }]); - edgeLogger.info('Export completed successfully', { action: 'export_complete', userId: user.id }); + const duration = endRequest(tracking); + edgeLogger.info('Export completed successfully', { + action: 'export_complete', + requestId: tracking.requestId, + traceId: tracking.traceId, + userId: user.id, + duration, + dataSize: JSON.stringify(exportData).length + }); return new Response( - JSON.stringify({ success: true, data: exportData }), + JSON.stringify({ + success: true, + data: exportData, + requestId: tracking.requestId + }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json', - 'Content-Disposition': `attachment; filename="thrillwiki-data-export-${new Date().toISOString().split('T')[0]}.json"` + 'Content-Disposition': `attachment; filename="thrillwiki-data-export-${new Date().toISOString().split('T')[0]}.json"`, + 'X-Request-ID': tracking.requestId } } ); } catch (error) { - edgeLogger.error('Export error', { action: 'export_error', error: error instanceof Error ? error.message : String(error) }); + const duration = endRequest(tracking); + edgeLogger.error('Export error', { + action: 'export_error', + requestId: tracking.requestId, + duration, + error: error instanceof Error ? error.message : String(error) + }); const sanitized = sanitizeError(error, 'export-user-data'); return new Response( JSON.stringify({ ...sanitized, - success: false + success: false, + requestId: tracking.requestId }), - { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + { + status: 500, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } }); diff --git a/supabase/functions/mfa-unenroll/index.ts b/supabase/functions/mfa-unenroll/index.ts index 27078e31..519053ce 100644 --- a/supabase/functions/mfa-unenroll/index.ts +++ b/supabase/functions/mfa-unenroll/index.ts @@ -1,5 +1,5 @@ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4'; -import { edgeLogger } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -7,9 +7,16 @@ const corsHeaders = { }; Deno.serve(async (req) => { + const tracking = startRequest(); + // Handle CORS preflight requests if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -23,14 +30,33 @@ Deno.serve(async (req) => { // Get authenticated user const { data: { user }, error: userError } = await supabaseClient.auth.getUser(); if (userError || !user) { - edgeLogger.error('Authentication failed', { action: 'mfa_unenroll_auth' }); + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'mfa_unenroll_auth', + requestId: tracking.requestId, + duration + }); return new Response( - JSON.stringify({ error: 'Unauthorized' }), - { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + JSON.stringify({ + error: 'Unauthorized', + requestId: tracking.requestId + }), + { + status: 401, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } - edgeLogger.info('Processing MFA unenroll', { action: 'mfa_unenroll', userId: user.id }); + edgeLogger.info('Processing MFA unenroll', { + action: 'mfa_unenroll', + requestId: tracking.requestId, + userId: user.id + }); // Phase 1: Check AAL level const { data: { session } } = await supabaseClient.auth.getSession(); @@ -130,18 +156,50 @@ Deno.serve(async (req) => { edgeLogger.error('Notification failed', { action: 'mfa_unenroll_notification', userId: user.id }); } - edgeLogger.info('MFA successfully disabled', { action: 'mfa_unenroll_success', userId: user.id }); + const duration = endRequest(tracking); + edgeLogger.info('MFA successfully disabled', { + action: 'mfa_unenroll_success', + requestId: tracking.requestId, + userId: user.id, + duration + }); return new Response( - JSON.stringify({ success: true }), - { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + JSON.stringify({ + success: true, + requestId: tracking.requestId + }), + { + status: 200, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } catch (error) { - edgeLogger.error('Unexpected error', { action: 'mfa_unenroll_error', error: error instanceof Error ? error.message : String(error) }); + const duration = endRequest(tracking); + edgeLogger.error('Unexpected error', { + action: 'mfa_unenroll_error', + requestId: tracking.requestId, + duration, + error: error instanceof Error ? error.message : String(error) + }); return new Response( - JSON.stringify({ error: 'Internal server error' }), - { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + JSON.stringify({ + error: 'Internal server error', + requestId: tracking.requestId + }), + { + status: 500, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + } + } ); } }); diff --git a/supabase/functions/notify-moderators-submission/index.ts b/supabase/functions/notify-moderators-submission/index.ts index e3123e66..aec7ced2 100644 --- a/supabase/functions/notify-moderators-submission/index.ts +++ b/supabase/functions/notify-moderators-submission/index.ts @@ -1,5 +1,6 @@ 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 { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -19,8 +20,15 @@ interface NotificationPayload { } serve(async (req) => { + const tracking = startRequest(); + if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -42,7 +50,9 @@ serve(async (req) => { is_escalated } = payload; - console.log('Notifying moderators about submission via topic:', { + edgeLogger.info('Notifying moderators about submission via topic', { + action: 'notify_moderators', + requestId: tracking.requestId, submission_id, submission_type, content_preview @@ -78,14 +88,25 @@ serve(async (req) => { .single(); if (workflowError || !workflow) { - console.error('Error fetching workflow:', workflowError); + const duration = endRequest(tracking); + edgeLogger.error('Error fetching workflow', { + action: 'notify_moderators', + requestId: tracking.requestId, + duration, + error: workflowError + }); return new Response( JSON.stringify({ success: false, - error: 'Workflow not found or not active' + error: 'Workflow not found or not active', + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); @@ -112,6 +133,10 @@ serve(async (req) => { hasPhotos: has_photos, itemCount: item_count, isEscalated: is_escalated, + + // Request tracking + requestId: tracking.requestId, + traceId: tracking.traceId, }; // Send ONE notification to the moderation-submissions topic @@ -125,21 +150,39 @@ serve(async (req) => { }); if (error) { - console.error('Failed to notify moderators via topic:', error); + const duration = endRequest(tracking); + edgeLogger.error('Failed to notify moderators via topic', { + action: 'notify_moderators', + requestId: tracking.requestId, + duration, + error: error.message + }); return new Response( JSON.stringify({ success: false, error: 'Failed to send notification to topic', - details: error.message + details: error.message, + requestId: tracking.requestId }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, status: 500, } ); } - console.log('Successfully notified all moderators via topic:', data); + const duration = endRequest(tracking); + edgeLogger.info('Successfully notified all moderators via topic', { + action: 'notify_moderators', + requestId: tracking.requestId, + traceId: tracking.traceId, + duration, + transactionId: data?.transactionId + }); return new Response( JSON.stringify({ @@ -147,22 +190,38 @@ serve(async (req) => { message: 'Moderator notifications sent via topic', topicKey: 'moderation-submissions', transactionId: data?.transactionId, + 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-submission:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error in notify-moderators-submission', { + action: 'notify_moderators', + requestId: tracking.requestId, + duration, + error: 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/request-account-deletion/index.ts b/supabase/functions/request-account-deletion/index.ts index 83ddc97b..d795e45a 100644 --- a/supabase/functions/request-account-deletion/index.ts +++ b/supabase/functions/request-account-deletion/index.ts @@ -1,5 +1,6 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -7,8 +8,15 @@ const corsHeaders = { }; serve(async (req) => { + const tracking = startRequest(); + if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -29,10 +37,20 @@ serve(async (req) => { } = await supabaseClient.auth.getUser(); if (userError || !user) { + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'request_deletion', + requestId: tracking.requestId, + duration + }); throw new Error('Unauthorized'); } - console.log(`Processing deletion request for user: ${user.id}`); + edgeLogger.info('Processing deletion request', { + action: 'request_deletion', + requestId: tracking.requestId, + userId: user.id + }); // Check for existing pending deletion request const { data: existingRequest } = await supabaseClient @@ -142,25 +160,52 @@ serve(async (req) => { } } + const duration = endRequest(tracking); + edgeLogger.info('Deletion request created successfully', { + action: 'request_deletion', + requestId: tracking.requestId, + userId: user.id, + duration, + requestId: deletionRequest.id + }); + return new Response( JSON.stringify({ success: true, message: 'Account deletion request created successfully', scheduled_deletion_at: scheduledDeletionAt.toISOString(), request_id: deletionRequest.id, + 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 deletion request:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error processing deletion request', { + action: 'request_deletion', + requestId: tracking.requestId, + duration, + error: error.message + }); return new Response( - JSON.stringify({ error: error.message }), + JSON.stringify({ + error: error.message, + requestId: tracking.requestId + }), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } diff --git a/supabase/functions/resend-deletion-code/index.ts b/supabase/functions/resend-deletion-code/index.ts index 3ebcf740..0daff935 100644 --- a/supabase/functions/resend-deletion-code/index.ts +++ b/supabase/functions/resend-deletion-code/index.ts @@ -1,5 +1,6 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -7,8 +8,15 @@ const corsHeaders = { }; serve(async (req) => { + const tracking = startRequest(); + if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -29,10 +37,20 @@ serve(async (req) => { } = await supabaseClient.auth.getUser(); if (userError || !user) { + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'resend_deletion_code', + requestId: tracking.requestId, + duration + }); throw new Error('Unauthorized'); } - console.log(`Resending deletion code for user: ${user.id}`); + edgeLogger.info('Resending deletion code for user', { + action: 'resend_deletion_code', + requestId: tracking.requestId, + userId: user.id + }); // Find pending deletion request const { data: deletionRequest, error: requestError } = await supabaseClient @@ -114,23 +132,49 @@ serve(async (req) => { } } + const duration = endRequest(tracking); + edgeLogger.info('New confirmation code sent successfully', { + action: 'resend_deletion_code', + requestId: tracking.requestId, + userId: user.id, + duration + }); + return new Response( JSON.stringify({ success: true, message: 'New confirmation code sent successfully', + 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 resending code:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error resending code', { + action: 'resend_deletion_code', + requestId: tracking.requestId, + duration, + error: error.message + }); return new Response( - JSON.stringify({ error: error.message }), + JSON.stringify({ + error: error.message, + requestId: tracking.requestId + }), { status: 400, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); } diff --git a/supabase/functions/send-password-added-email/index.ts b/supabase/functions/send-password-added-email/index.ts index 8e70206d..27c93e82 100644 --- a/supabase/functions/send-password-added-email/index.ts +++ b/supabase/functions/send-password-added-email/index.ts @@ -1,5 +1,6 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', @@ -13,8 +14,15 @@ interface EmailRequest { } serve(async (req) => { + const tracking = startRequest(); + if (req.method === 'OPTIONS') { - return new Response(null, { headers: corsHeaders }); + return new Response(null, { + headers: { + ...corsHeaders, + 'X-Request-ID': tracking.requestId + } + }); } try { @@ -31,6 +39,12 @@ serve(async (req) => { const { data: { user }, error: userError } = await supabaseClient.auth.getUser(); if (userError || !user) { + const duration = endRequest(tracking); + edgeLogger.error('Authentication failed', { + action: 'send_password_email', + requestId: tracking.requestId, + duration + }); throw new Error('Unauthorized'); } @@ -40,6 +54,13 @@ serve(async (req) => { throw new Error('Email is required'); } + edgeLogger.info('Sending password added email', { + action: 'send_password_email', + requestId: tracking.requestId, + userId: user.id, + email + }); + const recipientName = displayName || username || 'there'; const siteUrl = Deno.env.get('SITE_URL') || 'https://thrillwiki.com'; @@ -141,30 +162,53 @@ serve(async (req) => { throw new Error(`Failed to send email: ${emailResponse.statusText}`); } - console.log(`Password addition email sent successfully to: ${email}`); + const duration = endRequest(tracking); + edgeLogger.info('Password addition email sent successfully', { + action: 'send_password_email', + requestId: tracking.requestId, + userId: user.id, + email, + duration + }); return new Response( JSON.stringify({ success: true, message: 'Password addition email sent successfully', + 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 in send-password-added-email function:', error); + const duration = endRequest(tracking); + edgeLogger.error('Error in send-password-added-email function', { + action: 'send_password_email', + requestId: tracking.requestId, + duration, + error: error instanceof Error ? error.message : 'Unknown error' + }); return new Response( JSON.stringify({ success: false, error: error instanceof Error ? error.message : 'Unknown error', + requestId: tracking.requestId, }), { status: 500, - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + headers: { + ...corsHeaders, + 'Content-Type': 'application/json', + 'X-Request-ID': tracking.requestId + }, } ); }