85 lines
2.4 KiB
JavaScript
85 lines
2.4 KiB
JavaScript
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
import { join, extname, resolve } from 'node:path'
|
|
import { readMultipartFormData } from 'h3'
|
|
|
|
const publicDir = existsSync('./public') ? resolve('./public') : resolve('./.output/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}`
|
|
}
|
|
})
|