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"; import { withEdgeRetry } from '../_shared/retryHelper.ts'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; interface NotificationPayload { reportId: string; reportType: string; reportedEntityType: string; reportedEntityId: string; reporterName: string; reason: string; reportedAt: string; entityPreview: string; } serve(async (req) => { if (req.method === 'OPTIONS') { 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')!; const supabase = createClient(supabaseUrl, supabaseServiceKey); const payload: NotificationPayload = await req.json(); edgeLogger.info('Processing report notification', { action: 'notify_moderators_report', reportId: payload.reportId, reportType: payload.reportType, reportedEntityType: payload.reportedEntityType, requestId: tracking.requestId }); // Calculate relative time const reportedAt = new Date(payload.reportedAt); const now = new Date(); const diffMs = now.getTime() - reportedAt.getTime(); const diffMins = Math.floor(diffMs / 60000); let relativeTime: string; if (diffMins < 1) { relativeTime = 'just now'; } else if (diffMins < 60) { relativeTime = `${diffMins} minute${diffMins === 1 ? '' : 's'} ago`; } else { const diffHours = Math.floor(diffMins / 60); relativeTime = `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`; } // Determine priority based on report type and age let priority: string; const criticalTypes = ['harassment', 'offensive']; const isUrgent = diffMins < 5; if (criticalTypes.includes(payload.reportType) || isUrgent) { priority = 'high'; } else if (diffMins < 30) { priority = 'medium'; } else { priority = 'low'; } // Fetch the workflow ID for report alerts const { data: template, error: templateError } = await supabase .from('notification_templates') .select('workflow_id') .eq('workflow_id', 'report-alert') .eq('is_active', true) .maybeSingle(); if (templateError) { edgeLogger.error('Error fetching workflow', { action: 'notify_moderators_report', requestId: tracking.requestId, error: templateError }); throw new Error(`Failed to fetch workflow: ${templateError.message}`); } if (!template) { edgeLogger.warn('No active report-alert workflow found', { action: 'notify_moderators_report', requestId: tracking.requestId }); return new Response( JSON.stringify({ success: false, error: 'No active report-alert workflow configured', }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 400, } ); } // Fetch reported entity name let reportedEntityName = 'Unknown'; try { if (payload.reportedEntityType === 'review') { const { data: review } = await supabase .from('reviews') .select('ride:rides(name), park:parks(name)') .eq('id', payload.reportedEntityId) .maybeSingle(); reportedEntityName = review?.ride?.name || review?.park?.name || 'Review'; } else if (payload.reportedEntityType === 'profile') { const { data: profile } = await supabase .from('profiles') .select('display_name, username') .eq('user_id', payload.reportedEntityId) .maybeSingle(); reportedEntityName = profile?.display_name || profile?.username || 'User Profile'; } else if (payload.reportedEntityType === 'content_submission') { // Query submission_metadata table for the name instead of dropped content JSONB column const { data: metadata } = await supabase .from('submission_metadata') .select('metadata_value') .eq('submission_id', payload.reportedEntityId) .eq('metadata_key', 'name') .maybeSingle(); reportedEntityName = metadata?.metadata_value || 'Submission'; } } catch (error) { edgeLogger.warn('Could not fetch entity name', { action: 'notify_moderators_report', requestId: tracking.requestId, error }); } // Build enhanced notification payload const notificationPayload = { baseUrl: 'https://www.thrillwiki.com', reportId: payload.reportId, reportType: payload.reportType, reportedEntityType: payload.reportedEntityType, reportedEntityId: payload.reportedEntityId, reporterName: payload.reporterName, reason: payload.reason, entityPreview: payload.entityPreview, reportedEntityName, reportedAt: payload.reportedAt, relativeTime, priority, }; edgeLogger.info('Triggering notification with payload', { action: 'notify_moderators_report', requestId: tracking.requestId }); // Invoke the trigger-notification function with retry const result = await withEdgeRetry( async () => { const { data, error } = await supabase.functions.invoke( 'trigger-notification', { body: { workflowId: template.workflow_id, topicKey: 'moderation-reports', payload: notificationPayload, }, } ); if (error) { const enhancedError = new Error(error.message || 'Notification trigger failed'); (enhancedError as any).status = error.status; throw enhancedError; } return data; }, { maxAttempts: 3, baseDelay: 1000 }, tracking.requestId, 'trigger-report-notification' ); edgeLogger.info('Notification triggered successfully', { action: 'notify_moderators_report', requestId: tracking.requestId, 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', 'X-Request-ID': tracking.requestId }, status: 200, } ); } catch (error: any) { edgeLogger.error('Error in notify-moderators-report', { action: 'notify_moderators_report', requestId: tracking.requestId, error: error.message }); endRequest(tracking, 500, error.message); return new Response( JSON.stringify({ success: false, error: error.message, requestId: tracking.requestId }), { headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }, status: 500, } ); } });