/** * Image Cleanup Utility * Safely deletes orphaned image files from disk. * Checks database references before deleting to avoid breaking shared images. */ import { promises as fs } from 'fs' import path from 'path' import { prisma } from '@/lib/prisma' const UPLOADS_DIR = 'public/uploads/notes' /** * Delete an image file from disk only if no other note references it. * @param imageUrl - The relative URL path (e.g. "/uploads/notes/abc.jpg") * @param excludeNoteId - Note ID to exclude from reference check (the note being deleted) */ export async function deleteImageFileSafely(imageUrl: string, excludeNoteId?: string): Promise { if (!imageUrl || !imageUrl.startsWith('/uploads/notes/')) return try { const notes = await prisma.note.findMany({ where: { images: { contains: imageUrl } }, select: { id: true }, }) const otherRefs = notes.filter(n => n.id !== excludeNoteId) if (otherRefs.length > 0) return // File still referenced elsewhere const filePath = path.join(process.cwd(), imageUrl) await fs.unlink(filePath) } catch { // File already gone or unreadable -- silently skip } } /** * Delete all image files associated with a note. * Checks that each image is not referenced by any other note before deleting. */ export async function cleanupNoteImages(noteId: string, imageUrls: string[]): Promise { for (const url of imageUrls) { await deleteImageFileSafely(url, noteId) } } /** * Parse the images JSON field from a note record. */ export function parseImageUrls(imagesJson: string | null): string[] { if (!imagesJson) return [] try { const parsed = JSON.parse(imagesJson) return Array.isArray(parsed) ? parsed.filter((u: unknown) => typeof u === 'string') : [] } catch { return [] } }