mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:11:16 -05:00
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
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 { edgeLogger, startRequest, endRequest } from "../_shared/logger.ts";
|
|
import { createErrorResponse } from "../_shared/errorSanitizer.ts";
|
|
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
|
};
|
|
|
|
interface InboundEmailPayload {
|
|
from: string;
|
|
to: string;
|
|
subject: string;
|
|
text: string;
|
|
html?: string;
|
|
messageId: string;
|
|
inReplyTo?: string;
|
|
references?: string[];
|
|
headers: Record<string, string>;
|
|
}
|
|
|
|
const handler = async (req: Request): Promise<Response> => {
|
|
if (req.method === 'OPTIONS') {
|
|
return new Response(null, { headers: corsHeaders });
|
|
}
|
|
|
|
const tracking = startRequest();
|
|
|
|
try {
|
|
const supabase = createClient(
|
|
Deno.env.get('SUPABASE_URL')!,
|
|
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
);
|
|
|
|
const payload: InboundEmailPayload = await req.json();
|
|
const { from, to, subject, text, html, messageId, inReplyTo, references, headers } = payload;
|
|
|
|
edgeLogger.info('Inbound email received', {
|
|
requestId: tracking.requestId,
|
|
from,
|
|
to,
|
|
messageId
|
|
});
|
|
|
|
// Extract thread ID from headers or inReplyTo
|
|
const threadId = headers['X-Thread-ID'] ||
|
|
(inReplyTo ? inReplyTo.replace(/<|>/g, '').split('@')[0] : null);
|
|
|
|
if (!threadId) {
|
|
edgeLogger.warn('Email missing thread ID', {
|
|
requestId: tracking.requestId,
|
|
messageId
|
|
});
|
|
return new Response(JSON.stringify({ success: false, reason: 'no_thread_id' }), {
|
|
status: 200,
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// Find submission by thread_id
|
|
const { data: submission, error: submissionError } = await supabase
|
|
.from('contact_submissions')
|
|
.select('id, email, status')
|
|
.eq('thread_id', threadId)
|
|
.single();
|
|
|
|
if (submissionError || !submission) {
|
|
edgeLogger.warn('Submission not found for thread ID', {
|
|
requestId: tracking.requestId,
|
|
threadId
|
|
});
|
|
return new Response(JSON.stringify({ success: false, reason: 'submission_not_found' }), {
|
|
status: 200,
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// Verify sender email matches
|
|
const senderEmail = from.match(/<(.+)>/)?.[1] || from;
|
|
if (senderEmail.toLowerCase() !== submission.email.toLowerCase()) {
|
|
edgeLogger.warn('Sender email mismatch', {
|
|
requestId: tracking.requestId,
|
|
expected: submission.email,
|
|
received: senderEmail
|
|
});
|
|
return new Response(JSON.stringify({ success: false, reason: 'email_mismatch' }), {
|
|
status: 200,
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// Insert email thread record
|
|
const { error: insertError } = await supabase
|
|
.from('contact_email_threads')
|
|
.insert({
|
|
submission_id: submission.id,
|
|
message_id: messageId,
|
|
in_reply_to: inReplyTo,
|
|
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
|
|
}
|
|
});
|
|
|
|
if (insertError) {
|
|
edgeLogger.error('Failed to insert inbound email thread', {
|
|
requestId: tracking.requestId,
|
|
error: insertError
|
|
});
|
|
return createErrorResponse(insertError, 500, corsHeaders);
|
|
}
|
|
|
|
// Update submission status if pending
|
|
if (submission.status === 'pending') {
|
|
await supabase
|
|
.from('contact_submissions')
|
|
.update({ status: 'in_progress' })
|
|
.eq('id', submission.id);
|
|
}
|
|
|
|
edgeLogger.info('Inbound email processed successfully', {
|
|
requestId: tracking.requestId,
|
|
submissionId: submission.id,
|
|
duration: endRequest(tracking)
|
|
});
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true }),
|
|
{ status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
|
|
} catch (error) {
|
|
edgeLogger.error('Unexpected error in receive-inbound-email', {
|
|
requestId: tracking.requestId,
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
return createErrorResponse(error, 500, corsHeaders);
|
|
}
|
|
};
|
|
|
|
serve(handler); |