mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 12:31:12 -05:00
Refactor account deletion flow
This commit is contained in:
@@ -57,117 +57,43 @@ serve(async (req) => {
|
||||
throw new Error('Invalid confirmation code');
|
||||
}
|
||||
|
||||
// Check if 14 days have passed
|
||||
const scheduledDate = new Date(deletionRequest.scheduled_deletion_at);
|
||||
// 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 (now < scheduledDate) {
|
||||
const daysRemaining = Math.ceil((scheduledDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||||
throw new Error(`You must wait ${daysRemaining} more day(s) before confirming deletion`);
|
||||
if (hoursSinceCodeSent > 24) {
|
||||
throw new Error('Confirmation code has expired. Please request a new deletion code.');
|
||||
}
|
||||
|
||||
// Use service role client for admin operations
|
||||
const supabaseAdmin = createClient(
|
||||
Deno.env.get('SUPABASE_URL') ?? '',
|
||||
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
|
||||
);
|
||||
console.log('Deactivating account and confirming deletion request...');
|
||||
|
||||
console.log('Starting deletion process...');
|
||||
|
||||
// Delete reviews (CASCADE will handle review_photos)
|
||||
const { error: reviewsError } = await supabaseAdmin
|
||||
.from('reviews')
|
||||
.delete()
|
||||
.eq('user_id', user.id);
|
||||
|
||||
if (reviewsError) {
|
||||
console.error('Error deleting reviews:', reviewsError);
|
||||
}
|
||||
|
||||
// Anonymize submissions and photos
|
||||
const { error: anonymizeError } = await supabaseAdmin
|
||||
.rpc('anonymize_user_submissions', { target_user_id: user.id });
|
||||
|
||||
if (anonymizeError) {
|
||||
console.error('Error anonymizing submissions:', anonymizeError);
|
||||
}
|
||||
|
||||
// Delete user roles
|
||||
const { error: rolesError } = await supabaseAdmin
|
||||
.from('user_roles')
|
||||
.delete()
|
||||
.eq('user_id', user.id);
|
||||
|
||||
if (rolesError) {
|
||||
console.error('Error deleting user roles:', rolesError);
|
||||
}
|
||||
|
||||
// Get profile to check for avatar before deletion
|
||||
const { data: profile } = await supabaseAdmin
|
||||
// Deactivate profile
|
||||
const { error: profileError } = await supabaseClient
|
||||
.from('profiles')
|
||||
.select('avatar_image_id')
|
||||
.eq('user_id', user.id)
|
||||
.maybeSingle();
|
||||
|
||||
// Delete avatar from Cloudflare Images if it exists
|
||||
if (profile?.avatar_image_id) {
|
||||
const cloudflareAccountId = Deno.env.get('VITE_CLOUDFLARE_ACCOUNT_ID');
|
||||
const cloudflareApiToken = Deno.env.get('CLOUDFLARE_API_TOKEN');
|
||||
|
||||
if (cloudflareAccountId && cloudflareApiToken) {
|
||||
try {
|
||||
console.log(`Deleting avatar image: ${profile.avatar_image_id}`);
|
||||
const deleteResponse = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${cloudflareAccountId}/images/v1/${profile.avatar_image_id}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cloudflareApiToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!deleteResponse.ok) {
|
||||
console.error('Failed to delete avatar from Cloudflare:', await deleteResponse.text());
|
||||
} else {
|
||||
console.log('Avatar deleted from Cloudflare successfully');
|
||||
}
|
||||
} catch (avatarError) {
|
||||
console.error('Error deleting avatar from Cloudflare:', avatarError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete profile
|
||||
const { error: profileError } = await supabaseAdmin
|
||||
.from('profiles')
|
||||
.delete()
|
||||
.update({
|
||||
deactivated: true,
|
||||
deactivated_at: new Date().toISOString(),
|
||||
deactivation_reason: 'User confirmed account deletion request',
|
||||
})
|
||||
.eq('user_id', user.id);
|
||||
|
||||
if (profileError) {
|
||||
console.error('Error deleting profile:', profileError);
|
||||
console.error('Error deactivating profile:', profileError);
|
||||
throw profileError;
|
||||
}
|
||||
|
||||
// Update deletion request status
|
||||
const { error: updateError } = await supabaseAdmin
|
||||
// Update deletion request status to 'confirmed'
|
||||
const { error: updateError } = await supabaseClient
|
||||
.from('account_deletion_requests')
|
||||
.update({
|
||||
status: 'completed',
|
||||
completed_at: new Date().toISOString(),
|
||||
status: 'confirmed',
|
||||
})
|
||||
.eq('id', deletionRequest.id);
|
||||
|
||||
if (updateError) {
|
||||
console.error('Error updating deletion request:', updateError);
|
||||
}
|
||||
|
||||
// Delete auth user (this should cascade delete the deletion request)
|
||||
const { error: authError } = await supabaseAdmin.auth.admin.deleteUser(user.id);
|
||||
|
||||
if (authError) {
|
||||
console.error('Error deleting auth user:', authError);
|
||||
throw authError;
|
||||
throw updateError;
|
||||
}
|
||||
|
||||
// Send confirmation email
|
||||
@@ -185,12 +111,19 @@ serve(async (req) => {
|
||||
body: JSON.stringify({
|
||||
from: fromEmail,
|
||||
to: user.email,
|
||||
subject: 'Account Deletion Confirmed',
|
||||
subject: 'Account Deletion Confirmed - 14 Days to Cancel',
|
||||
html: `
|
||||
<h2>Account Deletion Confirmed</h2>
|
||||
<p>Your account has been permanently deleted on ${new Date().toLocaleDateString()}.</p>
|
||||
<p>Your profile and reviews have been removed, but your contributions to the database remain preserved.</p>
|
||||
<p>Thank you for being part of our community.</p>
|
||||
<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>
|
||||
|
||||
<p>If you take no action, your account will be automatically deleted after the 14-day waiting period.</p>
|
||||
`,
|
||||
}),
|
||||
});
|
||||
@@ -200,12 +133,13 @@ serve(async (req) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Account deletion completed successfully');
|
||||
console.log('Account deactivated and deletion confirmed');
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: 'Account deleted successfully',
|
||||
message: 'Deletion confirmed. Account deactivated and scheduled for permanent deletion.',
|
||||
scheduled_deletion_at: deletionRequest.scheduled_deletion_at,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
|
||||
Reference in New Issue
Block a user