mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-26 01:31:12 -05:00
Fix migration for admin settings
This commit is contained in:
@@ -16,6 +16,8 @@ import { useAuth } from "@/hooks/useAuth";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Camera, CheckCircle, AlertCircle, Info } from "lucide-react";
|
||||
import { UppyPhotoSubmissionUploadProps } from "@/types/submissions";
|
||||
import { withRetry } from "@/lib/retryHelpers";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
export function UppyPhotoSubmissionUpload({
|
||||
onSubmissionComplete,
|
||||
@@ -94,66 +96,92 @@ export function UppyPhotoSubmissionUpload({
|
||||
setPhotos((prev) => prev.map((p) => (p === photo ? { ...p, uploadStatus: "uploading" as const } : p)));
|
||||
|
||||
try {
|
||||
// Get upload URL from edge function
|
||||
const { data: uploadData, error: uploadError } = await invokeWithTracking(
|
||||
"upload-image",
|
||||
{ metadata: { requireSignedURLs: false }, variant: "public" },
|
||||
user?.id,
|
||||
);
|
||||
// Wrap Cloudflare upload in retry logic
|
||||
const cloudflareUrl = await withRetry(
|
||||
async () => {
|
||||
// Get upload URL from edge function
|
||||
const { data: uploadData, error: uploadError } = await invokeWithTracking(
|
||||
"upload-image",
|
||||
{ metadata: { requireSignedURLs: false }, variant: "public" },
|
||||
user?.id,
|
||||
);
|
||||
|
||||
if (uploadError) throw uploadError;
|
||||
if (uploadError) throw uploadError;
|
||||
|
||||
const { uploadURL, id: cloudflareId } = uploadData;
|
||||
const { uploadURL, id: cloudflareId } = uploadData;
|
||||
|
||||
// Upload file to Cloudflare
|
||||
if (!photo.file) {
|
||||
throw new Error("Photo file is missing");
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("file", photo.file);
|
||||
// Upload file to Cloudflare
|
||||
if (!photo.file) {
|
||||
throw new Error("Photo file is missing");
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("file", photo.file);
|
||||
|
||||
const uploadResponse = await fetch(uploadURL, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const uploadResponse = await fetch(uploadURL, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
throw new Error("Failed to upload to Cloudflare");
|
||||
}
|
||||
if (!uploadResponse.ok) {
|
||||
throw new Error("Failed to upload to Cloudflare");
|
||||
}
|
||||
|
||||
// Poll for processing completion
|
||||
let attempts = 0;
|
||||
const maxAttempts = 30;
|
||||
let cloudflareUrl = "";
|
||||
// Poll for processing completion
|
||||
let attempts = 0;
|
||||
const maxAttempts = 30;
|
||||
let cloudflareUrl = "";
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
const supabaseUrl = "https://api.thrillwiki.com";
|
||||
const statusResponse = await fetch(`${supabaseUrl}/functions/v1/upload-image?id=${cloudflareId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session?.access_token || ""}`,
|
||||
apikey:
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkdnRtbnJzenlicW5iY3FiZGN5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgzMjYzNTYsImV4cCI6MjA3MzkwMjM1Nn0.DM3oyapd_omP5ZzIlrT0H9qBsiQBxBRgw2tYuqgXKX4",
|
||||
},
|
||||
});
|
||||
while (attempts < maxAttempts) {
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
const supabaseUrl = "https://api.thrillwiki.com";
|
||||
const statusResponse = await fetch(`${supabaseUrl}/functions/v1/upload-image?id=${cloudflareId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session?.access_token || ""}`,
|
||||
apikey:
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkdnRtbnJzenlicW5iY3FiZGN5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgzMjYzNTYsImV4cCI6MjA3MzkwMjM1Nn0.DM3oyapd_omP5ZzIlrT0H9qBsiQBxBRgw2tYuqgXKX4",
|
||||
},
|
||||
});
|
||||
|
||||
if (statusResponse.ok) {
|
||||
const status = await statusResponse.json();
|
||||
if (status.uploaded && status.urls) {
|
||||
cloudflareUrl = status.urls.public;
|
||||
break;
|
||||
if (statusResponse.ok) {
|
||||
const status = await statusResponse.json();
|
||||
if (status.uploaded && status.urls) {
|
||||
cloudflareUrl = status.urls.public;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (!cloudflareUrl) {
|
||||
throw new Error("Upload processing timeout");
|
||||
}
|
||||
|
||||
return cloudflareUrl;
|
||||
},
|
||||
{
|
||||
onRetry: (attempt, error, delay) => {
|
||||
logger.warn('Retrying photo upload', {
|
||||
attempt,
|
||||
delay,
|
||||
fileName: photo.file?.name
|
||||
});
|
||||
|
||||
// Emit event for UI indicator
|
||||
window.dispatchEvent(new CustomEvent('submission-retry', {
|
||||
detail: {
|
||||
attempt,
|
||||
maxAttempts: 3,
|
||||
delay,
|
||||
type: 'photo upload'
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (!cloudflareUrl) {
|
||||
throw new Error("Upload processing timeout");
|
||||
}
|
||||
);
|
||||
|
||||
// Revoke object URL
|
||||
URL.revokeObjectURL(photo.url);
|
||||
@@ -185,59 +213,78 @@ export function UppyPhotoSubmissionUpload({
|
||||
|
||||
setUploadProgress(null);
|
||||
|
||||
// Create content_submission record first
|
||||
const { data: submissionData, error: submissionError } = await supabase
|
||||
.from("content_submissions")
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
submission_type: "photo",
|
||||
content: {}, // Empty content, all data is in relational tables
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
// Create submission records with retry logic
|
||||
await withRetry(
|
||||
async () => {
|
||||
// Create content_submission record first
|
||||
const { data: submissionData, error: submissionError } = await supabase
|
||||
.from("content_submissions")
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
submission_type: "photo",
|
||||
content: {}, // Empty content, all data is in relational tables
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (submissionError || !submissionData) {
|
||||
throw submissionError || new Error("Failed to create submission record");
|
||||
}
|
||||
if (submissionError || !submissionData) {
|
||||
throw submissionError || new Error("Failed to create submission record");
|
||||
}
|
||||
|
||||
// Create photo_submission record
|
||||
const { data: photoSubmissionData, error: photoSubmissionError } = await supabase
|
||||
.from("photo_submissions")
|
||||
.insert({
|
||||
submission_id: submissionData.id,
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
parent_id: parentId || null,
|
||||
title: title.trim() || null,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
// Create photo_submission record
|
||||
const { data: photoSubmissionData, error: photoSubmissionError } = await supabase
|
||||
.from("photo_submissions")
|
||||
.insert({
|
||||
submission_id: submissionData.id,
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
parent_id: parentId || null,
|
||||
title: title.trim() || null,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (photoSubmissionError || !photoSubmissionData) {
|
||||
throw photoSubmissionError || new Error("Failed to create photo submission");
|
||||
}
|
||||
if (photoSubmissionError || !photoSubmissionData) {
|
||||
throw photoSubmissionError || new Error("Failed to create photo submission");
|
||||
}
|
||||
|
||||
// Insert all photo items
|
||||
const photoItems = photos.map((photo, index) => ({
|
||||
photo_submission_id: photoSubmissionData.id,
|
||||
cloudflare_image_id: photo.url.split("/").slice(-2, -1)[0] || "", // Extract ID from URL
|
||||
cloudflare_image_url:
|
||||
photo.uploadStatus === "uploaded"
|
||||
? photo.url
|
||||
: uploadedPhotos.find((p) => p.order === photo.order)?.url || photo.url,
|
||||
caption: photo.caption.trim() || null,
|
||||
title: photo.title?.trim() || null,
|
||||
filename: photo.file?.name || null,
|
||||
order_index: index,
|
||||
file_size: photo.file?.size || null,
|
||||
mime_type: photo.file?.type || null,
|
||||
}));
|
||||
// Insert all photo items
|
||||
const photoItems = photos.map((photo, index) => ({
|
||||
photo_submission_id: photoSubmissionData.id,
|
||||
cloudflare_image_id: photo.url.split("/").slice(-2, -1)[0] || "", // Extract ID from URL
|
||||
cloudflare_image_url:
|
||||
photo.uploadStatus === "uploaded"
|
||||
? photo.url
|
||||
: uploadedPhotos.find((p) => p.order === photo.order)?.url || photo.url,
|
||||
caption: photo.caption.trim() || null,
|
||||
title: photo.title?.trim() || null,
|
||||
filename: photo.file?.name || null,
|
||||
order_index: index,
|
||||
file_size: photo.file?.size || null,
|
||||
mime_type: photo.file?.type || null,
|
||||
}));
|
||||
|
||||
const { error: itemsError } = await supabase.from("photo_submission_items").insert(photoItems);
|
||||
const { error: itemsError } = await supabase.from("photo_submission_items").insert(photoItems);
|
||||
|
||||
if (itemsError) {
|
||||
throw itemsError;
|
||||
}
|
||||
if (itemsError) {
|
||||
throw itemsError;
|
||||
}
|
||||
},
|
||||
{
|
||||
onRetry: (attempt, error, delay) => {
|
||||
logger.warn('Retrying photo submission creation', { attempt, delay });
|
||||
|
||||
window.dispatchEvent(new CustomEvent('submission-retry', {
|
||||
detail: {
|
||||
attempt,
|
||||
maxAttempts: 3,
|
||||
delay,
|
||||
type: 'photo submission'
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
toast({
|
||||
title: "Submission Successful",
|
||||
@@ -259,7 +306,12 @@ export function UppyPhotoSubmissionUpload({
|
||||
handleError(error, {
|
||||
action: 'Submit Photo Submission',
|
||||
userId: user?.id,
|
||||
metadata: { entityType, entityId, photoCount: photos.length }
|
||||
metadata: {
|
||||
entityType,
|
||||
entityId,
|
||||
photoCount: photos.length,
|
||||
retriesExhausted: true
|
||||
}
|
||||
});
|
||||
|
||||
toast({
|
||||
|
||||
Reference in New Issue
Block a user