import { readdirSync, statSync, unlinkSync, existsSync } from 'node:fs' import { join, resolve } from 'node:path' const publicDir = existsSync('./public') ? resolve('./public') : resolve('./.output/public') const allowedFolders = ['scores', 'pubs', 'album_art', 'images', 'hdp_images'] function getFolderPath(folder) { return join(publicDir, folder) } function sanitizeFilename(filename) { return filename.replace(/[^a-zA-Z0-9._-]/g, '_') } function isPathInside(base, target) { const resolvedBase = resolve(base) const resolvedTarget = resolve(target) return resolvedTarget.startsWith(resolvedBase) } export default defineEventHandler(async (event) => { requireAuth(event) const method = event.method const query = getQuery(event) const folder = query.folder if (!folder || !allowedFolders.includes(folder)) { throw createError({ statusCode: 400, message: 'Invalid folder' }) } const folderPath = getFolderPath(folder) if (!existsSync(folderPath)) { throw createError({ statusCode: 404, message: 'Folder not found' }) } if (method === 'GET') { const files = readdirSync(folderPath) .filter(f => statSync(join(folderPath, f)).isFile()) .map(f => ({ name: f, size: statSync(join(folderPath, f)).size, url: `/${folder}/${f}` })) return files } if (method === 'DELETE') { const rawFilename = query.file if (!rawFilename) { throw createError({ statusCode: 400, message: 'Filename required' }) } const filename = sanitizeFilename(rawFilename) const filePath = resolve(join(folderPath, filename)) if (!isPathInside(resolve(folderPath), filePath)) { throw createError({ statusCode: 403, message: 'Forbidden' }) } if (existsSync(filePath)) { unlinkSync(filePath) return { success: true } } throw createError({ statusCode: 404, message: 'File not found' }) } throw createError({ statusCode: 405, message: 'Method not allowed' }) })