/
zxclovly
/
LUME
Обзор
Документация
Войти
/
zxclovly
/
LUME
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
backend/src/uploads.js
195 строк
5 KB
lovlygod
Ship cloud media polish
19 мар 2026, 08:56
19 мар 2026, 08:56
64206ce
Код
Авторство
О чём код?
const db = require('./db'); const multer = require('multer'); const path = require('path'); const { uploadFile: cloudUploadFile, uploadVoice: cloudUploadVoice } = require('./middleware/upload'); // File type validation const allowedMimeTypes = [ // Images 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', // Documents 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/plain', 'text/csv', // Archives 'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed', // Audio (voice messages) 'audio/webm', 'audio/webm;codecs=opus', 'audio/ogg', 'audio/ogg;codecs=opus', 'audio/mp4', 'audio/mpeg', 'audio/wav' ]; const blockedExtensions = ['.exe', '.bat', '.cmd', '.sh', '.ps1', '.vbs', '.js', '.jar']; const fileFilter = (req, file, cb) => { const ext = path.extname(file.originalname).toLowerCase(); // Block dangerous extensions if (blockedExtensions.includes(ext)) { return cb(new Error('File type not allowed')); } // Allow images if (file.mimetype.startsWith('image/')) { return cb(null, true); } // Allow specific MIME types if (allowedMimeTypes.includes(file.mimetype)) { return cb(null, true); } // Block everything else cb(new Error('File type not allowed')); }; // Create multer upload instance (Cloudinary storage) const upload = multer({ storage: cloudUploadFile.storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 // 5MB limit } }); // Upload handler const uploadFile = async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } const url = req.file.path || req.file.secure_url || req.file.url; const mime = req.file.mimetype; const size = req.file.size; // Determine type const isImage = mime.startsWith('image/'); const type = isImage ? 'image' : 'file'; // Get image dimensions if it's an image let width = null; let height = null; if (isImage) { try { const sizeOf = require('image-size'); const dimensions = sizeOf(url); width = dimensions.width; height = dimensions.height; } catch (e) { // Ignore if we can't get dimensions } } // Create attachment record in database (as draft, no message_id yet) db.run( `INSERT INTO attachments (message_id, type, url, mime, size, width, height) VALUES (NULL, $1, $2, $3, $4, $5, $6)`, [type, url, mime, size, width, height], function(err) { if (err) { console.error('Error creating attachment record:', err); return res.status(500).json({ error: 'Database error' }); } const attachmentId = this.lastID; res.json({ attachmentId: attachmentId.toString(), url, mime, size, type, width, height, filename: req.file.originalname }); } ); } catch (error) { if (error instanceof multer.MulterError) { if (error.code === 'LIMIT_FILE_SIZE') { return res.status(400).json({ error: 'File too large. Max size is 5MB' }); } return res.status(400).json({ error: error.message }); } res.status(500).json({ error: error.message || 'Server error' }); } }; // Voice message upload instance (Cloudinary storage) const voiceUpload = multer({ storage: cloudUploadVoice.storage, fileFilter: (req, file, cb) => { const ext = path.extname(file.originalname).toLowerCase(); // Block dangerous extensions if (blockedExtensions.includes(ext)) { return cb(new Error('File type not allowed')); } // Allow audio types if (file.mimetype.startsWith('audio/')) { return cb(null, true); } cb(new Error('File type not allowed')); }, limits: { fileSize: 5 * 1024 * 1024, // 5MB limit for voice messages files: 1 } }); // Voice message upload handler const uploadVoiceMessage = async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } const url = req.file.path || req.file.secure_url || req.file.url; const mime = req.file.mimetype; const size = req.file.size; const duration = parseFloat(req.body.duration) || 0; // Create attachment record in database db.run( `INSERT INTO attachments (message_id, type, url, mime, size, duration) VALUES (NULL, 'voice', $1, $2, $3, $4)`, [url, mime, size, duration], function(err) { if (err) { console.error('Error creating voice attachment record:', err); return res.status(500).json({ error: 'Database error' }); } const attachmentId = this.lastID; res.json({ attachmentId: attachmentId.toString(), url, mime, size, type: 'voice', duration }); } ); } catch (error) { if (error instanceof multer.MulterError) { if (error.code === 'LIMIT_FILE_SIZE') { return res.status(400).json({ error: 'File too large. Max size is 5MB' }); } return res.status(400).json({ error: error.message }); } res.status(500).json({ error: error.message || 'Server error' }); } }; module.exports = { upload, uploadFile, voiceUpload, uploadVoiceMessage };