From c77381a59ad9b8c8a17f544507ac3be7b3ddf728 Mon Sep 17 00:00:00 2001 From: wass08 Date: Thu, 26 Feb 2026 09:53:36 +0900 Subject: [PATCH] fix deletion of assets on delete ref and delete proj --- .../panels/site-panel/references-dialog.tsx | 18 +++++++--- .../features/community/lib/assets/actions.ts | 34 +++++++++---------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/apps/editor/components/ui/sidebar/panels/site-panel/references-dialog.tsx b/apps/editor/components/ui/sidebar/panels/site-panel/references-dialog.tsx index 590a8db1..7fd0b6b1 100644 --- a/apps/editor/components/ui/sidebar/panels/site-panel/references-dialog.tsx +++ b/apps/editor/components/ui/sidebar/panels/site-panel/references-dialog.tsx @@ -1,3 +1,5 @@ +'use client' + import { type AnyNodeId, type GuideNode, @@ -133,19 +135,25 @@ export function ReferencesDialog({ levelId, open, onOpenChange }: ReferencesDial ) const handleDelete = useCallback( - (nodeId: string) => { + async (nodeId: string) => { const refNode = nodes[nodeId as AnyNodeId] as ScanNode | GuideNode | undefined - deleteNode(nodeId as AnyNodeId) - // Fire-and-forget storage cleanup for Supabase-hosted assets const projectId = activeProject?.id + + // Delete storage asset first (before removing from scene) if ( projectId && !projectId.startsWith('local_') && refNode?.url && - refNode.url.startsWith('https://') + (refNode.url.startsWith('http://') || refNode.url.startsWith('https://')) ) { - deleteProjectAssetByUrl(projectId, refNode.url).catch(console.error) + const result = await deleteProjectAssetByUrl(projectId, refNode.url) + if (!result.success) { + setUploadError(`Failed to delete asset: ${result.error}`) + return + } } + + deleteNode(nodeId as AnyNodeId) }, [deleteNode, nodes, activeProject], ) diff --git a/apps/editor/features/community/lib/assets/actions.ts b/apps/editor/features/community/lib/assets/actions.ts index f39e9748..55a0ef00 100644 --- a/apps/editor/features/community/lib/assets/actions.ts +++ b/apps/editor/features/community/lib/assets/actions.ts @@ -130,29 +130,29 @@ export async function deleteProjectAssetByUrl( return { success: false, error: 'Not authorized' } } - // Look up the asset row by url + projectId - const { data: asset, error: fetchError } = await (supabase.from('project_assets') as any) - .select('id, storage_key') - .eq('project_id', projectId) - .eq('url', url) - .maybeSingle() + // Derive storage_key from the public URL + // URL format: https://.supabase.co/storage/v1/object/public/project-assets/ + const storageKeyFromUrl = url.split(`/${BUCKET}/`)[1]?.split('?')[0] - if (fetchError) { - return { success: false, error: fetchError.message } + if (!storageKeyFromUrl) { + return { success: false, error: 'Could not derive storage key from URL' } } - if (!asset) { - // Nothing to delete — treat as success - return { success: true } + // Delete from storage directly — remove() is a no-op if the file doesn't exist + const { error: storageError } = await supabase.storage.from(BUCKET).remove([storageKeyFromUrl]) + if (storageError) { + return { success: false, error: `Storage delete failed: ${storageError.message}` } } - // Remove from storage - await supabase.storage.from(BUCKET).remove([(asset as any).storage_key]) - - // Delete row - await (supabase.from('project_assets') as any) + // Delete DB row by storage_key scoped to this project + const { error: dbError } = await (supabase.from('project_assets') as any) .delete() - .eq('id', (asset as any).id) + .eq('project_id', projectId) + .eq('storage_key', storageKeyFromUrl) + + if (dbError) { + return { success: false, error: `DB delete failed: ${dbError.message}` } + } return { success: true } } catch (error) {