/** * TASKMATE - File Upload * ====================== * Multer-Konfiguration für Datei-Uploads */ const multer = require('multer'); const path = require('path'); const fs = require('fs'); const { v4: uuidv4 } = require('uuid'); const logger = require('../utils/logger'); // Upload-Verzeichnis const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '..', 'uploads'); // Verzeichnis erstellen falls nicht vorhanden if (!fs.existsSync(UPLOAD_DIR)) { fs.mkdirSync(UPLOAD_DIR, { recursive: true }); } // Standard-Werte (Fallback) let MAX_FILE_SIZE = (parseInt(process.env.MAX_FILE_SIZE_MB) || 15) * 1024 * 1024; let ALLOWED_MIME_TYPES = [ 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'text/plain', 'text/csv', 'text/markdown', 'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed', 'application/json' ]; /** * Lädt Upload-Einstellungen aus der Datenbank */ function loadUploadSettings() { try { // Lazy-Load um zirkuläre Abhängigkeiten zu vermeiden const { getUploadSettings } = require('../routes/admin'); const settings = getUploadSettings(); if (settings) { MAX_FILE_SIZE = (settings.maxFileSizeMB || 15) * 1024 * 1024; // Erlaubte MIME-Types aus den aktiven Kategorien zusammenstellen const types = []; if (settings.allowedTypes) { Object.values(settings.allowedTypes).forEach(category => { if (category.enabled && Array.isArray(category.types)) { types.push(...category.types); } }); } if (types.length > 0) { ALLOWED_MIME_TYPES = types; } } } catch (error) { // Bei Fehler Standard-Werte beibehalten logger.warn('Upload-Einstellungen konnten nicht geladen werden, verwende Standards'); } } /** * Aktuelle Einstellungen abrufen (für dynamische Prüfung) */ function getCurrentSettings() { loadUploadSettings(); return { maxFileSize: MAX_FILE_SIZE, allowedMimeTypes: ALLOWED_MIME_TYPES }; } /** * Storage-Konfiguration */ const storage = multer.diskStorage({ destination: (req, file, cb) => { // Task-ID aus URL oder Body const taskId = req.params.taskId || req.body.taskId; if (taskId) { // Unterordner pro Task const taskDir = path.join(UPLOAD_DIR, `task_${taskId}`); if (!fs.existsSync(taskDir)) { fs.mkdirSync(taskDir, { recursive: true }); } cb(null, taskDir); } else { cb(null, UPLOAD_DIR); } }, filename: (req, file, cb) => { // Eindeutiger Dateiname mit Original-Extension const ext = path.extname(file.originalname).toLowerCase(); const uniqueName = `${uuidv4()}${ext}`; cb(null, uniqueName); } }); /** * Datei-Filter */ const fileFilter = (req, file, cb) => { // Aktuelle Einstellungen laden const settings = getCurrentSettings(); // MIME-Type prüfen if (settings.allowedMimeTypes.includes(file.mimetype)) { cb(null, true); } else { logger.warn(`Abgelehnter Upload: ${file.originalname} (${file.mimetype})`); cb(new Error(`Dateityp nicht erlaubt: ${file.mimetype}`), false); } }; /** * Dynamische Multer-Instanz erstellen */ function createUpload() { const settings = getCurrentSettings(); return multer({ storage, fileFilter, limits: { fileSize: settings.maxFileSize, files: 10 // Maximal 10 Dateien pro Request } }); } // Standard-Instanz für Rückwärtskompatibilität const upload = createUpload(); /** * Datei löschen */ function deleteFile(filePath) { try { const fullPath = path.join(UPLOAD_DIR, filePath); if (fs.existsSync(fullPath)) { fs.unlinkSync(fullPath); logger.info(`Datei gelöscht: ${filePath}`); return true; } return false; } catch (error) { logger.error(`Fehler beim Löschen: ${filePath}`, { error: error.message }); return false; } } /** * Dateigröße formatieren */ function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * Ist Bild? */ function isImage(mimeType) { return mimeType && mimeType.startsWith('image/'); } /** * Datei-Icon basierend auf MIME-Type */ function getFileIcon(mimeType) { if (mimeType.startsWith('image/')) return 'image'; if (mimeType === 'application/pdf') return 'pdf'; if (mimeType.includes('word')) return 'word'; if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'excel'; if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return 'powerpoint'; if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('7z')) return 'archive'; if (mimeType.startsWith('text/')) return 'text'; return 'file'; } module.exports = { upload, createUpload, deleteFile, formatFileSize, isImage, getFileIcon, getCurrentSettings, UPLOAD_DIR, MAX_FILE_SIZE, ALLOWED_MIME_TYPES };