mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 15:51:12 -05:00
Improve error handling and environment configuration across the application
Enhance input validation, update environment variable usage for Supabase and Turnstile, and refine image upload and auth logic for better robustness and developer experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: cb061c75-702e-4b89-a8d1-77a96cdcdfbb Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/cb061c75-702e-4b89-a8d1-77a96cdcdfbb/ANdRXVZ
This commit is contained in:
@@ -24,6 +24,48 @@ serve(async (req) => {
|
||||
|
||||
const { subscriberId, email, firstName, lastName, phone, avatar, data } = await req.json();
|
||||
|
||||
// Validate required fields
|
||||
if (!subscriberId || typeof subscriberId !== 'string' || subscriberId.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
error: 'subscriberId is required and must be a non-empty string',
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!email || typeof email !== 'string' || email.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
error: 'email is required and must be a non-empty string',
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Validate email format using regex
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
error: 'Invalid email format. Please provide a valid email address',
|
||||
}),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Creating Novu subscriber:', { subscriberId, email, firstName });
|
||||
|
||||
const subscriber = await novu.subscribers.identify(subscriberId, {
|
||||
|
||||
@@ -56,6 +56,54 @@ serve(async (req) => {
|
||||
|
||||
const { itemIds, userId, submissionId }: ApprovalRequest = await req.json();
|
||||
|
||||
// UUID validation regex
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
// Validate itemIds
|
||||
if (!itemIds || !Array.isArray(itemIds)) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'itemIds is required and must be an array' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
if (itemIds.length === 0) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'itemIds must be a non-empty array' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate userId
|
||||
if (!userId || typeof userId !== 'string' || userId.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'userId is required and must be a non-empty string' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
if (!uuidRegex.test(userId)) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'userId must be a valid UUID format' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate submissionId
|
||||
if (!submissionId || typeof submissionId !== 'string' || submissionId.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'submissionId is required and must be a non-empty string' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
if (!uuidRegex.test(submissionId)) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'submissionId must be a valid UUID format' }),
|
||||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Processing selective approval:', { itemIds, userId, submissionId });
|
||||
|
||||
// Fetch all items for the submission
|
||||
@@ -84,7 +132,13 @@ serve(async (req) => {
|
||||
// Topologically sort items by dependencies
|
||||
const sortedItems = topologicalSort(items);
|
||||
const dependencyMap = new Map<string, string>();
|
||||
const approvalResults = [];
|
||||
const approvalResults: Array<{
|
||||
itemId: string;
|
||||
entityId?: string | null;
|
||||
itemType: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> = [];
|
||||
|
||||
// Process items in order
|
||||
for (const item of sortedItems) {
|
||||
|
||||
@@ -55,11 +55,24 @@ serve(async (req) => {
|
||||
}
|
||||
|
||||
// Delete image from Cloudflare
|
||||
const { imageId } = await req.json()
|
||||
|
||||
if (!imageId) {
|
||||
let requestBody;
|
||||
try {
|
||||
requestBody = await req.json();
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Image ID is required for deletion' }),
|
||||
JSON.stringify({ error: 'Invalid JSON in request body' }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const { imageId } = requestBody;
|
||||
|
||||
if (!imageId || typeof imageId !== 'string' || imageId.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'imageId is required and must be a non-empty string' }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
@@ -103,7 +116,25 @@ serve(async (req) => {
|
||||
|
||||
if (req.method === 'POST') {
|
||||
// Request a direct upload URL from Cloudflare
|
||||
const { metadata = {}, variant = 'public', requireSignedURLs = false } = await req.json().catch(() => ({}))
|
||||
let requestBody;
|
||||
try {
|
||||
requestBody = await req.json();
|
||||
} catch (error) {
|
||||
requestBody = {};
|
||||
}
|
||||
|
||||
// Validate request body structure
|
||||
if (requestBody && typeof requestBody !== 'object') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Request body must be a valid JSON object' }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const { metadata = {}, variant = 'public', requireSignedURLs = false } = requestBody;
|
||||
|
||||
// Create FormData for the request (Cloudflare API requires multipart/form-data)
|
||||
const formData = new FormData()
|
||||
@@ -159,9 +190,9 @@ serve(async (req) => {
|
||||
const url = new URL(req.url)
|
||||
const imageId = url.searchParams.get('id')
|
||||
|
||||
if (!imageId) {
|
||||
if (!imageId || imageId.trim() === '') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Image ID is required' }),
|
||||
JSON.stringify({ error: 'id query parameter is required and must be non-empty' }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
|
||||
Reference in New Issue
Block a user