Dieser Commit ist enthalten in:
Claude Project Manager
2025-12-28 21:36:45 +00:00
Commit ab1e5be9a9
146 geänderte Dateien mit 65525 neuen und 0 gelöschten Zeilen

198
backend/middleware/upload.js Normale Datei
Datei anzeigen

@ -0,0 +1,198 @@
/**
* 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
};