mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 06:51:13 -05:00
Implement RLS and security functions
Apply Row Level Security to orphaned_images and system_alerts tables. Create RLS policies for admin/moderator access. Replace system_health view with get_system_health() function.
This commit is contained in:
@@ -16,6 +16,21 @@ interface UploadedImageWithFlag extends UploadedImage {
|
||||
wasNewlyUploaded?: boolean;
|
||||
}
|
||||
|
||||
// Upload timeout in milliseconds (30 seconds)
|
||||
const UPLOAD_TIMEOUT_MS = 30000;
|
||||
|
||||
/**
|
||||
* Creates a promise that rejects after a timeout
|
||||
*/
|
||||
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, operation: string): Promise<T> {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise<T>((_, reject) =>
|
||||
setTimeout(() => reject(new Error(`${operation} timed out after ${timeoutMs}ms`)), timeoutMs)
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads pending local images to Cloudflare via Supabase Edge Function
|
||||
* @param images Array of UploadedImage objects (mix of local and already uploaded)
|
||||
@@ -27,10 +42,14 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
|
||||
if (image.isLocal && image.file) {
|
||||
const fileName = image.file.name;
|
||||
|
||||
// Step 1: Get upload URL from our Supabase Edge Function (with tracking)
|
||||
const { data: uploadUrlData, error: urlError, requestId } = await invokeWithTracking(
|
||||
'upload-image',
|
||||
{ action: 'get-upload-url' }
|
||||
// Step 1: Get upload URL from our Supabase Edge Function (with tracking and timeout)
|
||||
const { data: uploadUrlData, error: urlError, requestId } = await withTimeout(
|
||||
invokeWithTracking(
|
||||
'upload-image',
|
||||
{ action: 'get-upload-url' }
|
||||
),
|
||||
UPLOAD_TIMEOUT_MS,
|
||||
'Get upload URL'
|
||||
);
|
||||
|
||||
if (urlError || !uploadUrlData?.uploadURL) {
|
||||
@@ -43,21 +62,25 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
|
||||
}
|
||||
|
||||
|
||||
// Step 2: Upload file directly to Cloudflare
|
||||
// Step 2: Upload file directly to Cloudflare (with timeout)
|
||||
const formData = new FormData();
|
||||
formData.append('file', image.file);
|
||||
|
||||
const uploadResponse = await fetch(uploadUrlData.uploadURL, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const uploadResponse = await withTimeout(
|
||||
fetch(uploadUrlData.uploadURL, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
}),
|
||||
UPLOAD_TIMEOUT_MS,
|
||||
'Cloudflare upload'
|
||||
);
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
const errorText = await uploadResponse.text();
|
||||
const error = new Error(`Upload failed for "${fileName}" (status ${uploadResponse.status}): ${errorText}`);
|
||||
handleError(error, {
|
||||
action: 'Cloudflare Upload',
|
||||
metadata: { fileName, status: uploadResponse.status }
|
||||
metadata: { fileName, status: uploadResponse.status, timeout_ms: UPLOAD_TIMEOUT_MS }
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export const authTestSuite: TestSuite = {
|
||||
|
||||
// Test is_superuser() database function
|
||||
const { data: isSuper, error: superError } = await supabase
|
||||
.rpc('is_superuser', { _user_id: user.id });
|
||||
.rpc('is_superuser', { p_user_id: user.id });
|
||||
|
||||
if (superError) throw new Error(`is_superuser() failed: ${superError.message}`);
|
||||
|
||||
@@ -217,7 +217,7 @@ export const authTestSuite: TestSuite = {
|
||||
|
||||
// Test is_user_banned() database function
|
||||
const { data: isBanned, error: bannedError } = await supabase
|
||||
.rpc('is_user_banned', { _user_id: user.id });
|
||||
.rpc('is_user_banned', { p_user_id: user.id });
|
||||
|
||||
if (bannedError) throw new Error(`is_user_banned() failed: ${bannedError.message}`);
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ export const performanceTestSuite: TestSuite = {
|
||||
const banStart = Date.now();
|
||||
const { data: isBanned, error: banError } = await supabase
|
||||
.rpc('is_user_banned', {
|
||||
_user_id: userData.user.id
|
||||
p_user_id: userData.user.id
|
||||
});
|
||||
|
||||
const banDuration = Date.now() - banStart;
|
||||
|
||||
Reference in New Issue
Block a user