mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 07:11:13 -05:00
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.
This commit is contained in:
@@ -1,26 +1,17 @@
|
||||
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
|
||||
import { corsHeaders } from '../_shared/cors.ts';
|
||||
import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts';
|
||||
import { createEdgeFunction } from '../_shared/edgeFunctionWrapper.ts';
|
||||
import { validateString } from '../_shared/typeValidation.ts';
|
||||
|
||||
serve(async (req) => {
|
||||
const tracking = startRequest();
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
...corsHeaders,
|
||||
'X-Request-ID': tracking.requestId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
export default createEdgeFunction(
|
||||
{
|
||||
name: 'confirm-account-deletion',
|
||||
requireAuth: true,
|
||||
corsHeaders: corsHeaders
|
||||
},
|
||||
async (req, context) => {
|
||||
const { confirmation_code } = await req.json();
|
||||
|
||||
if (!confirmation_code) {
|
||||
throw new Error('Confirmation code is required');
|
||||
}
|
||||
validateString(confirmation_code, 'confirmation_code', { userId: context.userId, requestId: context.requestId });
|
||||
|
||||
const supabaseClient = createClient(
|
||||
Deno.env.get('SUPABASE_URL') ?? '',
|
||||
@@ -32,33 +23,13 @@ serve(async (req) => {
|
||||
}
|
||||
);
|
||||
|
||||
// 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
|
||||
});
|
||||
context.span.setAttribute('action', 'confirm_deletion');
|
||||
|
||||
// Find deletion request
|
||||
const { data: deletionRequest, error: requestError } = await supabaseClient
|
||||
.from('account_deletion_requests')
|
||||
.select('*')
|
||||
.eq('user_id', user.id)
|
||||
.eq('user_id', context.userId)
|
||||
.eq('status', 'pending')
|
||||
.maybeSingle();
|
||||
|
||||
@@ -70,7 +41,7 @@ serve(async (req) => {
|
||||
const { data: confirmedRequest } = await supabaseClient
|
||||
.from('account_deletion_requests')
|
||||
.select('*')
|
||||
.eq('user_id', user.id)
|
||||
.eq('user_id', context.userId)
|
||||
.eq('status', 'confirmed')
|
||||
.maybeSingle();
|
||||
|
||||
@@ -92,11 +63,6 @@ serve(async (req) => {
|
||||
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')
|
||||
@@ -105,14 +71,9 @@ serve(async (req) => {
|
||||
deactivated_at: new Date().toISOString(),
|
||||
deactivation_reason: 'User confirmed account deletion request',
|
||||
})
|
||||
.eq('user_id', user.id);
|
||||
.eq('user_id', context.userId);
|
||||
|
||||
if (profileError) {
|
||||
edgeLogger.error('Error deactivating profile', {
|
||||
error: profileError.message,
|
||||
userId: user.id,
|
||||
requestId: tracking.requestId
|
||||
});
|
||||
throw profileError;
|
||||
}
|
||||
|
||||
@@ -125,18 +86,15 @@ serve(async (req) => {
|
||||
.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';
|
||||
const userEmail = (await supabaseClient.auth.getUser()).data.user?.email;
|
||||
|
||||
if (forwardEmailKey) {
|
||||
if (forwardEmailKey && userEmail) {
|
||||
try {
|
||||
await fetch('https://api.forwardemail.net/v1/emails', {
|
||||
method: 'POST',
|
||||
@@ -146,7 +104,7 @@ serve(async (req) => {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
from: fromEmail,
|
||||
to: user.email,
|
||||
to: userEmail,
|
||||
subject: 'Account Deletion Confirmed - 14 Days to Cancel',
|
||||
html: `
|
||||
<h2>Account Deletion Confirmed</h2>
|
||||
@@ -166,60 +124,21 @@ serve(async (req) => {
|
||||
`,
|
||||
}),
|
||||
});
|
||||
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
|
||||
});
|
||||
// Non-blocking email failure
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user