Merge pull request #7 from pacnpal/main

Main
This commit is contained in:
pacnpal
2025-11-07 22:49:49 -05:00
committed by GitHub
4 changed files with 70 additions and 34 deletions

View File

@@ -72,7 +72,13 @@ export async function fetchSubmissionItems(submissionId: string): Promise<Submis
.eq('submission_id', submissionId) .eq('submission_id', submissionId)
.order('order_index', { ascending: true }); .order('order_index', { ascending: true });
if (error) throw error; if (error) {
handleError(error, {
action: 'Fetch Submission Items',
metadata: { submissionId }
});
throw error;
}
// Transform data to include relational data as item_data // Transform data to include relational data as item_data
return await Promise.all((data || []).map(async item => { return await Promise.all((data || []).map(async item => {
@@ -84,14 +90,23 @@ export async function fetchSubmissionItems(submissionId: string): Promise<Submis
// Fetch location from park_submission_locations if available // Fetch location from park_submission_locations if available
let locationData: any = null; let locationData: any = null;
if (parkSub?.id) { if (parkSub?.id) {
const { data } = await supabase const { data, error: locationError } = await supabase
.from('park_submission_locations') .from('park_submission_locations')
.select('*') .select('*')
.eq('park_submission_id', parkSub.id) .eq('park_submission_id', parkSub.id)
.maybeSingle(); .maybeSingle();
locationData = data;
if (locationError) {
handleNonCriticalError(locationError, {
action: 'Fetch Park Submission Location',
metadata: { parkSubmissionId: parkSub.id, submissionId }
});
// Continue without location data - non-critical
} else {
locationData = data;
}
} }
item_data = { item_data = {
...parkSub, ...parkSub,
// Transform park_submission_location → location for form compatibility // Transform park_submission_location → location for form compatibility

View File

@@ -213,7 +213,7 @@ serve(async (req) => {
); );
// Log notification in notification_logs with idempotency key // Log notification in notification_logs with idempotency key
await supabase.from('notification_logs').insert({ const { error: logError } = await supabase.from('notification_logs').insert({
user_id: '00000000-0000-0000-0000-000000000000', // Topic-based user_id: '00000000-0000-0000-0000-000000000000', // Topic-based
notification_type: 'moderation_submission', notification_type: 'moderation_submission',
idempotency_key: idempotencyKey, idempotency_key: idempotencyKey,
@@ -225,13 +225,23 @@ serve(async (req) => {
} }
}); });
if (logError) {
// Non-blocking - notification was sent successfully, log failure shouldn't fail the request
edgeLogger.warn('Failed to log notification in notification_logs', {
action: 'notify_moderators',
requestId: tracking.requestId,
error: logError.message,
submissionId: submission_id
});
}
const duration = endRequest(tracking); const duration = endRequest(tracking);
edgeLogger.info('Successfully notified all moderators via topic', { edgeLogger.info('Successfully notified all moderators via topic', {
action: 'notify_moderators', action: 'notify_moderators',
requestId: tracking.requestId, requestId: tracking.requestId,
traceId: tracking.traceId, traceId: tracking.traceId,
duration, duration,
transactionId: data?.transactionId transactionId: data?.transactionId
}); });
return new Response( return new Response(

View File

@@ -213,14 +213,19 @@ const handler = async (req: Request) => {
console.error(`[${requestId}] Approval transaction failed:`, rpcError); console.error(`[${requestId}] Approval transaction failed:`, rpcError);
// Update idempotency key to failed // Update idempotency key to failed
await supabase try {
.from('submission_idempotency_keys') await supabase
.update({ .from('submission_idempotency_keys')
status: 'failed', .update({
error_message: rpcError.message, status: 'failed',
completed_at: new Date().toISOString() error_message: rpcError.message,
}) completed_at: new Date().toISOString()
.eq('idempotency_key', idempotencyKey); })
.eq('idempotency_key', idempotencyKey);
} catch (updateError) {
console.error(`[${requestId}] Failed to update idempotency key to failed:`, updateError);
// Non-blocking - continue with error response even if idempotency update fails
}
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
@@ -229,12 +234,12 @@ const handler = async (req: Request) => {
details: rpcError.details, details: rpcError.details,
retries: retryCount retries: retryCount
}), }),
{ {
status: 500, status: 500,
headers: { headers: {
...corsHeaders, ...corsHeaders,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
} }
); );
} }
@@ -242,14 +247,19 @@ const handler = async (req: Request) => {
console.log(`[${requestId}] Transaction completed successfully:`, result); console.log(`[${requestId}] Transaction completed successfully:`, result);
// STEP 8: Success - update idempotency key // STEP 8: Success - update idempotency key
await supabase try {
.from('submission_idempotency_keys') await supabase
.update({ .from('submission_idempotency_keys')
status: 'completed', .update({
result_data: result, status: 'completed',
completed_at: new Date().toISOString() result_data: result,
}) completed_at: new Date().toISOString()
.eq('idempotency_key', idempotencyKey); })
.eq('idempotency_key', idempotencyKey);
} catch (updateError) {
console.error(`[${requestId}] Failed to update idempotency key to completed:`, updateError);
// Non-blocking - transaction succeeded, so continue with success response
}
return new Response( return new Response(
JSON.stringify(result), JSON.stringify(result),

View File

@@ -107,24 +107,25 @@ serve(withRateLimit(async (req) => {
const tracking = startRequest(); const tracking = startRequest();
const requestOrigin = req.headers.get('origin'); const requestOrigin = req.headers.get('origin');
const allowedOrigin = getAllowedOrigin(requestOrigin); const allowedOrigin = getAllowedOrigin(requestOrigin);
// Check if this is a CORS request with a disallowed origin // Check if this is a CORS request with a disallowed origin
if (requestOrigin && !allowedOrigin) { if (requestOrigin && !allowedOrigin) {
edgeLogger.warn('CORS request rejected', { action: 'cors_validation', origin: requestOrigin, requestId: tracking.requestId }); edgeLogger.warn('CORS request rejected', { action: 'cors_validation', origin: requestOrigin, requestId: tracking.requestId });
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
error: 'Origin not allowed', error: 'Origin not allowed',
message: 'The origin of this request is not allowed to access this resource' message: 'The origin of this request is not allowed to access this resource'
}), }),
{ {
status: 403, status: 403,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
} }
); );
} }
// Define CORS headers at function scope so they're available in catch block
const corsHeaders = getCorsHeaders(allowedOrigin); const corsHeaders = getCorsHeaders(allowedOrigin);
// Handle CORS preflight requests // Handle CORS preflight requests
if (req.method === 'OPTIONS') { if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders }) return new Response(null, { headers: corsHeaders })