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': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', }; serve(async (req) => { const tracking = startRequest(); if (req.method === 'OPTIONS') { return new Response(null, { headers: { ...corsHeaders, 'X-Request-ID': tracking.requestId } }); } try { const { confirmation_code } = await req.json(); if (!confirmation_code) { throw new Error('Confirmation code is required'); } const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', { global: { headers: { Authorization: req.headers.get('Authorization')! }, }, } ); // Get authenticated user const { data: { user }, error: userError, } = 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'); } 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 .from('account_deletion_requests') .select('*') .eq('user_id', user.id) .eq('status', 'pending') .maybeSingle(); if (requestError || !deletionRequest) { throw new Error('No pending deletion request found'); } // Check if there's already a confirmed request const { data: confirmedRequest } = await supabaseClient .from('account_deletion_requests') .select('*') .eq('user_id', user.id) .eq('status', 'confirmed') .maybeSingle(); if (confirmedRequest) { throw new Error('You already have a confirmed deletion request. Your account is scheduled for deletion.'); } // Verify confirmation code if (deletionRequest.confirmation_code !== confirmation_code) { throw new Error('Invalid confirmation code'); } // Verify code was entered within 24 hours const codeSentAt = new Date(deletionRequest.confirmation_code_sent_at); const now = new Date(); const hoursSinceCodeSent = (now.getTime() - codeSentAt.getTime()) / (1000 * 60 * 60); if (hoursSinceCodeSent > 24) { throw new Error('Confirmation code has expired. Please request a new deletion code.'); } edgeLogger.info('Deactivating account and confirming deletion request', { userId: user.id, requestId: tracking.requestId }); // Deactivate profile const { error: profileError } = await supabaseClient .from('profiles') .update({ deactivated: true, deactivated_at: new Date().toISOString(), deactivation_reason: 'User confirmed account deletion request', }) .eq('user_id', user.id); if (profileError) { edgeLogger.error('Error deactivating profile', { error: profileError.message, userId: user.id, requestId: tracking.requestId }); throw profileError; } // Update deletion request status to 'confirmed' const { error: updateError } = await supabaseClient .from('account_deletion_requests') .update({ status: 'confirmed', }) .eq('id', deletionRequest.id); if (updateError) { edgeLogger.error('Error updating deletion request', { error: updateError.message, requestId: tracking.requestId }); throw updateError; } // Send confirmation email const forwardEmailKey = Deno.env.get('FORWARDEMAIL_API_KEY'); const fromEmail = Deno.env.get('FROM_EMAIL_ADDRESS') || 'noreply@thrillwiki.com'; if (forwardEmailKey) { try { await fetch('https://api.forwardemail.net/v1/emails', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${btoa(forwardEmailKey + ':')}`, }, body: JSON.stringify({ from: fromEmail, to: user.email, subject: 'Account Deletion Confirmed - 14 Days to Cancel', html: `
Your deletion request has been confirmed. Your account is now deactivated and will be permanently deleted on ${new Date(deletionRequest.scheduled_deletion_at).toLocaleDateString()}.
You can cancel at any time during the 14-day waiting period by logging in and clicking "Cancel Deletion" in your account settings.
If you take no action, your account will be automatically deleted after the 14-day waiting period.
`, }), }); edgeLogger.info('Deletion confirmation email sent', { requestId: tracking.requestId }); } catch (emailError) { edgeLogger.error('Failed to send email', { error: emailError instanceof Error ? emailError.message : String(emailError), requestId: tracking.requestId }); } } 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', 'X-Request-ID': tracking.requestId }, } ); } catch (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, requestId: tracking.requestId }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }, } ); } });