mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:31:11 -05:00
Refactor export-user-data, notify-user-submission-status, and resend-deletion-code to use createEdgeFunction wrapper. Remove manual CORS, auth, rate limiting boilerplate; adopt standardized EdgeFunctionContext (supabase, user, span, requestId), and integrate built-in tracing, rate limiting, and logging through the wrapper. Update handlers to rely on wrapper context and ensure consistent error handling and observability.
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
import { serve } from 'https://deno.land/std@0.190.0/http/server.ts';
|
|
import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts';
|
|
import { corsHeaders } from '../_shared/cors.ts';
|
|
import { addSpanEvent } from '../_shared/logger.ts';
|
|
|
|
const handler = async (req: Request, { supabase, user, span, requestId }: EdgeFunctionContext) => {
|
|
addSpanEvent(span, 'resending_deletion_code', { userId: user.id });
|
|
|
|
// Find pending deletion request
|
|
const { data: deletionRequest, error: requestError } = await supabase
|
|
.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 rate limiting (max 3 resends per hour - ~20 minutes between resends)
|
|
const lastSent = new Date(deletionRequest.confirmation_code_sent_at);
|
|
const now = new Date();
|
|
const hoursSinceLastSend = (now.getTime() - lastSent.getTime()) / (1000 * 60 * 60);
|
|
|
|
if (hoursSinceLastSend < 0.33) {
|
|
addSpanEvent(span, 'resend_rate_limited', {
|
|
hoursSinceLastSend,
|
|
minRequired: 0.33
|
|
});
|
|
throw new Error('Please wait at least 20 minutes between resend requests');
|
|
}
|
|
|
|
// Generate new confirmation code
|
|
const { data: codeData, error: codeError } = await supabase
|
|
.rpc('generate_deletion_confirmation_code');
|
|
|
|
if (codeError) {
|
|
throw codeError;
|
|
}
|
|
|
|
const confirmationCode = codeData as string;
|
|
|
|
// Update deletion request with new code
|
|
const { error: updateError } = await supabase
|
|
.from('account_deletion_requests')
|
|
.update({
|
|
confirmation_code: confirmationCode,
|
|
confirmation_code_sent_at: now.toISOString(),
|
|
})
|
|
.eq('id', deletionRequest.id);
|
|
|
|
if (updateError) {
|
|
throw updateError;
|
|
}
|
|
|
|
const scheduledDate = new Date(deletionRequest.scheduled_deletion_at);
|
|
|
|
addSpanEvent(span, 'new_code_generated', {
|
|
scheduledDeletionDate: scheduledDate.toISOString()
|
|
});
|
|
|
|
// Send email with new code
|
|
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 - New Confirmation Code',
|
|
html: `
|
|
<h2>New Confirmation Code</h2>
|
|
<p>You requested a new confirmation code for your account deletion.</p>
|
|
<p>Your account will be permanently deleted on <strong>${scheduledDate.toLocaleDateString()}</strong>.</p>
|
|
|
|
<h3>CONFIRMATION CODE: <strong>${confirmationCode}</strong></h3>
|
|
<p>To confirm deletion after the waiting period, you'll need to enter this 6-digit code.</p>
|
|
|
|
<p><strong>Need to cancel?</strong> Log in and visit your account settings to reactivate your account.</p>
|
|
`,
|
|
}),
|
|
});
|
|
addSpanEvent(span, 'email_sent', { email: user.email });
|
|
} catch (emailError) {
|
|
addSpanEvent(span, 'email_send_failed', {
|
|
error: emailError instanceof Error ? emailError.message : String(emailError)
|
|
});
|
|
}
|
|
}
|
|
|
|
addSpanEvent(span, 'resend_completed', { userId: user.id });
|
|
|
|
return {
|
|
success: true,
|
|
message: 'New confirmation code sent successfully',
|
|
};
|
|
};
|
|
|
|
serve(createEdgeFunction({
|
|
name: 'resend-deletion-code',
|
|
requireAuth: true,
|
|
corsHeaders,
|
|
enableTracing: true,
|
|
logRequests: true,
|
|
rateLimitTier: 'moderate', // 10 requests per minute
|
|
}, handler));
|