mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 23:11:12 -05:00
feat: Implement comprehensive request tracking and state management
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
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 } from '../_shared/logger.ts';
|
||||
import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts';
|
||||
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
@@ -8,6 +8,8 @@ const corsHeaders = {
|
||||
};
|
||||
|
||||
serve(async (req) => {
|
||||
const tracking = startRequest();
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
@@ -35,7 +37,7 @@ serve(async (req) => {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
|
||||
edgeLogger.info('Cancelling deletion request', { action: 'cancel_deletion', userId: user.id });
|
||||
edgeLogger.info('Cancelling deletion request', { action: 'cancel_deletion', userId: user.id, requestId: tracking.requestId });
|
||||
|
||||
// Find pending deletion request
|
||||
const { data: deletionRequest, error: requestError } = await supabaseClient
|
||||
@@ -101,29 +103,34 @@ serve(async (req) => {
|
||||
`,
|
||||
}),
|
||||
});
|
||||
edgeLogger.info('Cancellation confirmation email sent', { action: 'cancel_deletion_email', userId: user.id });
|
||||
edgeLogger.info('Cancellation confirmation email sent', { action: 'cancel_deletion_email', userId: user.id, requestId: tracking.requestId });
|
||||
} catch (emailError) {
|
||||
edgeLogger.error('Failed to send email', { action: 'cancel_deletion_email', userId: user.id });
|
||||
edgeLogger.error('Failed to send email', { action: 'cancel_deletion_email', userId: user.id, requestId: tracking.requestId });
|
||||
}
|
||||
}
|
||||
|
||||
const duration = endRequest(tracking);
|
||||
edgeLogger.info('Deletion cancelled successfully', { action: 'cancel_deletion_success', userId: user.id, requestId: tracking.requestId, duration });
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: 'Account deletion cancelled successfully',
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
edgeLogger.error('Error cancelling deletion', { action: 'cancel_deletion_error', error: error instanceof Error ? error.message : String(error) });
|
||||
const duration = endRequest(tracking);
|
||||
edgeLogger.error('Error cancelling deletion', { action: 'cancel_deletion_error', error: error instanceof Error ? error.message : String(error), requestId: tracking.requestId, duration });
|
||||
return new Response(
|
||||
JSON.stringify({ error: error.message }),
|
||||
JSON.stringify({ error: error.message, requestId: tracking.requestId }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
||||
import { Novu } from "npm:@novu/api@1.6.0";
|
||||
|
||||
// TODO: In production, restrict CORS to specific domains
|
||||
// For now, allowing all origins for development flexibility
|
||||
// Example production config: 'Access-Control-Allow-Origin': 'https://yourdomain.com'
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||
};
|
||||
|
||||
// Simple request tracking
|
||||
const startRequest = () => ({ requestId: crypto.randomUUID(), start: Date.now() });
|
||||
const endRequest = (tracking: { start: number }) => Date.now() - tracking.start;
|
||||
|
||||
serve(async (req) => {
|
||||
const tracking = startRequest();
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
@@ -197,28 +200,32 @@ serve(async (req) => {
|
||||
data,
|
||||
});
|
||||
|
||||
console.log('Subscriber created successfully:', subscriber.data);
|
||||
const duration = endRequest(tracking);
|
||||
console.log('Subscriber created successfully:', subscriber.data, { requestId: tracking.requestId, duration });
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
subscriberId: subscriber.data._id,
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
status: 200,
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error('Error creating Novu subscriber:', error);
|
||||
const duration = endRequest(tracking);
|
||||
console.error('Error creating Novu subscriber:', error, { requestId: tracking.requestId, duration });
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
error: error.message,
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
||||
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4";
|
||||
import { startRequest, endRequest } from '../_shared/logger.ts';
|
||||
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
@@ -73,6 +74,8 @@ async function ensureUniqueUsername(
|
||||
}
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
const tracking = startRequest();
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
@@ -188,9 +191,10 @@ Deno.serve(async (req) => {
|
||||
.single();
|
||||
|
||||
if (profile?.avatar_image_id) {
|
||||
console.log('[OAuth Profile] Avatar already exists, skipping');
|
||||
return new Response(JSON.stringify({ success: true, message: 'Avatar already exists' }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
const duration = endRequest(tracking);
|
||||
console.log('[OAuth Profile] Avatar already exists, skipping', { requestId: tracking.requestId, duration });
|
||||
return new Response(JSON.stringify({ success: true, message: 'Avatar already exists', requestId: tracking.requestId }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,22 +330,27 @@ Deno.serve(async (req) => {
|
||||
});
|
||||
}
|
||||
|
||||
console.log('[OAuth Profile] Profile updated successfully');
|
||||
console.log('[OAuth Profile] Profile updated successfully', { requestId: tracking.requestId });
|
||||
}
|
||||
|
||||
const duration = endRequest(tracking);
|
||||
console.log('[OAuth Profile] Processing complete', { requestId: tracking.requestId, duration });
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
avatar_uploaded: !!cloudflareImageId,
|
||||
profile_updated: Object.keys(updateData).length > 0,
|
||||
requestId: tracking.requestId
|
||||
}), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[OAuth Profile] Error:', error);
|
||||
return new Response(JSON.stringify({ error: error.message }), {
|
||||
const duration = endRequest(tracking);
|
||||
console.error('[OAuth Profile] Error:', error, { requestId: tracking.requestId, duration });
|
||||
return new Response(JSON.stringify({ error: error.message, requestId: tracking.requestId }), {
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 } from '../_shared/logger.ts'
|
||||
import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts'
|
||||
|
||||
// Environment-aware CORS configuration
|
||||
const getAllowedOrigin = (requestOrigin: string | null): string | null => {
|
||||
@@ -70,12 +70,13 @@ const createAuthenticatedSupabaseClient = (authHeader: string) => {
|
||||
}
|
||||
|
||||
serve(async (req) => {
|
||||
const tracking = startRequest();
|
||||
const requestOrigin = req.headers.get('origin');
|
||||
const allowedOrigin = getAllowedOrigin(requestOrigin);
|
||||
|
||||
// Check if this is a CORS request with a disallowed origin
|
||||
if (requestOrigin && !allowedOrigin) {
|
||||
edgeLogger.warn('CORS request rejected', { action: 'cors_validation', origin: requestOrigin });
|
||||
edgeLogger.warn('CORS request rejected', { action: 'cors_validation', origin: requestOrigin, requestId: tracking.requestId });
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Origin not allowed',
|
||||
@@ -160,19 +161,26 @@ serve(async (req) => {
|
||||
}
|
||||
|
||||
if (profile.banned) {
|
||||
const duration = endRequest(tracking);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Account suspended',
|
||||
message: 'Account suspended. Contact support for assistance.'
|
||||
message: 'Account suspended. Contact support for assistance.',
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
status: 403,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: {
|
||||
...corsHeaders,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Request-ID': tracking.requestId
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Delete image from Cloudflare
|
||||
edgeLogger.info('Deleting image', { action: 'delete_image', requestId: tracking.requestId });
|
||||
let requestBody;
|
||||
try {
|
||||
requestBody = await req.json();
|
||||
@@ -280,10 +288,12 @@ serve(async (req) => {
|
||||
)
|
||||
}
|
||||
|
||||
const duration = endRequest(tracking);
|
||||
edgeLogger.info('Image deleted successfully', { action: 'delete_image', requestId: tracking.requestId, duration });
|
||||
return new Response(
|
||||
JSON.stringify({ success: true, deleted: true }),
|
||||
JSON.stringify({ success: true, deleted: true, requestId: tracking.requestId }),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -344,19 +354,22 @@ serve(async (req) => {
|
||||
}
|
||||
|
||||
if (profile.banned) {
|
||||
const duration = endRequest(tracking);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Account suspended',
|
||||
message: 'Account suspended. Contact support for assistance.'
|
||||
message: 'Account suspended. Contact support for assistance.',
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
status: 403,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Request a direct upload URL from Cloudflare
|
||||
edgeLogger.info('Requesting upload URL', { action: 'request_upload_url', requestId: tracking.requestId });
|
||||
let requestBody;
|
||||
try {
|
||||
requestBody = await req.json();
|
||||
@@ -448,14 +461,17 @@ serve(async (req) => {
|
||||
}
|
||||
|
||||
// Return the upload URL and image ID to the client
|
||||
const duration = endRequest(tracking);
|
||||
edgeLogger.info('Upload URL created', { action: 'upload_url_success', requestId: tracking.requestId, duration });
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
uploadURL: directUploadResult.result.uploadURL,
|
||||
id: directUploadResult.result.id,
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -569,10 +585,13 @@ serve(async (req) => {
|
||||
|
||||
// Return the image details with convenient URLs
|
||||
const result = imageResult.result
|
||||
const duration = endRequest(tracking);
|
||||
|
||||
// Construct proper imagedelivery.net URLs using account hash and image ID
|
||||
const baseUrl = `https://imagedelivery.net/${CLOUDFLARE_ACCOUNT_HASH}/${result.id}`
|
||||
|
||||
edgeLogger.info('Image status retrieved', { action: 'get_image_status', requestId: tracking.requestId, duration });
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
@@ -587,36 +606,41 @@ serve(async (req) => {
|
||||
medium: `${baseUrl}/medium`,
|
||||
large: `${baseUrl}/large`,
|
||||
avatar: `${baseUrl}/avatar`,
|
||||
} : null
|
||||
} : null,
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const duration = endRequest(tracking);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Method not allowed',
|
||||
message: 'HTTP method not supported for this endpoint'
|
||||
message: 'HTTP method not supported for this endpoint',
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
status: 405,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
|
||||
} catch (error: unknown) {
|
||||
const duration = endRequest(tracking);
|
||||
const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
|
||||
console.error('[Upload] Error:', { error: errorMessage });
|
||||
edgeLogger.error('Upload function error', { action: 'upload_error', requestId: tracking.requestId, duration, error: errorMessage });
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Internal server error',
|
||||
message: errorMessage
|
||||
message: errorMessage,
|
||||
requestId: tracking.requestId
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user