From fa57d497afc181671668ba552babfb1100b71b75 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 18:30:31 +0000 Subject: [PATCH] Add database persistence to 8 edge functions Implement Phase 1 by adding error span logging and database persistence to 8 edge functions that already log errors: - detect-location - export-user-data - notify-moderators-submission - novu-webhook - send-escalation-notification - send-password-added-email - resend-deletion-code - merge-contact-tickets This update introduces startSpan/endSpan and logSpanToDatabase usage in catch blocks to ensure errors are recorded in the monitoring database. --- supabase/functions/detect-location/index.ts | 7 ++++++- supabase/functions/export-user-data/index.ts | 12 +++++++++++- supabase/functions/merge-contact-tickets/index.ts | 7 ++++++- .../functions/notify-moderators-submission/index.ts | 7 ++++++- supabase/functions/novu-webhook/index.ts | 7 ++++++- supabase/functions/resend-deletion-code/index.ts | 12 +++++++++++- .../functions/send-escalation-notification/index.ts | 7 ++++++- .../functions/send-password-added-email/index.ts | 12 +++++++++++- 8 files changed, 63 insertions(+), 8 deletions(-) diff --git a/supabase/functions/detect-location/index.ts b/supabase/functions/detect-location/index.ts index 502c9a45..b109c089 100644 --- a/supabase/functions/detect-location/index.ts +++ b/supabase/functions/detect-location/index.ts @@ -1,6 +1,6 @@ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { corsHeadersWithTracing as corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger, startRequest, endRequest } from "../_shared/logger.ts"; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from "../_shared/logger.ts"; import { formatEdgeError } from "../_shared/errorFormatter.ts"; interface IPLocationResponse { @@ -288,6 +288,11 @@ serve(async (req) => { requestId: tracking.requestId }); + // Persist error to database for monitoring + const errorSpan = startSpan('detect-location-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); + endRequest(tracking); // Return default (metric) with 500 status to indicate error occurred diff --git a/supabase/functions/export-user-data/index.ts b/supabase/functions/export-user-data/index.ts index 7c94bcd9..2b244476 100644 --- a/supabase/functions/export-user-data/index.ts +++ b/supabase/functions/export-user-data/index.ts @@ -3,7 +3,7 @@ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4'; import { corsHeaders } from '../_shared/cors.ts'; import { rateLimiters, withRateLimit } from '../_shared/rateLimiter.ts'; import { sanitizeError } from '../_shared/errorSanitizer.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; import { formatEdgeError } from '../_shared/errorFormatter.ts'; interface ExportOptions { @@ -53,6 +53,11 @@ serve(withRateLimit(async (req) => { requestId: tracking.requestId, duration }); + + // Persist error to database + const authErrorSpan = startSpan('export-user-data-auth-error', 'SERVER'); + endSpan(authErrorSpan, 'error', authError); + logSpanToDatabase(authErrorSpan, tracking.requestId); return new Response( JSON.stringify({ error: 'Unauthorized', @@ -350,6 +355,11 @@ serve(withRateLimit(async (req) => { duration, error: formatEdgeError(error) }); + + // Persist error to database for monitoring + const errorSpan = startSpan('export-user-data-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); const sanitized = sanitizeError(error, 'export-user-data'); return new Response( JSON.stringify({ diff --git a/supabase/functions/merge-contact-tickets/index.ts b/supabase/functions/merge-contact-tickets/index.ts index 0eabdcdf..fc5a13df 100644 --- a/supabase/functions/merge-contact-tickets/index.ts +++ b/supabase/functions/merge-contact-tickets/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 { corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; import { createErrorResponse, sanitizeError } from '../_shared/errorSanitizer.ts'; interface MergeTicketsRequest { @@ -279,6 +279,11 @@ serve(async (req) => { duration, error: error instanceof Error ? error.message : 'Unknown error', }); + + // Persist error to database for monitoring + const errorSpan = startSpan('merge-contact-tickets-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); return createErrorResponse(error, 500, corsHeaders, 'merge_contact_tickets'); } diff --git a/supabase/functions/notify-moderators-submission/index.ts b/supabase/functions/notify-moderators-submission/index.ts index a4fcc992..3c045525 100644 --- a/supabase/functions/notify-moderators-submission/index.ts +++ b/supabase/functions/notify-moderators-submission/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 { corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; import { withEdgeRetry } from '../_shared/retryHelper.ts'; interface NotificationPayload { @@ -266,6 +266,11 @@ serve(async (req) => { error: error.message }); + // Persist error to database for monitoring + const errorSpan = startSpan('notify-moderators-submission-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); + return new Response( JSON.stringify({ success: false, diff --git a/supabase/functions/novu-webhook/index.ts b/supabase/functions/novu-webhook/index.ts index c53957cf..e4390837 100644 --- a/supabase/functions/novu-webhook/index.ts +++ b/supabase/functions/novu-webhook/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 { corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger } from '../_shared/logger.ts'; +import { edgeLogger, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; // Simple request tracking const startRequest = () => ({ requestId: crypto.randomUUID(), start: Date.now() }); @@ -71,6 +71,11 @@ serve(async (req) => { duration }); + // Persist error to database for monitoring + const errorSpan = startSpan('novu-webhook-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); + return new Response( JSON.stringify({ success: false, diff --git a/supabase/functions/resend-deletion-code/index.ts b/supabase/functions/resend-deletion-code/index.ts index 6184a8d0..8f0a4f87 100644 --- a/supabase/functions/resend-deletion-code/index.ts +++ b/supabase/functions/resend-deletion-code/index.ts @@ -2,7 +2,7 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; import { corsHeaders } from '../_shared/cors.ts'; import { rateLimiters, withRateLimit } from '../_shared/rateLimiter.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; // Apply moderate rate limiting (10 req/min) to prevent deletion code spam // Protects against abuse while allowing legitimate resend requests @@ -42,6 +42,11 @@ serve(withRateLimit(async (req) => { requestId: tracking.requestId, duration }); + + // Persist error to database + const authErrorSpan = startSpan('resend-deletion-code-auth-error', 'SERVER'); + endSpan(authErrorSpan, 'error', userError); + logSpanToDatabase(authErrorSpan, tracking.requestId); throw new Error('Unauthorized'); } @@ -165,6 +170,11 @@ serve(withRateLimit(async (req) => { duration, error: error.message }); + + // Persist error to database for monitoring + const errorSpan = startSpan('resend-deletion-code-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); return new Response( JSON.stringify({ error: error.message, diff --git a/supabase/functions/send-escalation-notification/index.ts b/supabase/functions/send-escalation-notification/index.ts index b3e7569c..39c376f3 100644 --- a/supabase/functions/send-escalation-notification/index.ts +++ b/supabase/functions/send-escalation-notification/index.ts @@ -1,7 +1,7 @@ import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; import { corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; import { withEdgeRetry } from '../_shared/retryHelper.ts'; interface EscalationRequest { @@ -264,6 +264,11 @@ serve(async (req) => { duration, error: error instanceof Error ? error.message : 'Unknown error' }); + + // Persist error to database for monitoring + const errorSpan = startSpan('send-escalation-notification-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); return new Response( JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error occurred', diff --git a/supabase/functions/send-password-added-email/index.ts b/supabase/functions/send-password-added-email/index.ts index b39e8bc0..435beaaf 100644 --- a/supabase/functions/send-password-added-email/index.ts +++ b/supabase/functions/send-password-added-email/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'; import { corsHeaders } from '../_shared/cors.ts'; -import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'; +import { edgeLogger, startRequest, endRequest, logSpanToDatabase, startSpan, endSpan } from '../_shared/logger.ts'; interface EmailRequest { email: string; @@ -41,6 +41,11 @@ serve(async (req) => { requestId: tracking.requestId, duration }); + + // Persist error to database + const authErrorSpan = startSpan('send-password-added-email-auth-error', 'SERVER'); + endSpan(authErrorSpan, 'error', userError); + logSpanToDatabase(authErrorSpan, tracking.requestId); throw new Error('Unauthorized'); } @@ -196,6 +201,11 @@ serve(async (req) => { error: error instanceof Error ? error.message : 'Unknown error' }); + // Persist error to database for monitoring + const errorSpan = startSpan('send-password-added-email-error', 'SERVER'); + endSpan(errorSpan, 'error', error); + logSpanToDatabase(errorSpan, tracking.requestId); + return new Response( JSON.stringify({ success: false,