import { existsSync, mkdirSync, writeFileSync } from 'node:fs' import { join, extname } from 'node:path' import { readMultipartFormData } from 'h3' const publicDir = './public' const allowedFolders = ['scores', 'pubs', 'album_art', 'images', 'hdp_images'] const allowedExtensions = ['.pdf', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.mp3', '.wav', '.ogg'] const MAX_FILE_SIZE = 50 * 1024 * 1024 const mimeMap = { '.pdf': 'application/pdf', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml', '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg', } function getFolderPath(folder) { return join(publicDir, folder) } function sanitizeFilename(filename) { return filename.replace(/[^a-zA-Z0-9._-]/g, '_') } export default defineEventHandler(async (event) => { requireAuth(event) 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)) { mkdirSync(folderPath, { recursive: true }) } const formData = await readMultipartFormData(event) if (!formData || formData.length === 0) { throw createError({ statusCode: 400, message: 'No file uploaded' }) } const fileField = formData.find(f => f.name === 'file') if (!fileField || !fileField.data) { throw createError({ statusCode: 400, message: 'No file data' }) } if (fileField.data.length > MAX_FILE_SIZE) { throw createError({ statusCode: 400, message: 'File too large. Maximum size is 50MB' }) } const ext = fileField.filename ? extname(fileField.filename).toLowerCase() : '' if (!allowedExtensions.includes(ext)) { throw createError({ statusCode: 400, message: `File type not allowed. Allowed: ${allowedExtensions.join(', ')}` }) } if (fileField.type && mimeMap[ext] && fileField.type !== mimeMap[ext]) { throw createError({ statusCode: 400, message: `File content type mismatch for ${ext} files` }) } const filename = fileField.filename ? sanitizeFilename(fileField.filename) : `upload${ext}` const filePath = join(folderPath, filename) writeFileSync(filePath, fileField.data) return { success: true, filename, url: `/${folder}/${filename}` } })