Files
thrilltrack-explorer/supabase/functions/confirm-account-deletion/index.ts
gpt-engineer-app[bot] e28dc97d71 Migrate Phase 1 Functions
Migrate 8 high-priority functions (admin-delete-user, mfa-unenroll, confirm-account-deletion, request-account-deletion, send-contact-message, upload-image, validate-email-backend, process-oauth-profile) to wrapEdgeFunction pattern. Replace manual CORS/auth, add shared validations, integrate standardized error handling, and preserve existing rate limiting where applicable. Update implementations to leverage context span, requestId, and improved logging for consistent error reporting and tracing.
2025-11-11 03:03:26 +00:00

145 lines
5.0 KiB
TypeScript

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
import { corsHeaders } from '../_shared/cors.ts';
import { createEdgeFunction } from '../_shared/edgeFunctionWrapper.ts';
import { validateString } from '../_shared/typeValidation.ts';
export default createEdgeFunction(
{
name: 'confirm-account-deletion',
requireAuth: true,
corsHeaders: corsHeaders
},
async (req, context) => {
const { confirmation_code } = await req.json();
validateString(confirmation_code, 'confirmation_code', { userId: context.userId, requestId: context.requestId });
const supabaseClient = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
);
context.span.setAttribute('action', 'confirm_deletion');
// Find deletion request
const { data: deletionRequest, error: requestError } = await supabaseClient
.from('account_deletion_requests')
.select('*')
.eq('user_id', context.userId)
.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', context.userId)
.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.');
}
// 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', context.userId);
if (profileError) {
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) {
throw updateError;
}
// Send confirmation email
const forwardEmailKey = Deno.env.get('FORWARDEMAIL_API_KEY');
const fromEmail = Deno.env.get('FROM_EMAIL_ADDRESS') || 'noreply@thrillwiki.com';
const userEmail = (await supabaseClient.auth.getUser()).data.user?.email;
if (forwardEmailKey && userEmail) {
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: userEmail,
subject: 'Account Deletion Confirmed - 14 Days to Cancel',
html: `
<h2>Account Deletion Confirmed</h2>
<p>Your deletion request has been confirmed. Your account is now <strong>deactivated</strong> and will be permanently deleted on <strong>${new Date(deletionRequest.scheduled_deletion_at).toLocaleDateString()}</strong>.</p>
<h3>What happens now?</h3>
<ul>
<li>✓ Your account is deactivated immediately</li>
<li>✓ You have 14 days to cancel before permanent deletion</li>
<li>✓ To cancel, log in and visit your account settings</li>
</ul>
<h3>Changed Your Mind?</h3>
<p>You can cancel at any time during the 14-day waiting period by logging in and clicking "Cancel Deletion" in your account settings.</p>
<p><strong>If you take no action</strong>, your account will be automatically deleted after the 14-day waiting period.</p>
`,
}),
});
} catch (emailError) {
// Non-blocking email failure
}
}
return new Response(
JSON.stringify({
success: true,
message: 'Deletion confirmed. Account deactivated and scheduled for permanent deletion.',
scheduled_deletion_at: deletionRequest.scheduled_deletion_at,
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
}
);
}
);