Files
thrilltrack-explorer/supabase/functions/_shared/cors.ts
gpt-engineer-app[bot] bf3da6414a Centralize CORS configuration
Consolidate CORS handling by introducing a shared supabase/functions/_shared/cors.ts and migrate edge functions to import from it. Remove inline cors.ts usage across functions, standardize headers (including traceparent and x-request-id), and prepare for environment-aware origins.
2025-11-10 21:28:46 +00:00

120 lines
3.4 KiB
TypeScript

/**
* Centralized CORS configuration for all edge functions
* Provides consistent header handling across the application
*/
// Standard headers that should be allowed across all functions
const STANDARD_HEADERS = [
'authorization',
'x-client-info',
'apikey',
'content-type',
];
// Tracing headers for distributed tracing and request tracking
const TRACING_HEADERS = [
'traceparent',
'x-request-id',
];
// All headers combined
const ALL_HEADERS = [...STANDARD_HEADERS, ...TRACING_HEADERS];
/**
* Basic CORS headers - allows all origins
* Use for most edge functions that need public access
*/
export const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': STANDARD_HEADERS.join(', '),
};
/**
* Extended CORS headers - includes tracing headers
* Use for functions that participate in distributed tracing
*/
export const corsHeadersWithTracing = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': ALL_HEADERS.join(', '),
};
/**
* CORS headers with methods - for functions with multiple HTTP verbs
*/
export const corsHeadersWithMethods = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': ALL_HEADERS.join(', '),
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
};
/**
* CORS headers with credentials - for authenticated requests requiring cookies
*/
export const corsHeadersWithCredentials = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': ALL_HEADERS.join(', '),
'Access-Control-Allow-Credentials': 'true',
};
/**
* Environment-aware CORS configuration
* Validates origin against allowlist (production) or localhost (development)
*/
export const getAllowedOrigin = (requestOrigin: string | null): string | null => {
// If no origin header, it's not a CORS request (same-origin or server-to-server)
if (!requestOrigin) {
return null;
}
const environment = Deno.env.get('ENVIRONMENT') || 'development';
// Production allowlist - configure via ALLOWED_ORIGINS environment variable
const allowedOriginsEnv = Deno.env.get('ALLOWED_ORIGINS') || '';
const allowedOrigins = allowedOriginsEnv.split(',').filter(origin => origin.trim());
// In development, only allow localhost and Replit domains
if (environment === 'development') {
if (
requestOrigin.includes('localhost') ||
requestOrigin.includes('127.0.0.1') ||
requestOrigin.includes('.repl.co') ||
requestOrigin.includes('.replit.dev')
) {
return requestOrigin;
}
return null;
}
// In production, only allow specific domains from environment variable
if (allowedOrigins.includes(requestOrigin)) {
return requestOrigin;
}
return null;
};
/**
* Get CORS headers with validated origin
* Use for functions requiring strict origin validation (e.g., upload-image)
*/
export const getCorsHeaders = (allowedOrigin: string | null): Record<string, string> => {
if (!allowedOrigin) {
return {};
}
return {
'Access-Control-Allow-Origin': allowedOrigin,
'Access-Control-Allow-Headers': ALL_HEADERS.join(', '),
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Credentials': 'true',
};
};
/**
* Handle OPTIONS preflight request
* Returns a Response with appropriate CORS headers
*/
export const handleCorsPreFlight = (corsHeaders: Record<string, string>): Response => {
return new Response(null, { headers: corsHeaders });
};