fix deletion of assets on delete ref and delete proj

This commit is contained in:
wass08
2026-02-26 09:56:50 +09:00
parent b167702bca
commit c77381a59a
2 changed files with 30 additions and 22 deletions
@@ -1,3 +1,5 @@
'use client'
import { import {
type AnyNodeId, type AnyNodeId,
type GuideNode, type GuideNode,
@@ -133,19 +135,25 @@ export function ReferencesDialog({ levelId, open, onOpenChange }: ReferencesDial
) )
const handleDelete = useCallback( const handleDelete = useCallback(
(nodeId: string) => { async (nodeId: string) => {
const refNode = nodes[nodeId as AnyNodeId] as ScanNode | GuideNode | undefined 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 const projectId = activeProject?.id
// Delete storage asset first (before removing from scene)
if ( if (
projectId && projectId &&
!projectId.startsWith('local_') && !projectId.startsWith('local_') &&
refNode?.url && 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], [deleteNode, nodes, activeProject],
) )
@@ -130,29 +130,29 @@ export async function deleteProjectAssetByUrl(
return { success: false, error: 'Not authorized' } return { success: false, error: 'Not authorized' }
} }
// Look up the asset row by url + projectId // Derive storage_key from the public URL
const { data: asset, error: fetchError } = await (supabase.from('project_assets') as any) // URL format: https://<project>.supabase.co/storage/v1/object/public/project-assets/<storageKey>
.select('id, storage_key') const storageKeyFromUrl = url.split(`/${BUCKET}/`)[1]?.split('?')[0]
.eq('project_id', projectId)
.eq('url', url)
.maybeSingle()
if (fetchError) { if (!storageKeyFromUrl) {
return { success: false, error: fetchError.message } return { success: false, error: 'Could not derive storage key from URL' }
} }
if (!asset) { // Delete from storage directly — remove() is a no-op if the file doesn't exist
// Nothing to delete — treat as success const { error: storageError } = await supabase.storage.from(BUCKET).remove([storageKeyFromUrl])
return { success: true } if (storageError) {
return { success: false, error: `Storage delete failed: ${storageError.message}` }
} }
// Remove from storage // Delete DB row by storage_key scoped to this project
await supabase.storage.from(BUCKET).remove([(asset as any).storage_key]) const { error: dbError } = await (supabase.from('project_assets') as any)
// Delete row
await (supabase.from('project_assets') as any)
.delete() .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 } return { success: true }
} catch (error) { } catch (error) {