import { Router } from 'express'; import multer from 'multer'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { requireAuth } from '../auth.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '..', 'uploads'); if (!fs.existsSync(UPLOAD_DIR)) { fs.mkdirSync(UPLOAD_DIR, { recursive: true }); } const storage = multer.diskStorage({ destination: (_req, _file, cb) => cb(null, UPLOAD_DIR), filename: (_req, file, cb) => { const ext = path.extname(file.originalname).toLowerCase(); const base = path.basename(file.originalname, ext).replace(/[^a-zA-Z0-9-_]/g, '_').slice(0, 50); cb(null, `${base}-${Date.now()}${ext}`); }, }); const upload = multer({ storage, limits: { fileSize: 50 * 1024 * 1024 }, fileFilter: (_req, file, cb) => { const allowed = /^image\/|^video\/mp4$/; if (allowed.test(file.mimetype)) cb(null, true); else cb(new Error('Only images and MP4 videos are allowed')); }, }); const router = Router(); // Wrap multer for Express 5 — multer v2 doesn't handle Express 5 async errors natively function runUpload(req, res) { return new Promise((resolve, reject) => { upload.single('file')(req, res, (err) => { if (err) reject(err); else resolve(); }); }); } router.post('/', requireAuth, async (req, res) => { try { await runUpload(req, res); } catch (err) { const status = err.code === 'LIMIT_FILE_SIZE' ? 413 : 400; return res.status(status).json({ error: err.message, code: err.code || 'UPLOAD_ERROR' }); } if (!req.file) { return res.status(400).json({ error: 'No file uploaded', code: 'VALIDATION' }); } res.json({ url: `/uploads/${req.file.filename}`, filename: req.file.filename }); }); router.delete('/:filename', requireAuth, (req, res) => { const filePath = path.join(UPLOAD_DIR, path.basename(req.params.filename)); if (fs.existsSync(filePath)) fs.unlinkSync(filePath); res.json({ message: 'File deleted' }); }); export default router;