199 Zeilen
5.3 KiB
JavaScript
199 Zeilen
5.3 KiB
JavaScript
/**
|
|
* 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
|
|
};
|