unboundedpress/portfolio/server/api/admin/files.upload.js

61 lines
1.8 KiB
JavaScript
Raw Normal View History

import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
import { join } 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']
function getFolderPath(folder) {
return join(publicDir, folder)
}
function sanitizeFilename(filename) {
return filename.replace(/[^a-zA-Z0-9._-]/g, '_')
}
export default defineEventHandler(async (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' })
}
const ext = fileField.filename ? fileField.filename.toLowerCase().slice(fileField.filename.lastIndexOf('.')) : ''
if (!allowedExtensions.includes(ext)) {
throw createError({ statusCode: 400, message: `File type not allowed. Allowed: ${allowedExtensions.join(', ')}` })
}
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}`
}
})