import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; import { corsHeaders } from '../_shared/cors.ts'; import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts'; import { addSpanEvent } from '../_shared/logger.ts'; interface InboundEmailPayload { from: string; to: string; subject: string; text: string; html?: string; messageId: string; inReplyTo?: string; references?: string[]; headers: Record; } const handler = async (req: Request, { supabase, span, requestId }: EdgeFunctionContext): Promise => { const payload: InboundEmailPayload = await req.json(); const { from, to, subject, text, html, messageId, inReplyTo, references, headers } = payload; addSpanEvent(span, 'email_received', { from, to, messageId, hasInReplyTo: !!inReplyTo }); // Extract thread ID from headers or inReplyTo let threadId = headers['X-Thread-ID'] || (inReplyTo ? inReplyTo.replace(/<|>/g, '').split('@')[0] : null); // If no thread ID, this is a NEW direct email (not a reply) const isNewEmail = !threadId; if (isNewEmail) { addSpanEvent(span, 'new_direct_email', { from, subject }); } // Find or create submission let submission = null; if (isNewEmail) { // Extract sender email const senderEmail = from.match(/<(.+)>/)?.[1] || from; const senderName = from.match(/^(.+?)\s*/)?.[1] || from; if (senderEmail.toLowerCase() !== submission.email.toLowerCase()) { addSpanEvent(span, 'email_mismatch', { expected: submission.email, received: senderEmail }); return new Response(JSON.stringify({ success: false, reason: 'email_mismatch' }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } } // Insert email thread record const senderEmail = from.match(/<(.+)>/)?.[1] || from; const { error: insertError } = await supabase .from('contact_email_threads') .insert({ submission_id: submission.id, message_id: messageId, in_reply_to: inReplyTo || null, reference_chain: references || [], from_email: senderEmail, to_email: to, subject, body_text: text, body_html: html, direction: 'inbound', metadata: { received_at: new Date().toISOString(), headers: headers, is_new_ticket: isNewEmail } }); if (insertError) { addSpanEvent(span, 'thread_insert_failed', { error: insertError }); throw insertError; } addSpanEvent(span, 'thread_inserted'); // Update submission status if pending if (submission.status === 'pending') { await supabase .from('contact_submissions') .update({ status: 'in_progress' }) .eq('id', submission.id); addSpanEvent(span, 'status_updated', { newStatus: 'in_progress' }); } addSpanEvent(span, 'email_processed', { submissionId: submission.id }); return new Response( JSON.stringify({ success: true }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); }; serve(createEdgeFunction({ name: 'receive-inbound-email', requireAuth: false, useServiceRole: true, corsHeaders, logRequests: true, logResponses: true, }, handler));