All checks were successful
continuous-integration/drone/push Build is passing
✨ New Features: - Dynamic permission-based context menus for files and folders - Support for collaborative folder access control - Upload to specific folders including shared folders - Changelog modal for version updates - Improved dark mode synchronization 🐛 Bug Fixes: - Fixed context menu displaying incorrect options - Fixed CSS !important override preventing dynamic menu behavior - Fixed folder collaboration permission checks - Fixed breadcrumb navigation with empty segments - Fixed "Premature close" error loop in attachments - Fixed missing user variable in admin routes - Fixed avatar loading COEP policy issues 🔒 Security: - Added security middleware (CSRF, rate limiting, input validation) - Fixed collaboration folder access validation - Improved shared folder permission handling 🎨 UI/UX Improvements: - Removed Actions column from folder view - Context menu now properly hides/shows based on permissions - Better visual feedback for collaborative folders - Improved upload flow with inline modals 🧹 Code Quality: - Added collaboration data to folder routes - Refactored context menu logic for better maintainability - Added debug logging for troubleshooting - Improved file upload handling with chunking support
158 lines
6.1 KiB
JavaScript
158 lines
6.1 KiB
JavaScript
const express = require('express');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const multiparty = require('multiparty');
|
|
const router = express.Router();
|
|
const bcrypt = require('bcrypt');
|
|
|
|
// Limite de taille de fichier à 10 Go
|
|
const MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024; // 10 Go
|
|
|
|
// Crée le dossier temporaire à la racine s'il n'existe pas
|
|
const tempDir = path.join(process.cwd(), 'temp');
|
|
if (!fs.existsSync(tempDir)) {
|
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
}
|
|
|
|
router.post('/', (req, res) => {
|
|
const form = new multiparty.Form({
|
|
uploadDir: tempDir,
|
|
maxFilesSize: MAX_FILE_SIZE,
|
|
});
|
|
|
|
form.parse(req, async (err, fields, files) => {
|
|
if (err) {
|
|
console.error('Error parsing the file:', err);
|
|
return res.status(400).send('Error during the file upload');
|
|
}
|
|
|
|
if (!files.file || files.file.length === 0) {
|
|
return res.status(400).send('No file uploaded');
|
|
}
|
|
|
|
const file = files.file[0];
|
|
const targetFolder = fields.targetFolder ? fields.targetFolder[0] : '';
|
|
const isSharedFolder = fields.isSharedFolder ? fields.isSharedFolder[0] === 'true' : false;
|
|
const ownerName = fields.ownerName ? fields.ownerName[0] : '';
|
|
|
|
// Construction du chemin cible avec le dossier spécifié
|
|
let userName = req.user.name;
|
|
|
|
// Si c'est un dossier partagé, utiliser le nom du propriétaire
|
|
if (isSharedFolder && ownerName) {
|
|
// Vérifier les permissions de collaboration
|
|
const collaborationFilePath = path.join(__dirname, '../../../data', 'collaboration.json');
|
|
try {
|
|
const collaborationData = JSON.parse(await fs.promises.readFile(collaborationFilePath, 'utf8'));
|
|
const itemId = `folder-${targetFolder}`;
|
|
const folderInfo = collaborationData.activeFiles[itemId];
|
|
|
|
// Vérifier si l'utilisateur a accès au dossier partagé
|
|
if (!folderInfo || !folderInfo.isCollaborative ||
|
|
!folderInfo.activeUsers.some(u => u.id === req.user.id)) {
|
|
return res.status(403).send('Accès refusé au dossier partagé');
|
|
}
|
|
|
|
userName = ownerName;
|
|
} catch (error) {
|
|
console.error('Error checking collaboration permissions:', error);
|
|
return res.status(500).send('Erreur lors de la vérification des permissions');
|
|
}
|
|
}
|
|
|
|
let userDir = path.join(process.cwd(), 'cdn-files', userName);
|
|
if (targetFolder) {
|
|
userDir = path.join(userDir, targetFolder);
|
|
}
|
|
|
|
const filename = fields.filename ? fields.filename[0] : file.originalFilename;
|
|
const filePath = path.join(userDir, filename);
|
|
|
|
// Récupérer les champs supplémentaires
|
|
const expiryDate = fields.expiryDate ? fields.expiryDate[0] : '';
|
|
const password = fields.password ? fields.password[0] : '';
|
|
|
|
// Hasher le mot de passe si présent
|
|
const saltRounds = 10;
|
|
let hashedPassword = '';
|
|
if (password) {
|
|
hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
}
|
|
|
|
// Crée le répertoire s'il n'existe pas
|
|
if (!fs.existsSync(userDir)) {
|
|
fs.mkdirSync(userDir, { recursive: true });
|
|
}
|
|
|
|
// Lecture en chunks pour plus de performances
|
|
const readStream = fs.createReadStream(file.path, { highWaterMark: 1024 * 1024 });
|
|
const writeStream = fs.createWriteStream(filePath, { flags: 'a' });
|
|
|
|
readStream.pipe(writeStream);
|
|
|
|
readStream.on('end', async () => {
|
|
// Supprimer le fichier temporaire
|
|
fs.unlinkSync(file.path);
|
|
|
|
// Vérifier que le nom du fichier suit bien le format attendu
|
|
const fileNamePattern = /^\d{8}_[A-Z0-9]{6}_.*$/;
|
|
if (!fileNamePattern.test(filename)) {
|
|
console.warn('Le fichier uploadé ne suit pas le format de nom sécurisé attendu:', filename);
|
|
}
|
|
|
|
// Mettre à jour file_info.json si password ou expiryDate présent
|
|
if (expiryDate || password) {
|
|
const fileInfo = {
|
|
fileName: filename,
|
|
expiryDate: expiryDate,
|
|
password: hashedPassword,
|
|
Id: req.user.id,
|
|
path: filePath
|
|
};
|
|
|
|
try {
|
|
let data = [];
|
|
const fileInfoPath = path.join(__dirname, '../../../data', 'file_info.json');
|
|
|
|
if (fs.existsSync(fileInfoPath)) {
|
|
const existingData = await fs.promises.readFile(fileInfoPath, 'utf8');
|
|
data = JSON.parse(existingData);
|
|
if (!Array.isArray(data)) {
|
|
data = [];
|
|
}
|
|
}
|
|
|
|
data.push(fileInfo);
|
|
await fs.promises.writeFile(fileInfoPath, JSON.stringify(data, null, 2));
|
|
} catch (error) {
|
|
console.error('Error updating file_info.json:', error);
|
|
}
|
|
}
|
|
|
|
res.status(200).send({
|
|
message: 'File uploaded successfully.',
|
|
filename: filename
|
|
});
|
|
});
|
|
|
|
readStream.on('error', (err) => {
|
|
console.error('Error reading the file:', err);
|
|
// Nettoyer le fichier temporaire en cas d'erreur
|
|
if (fs.existsSync(file.path)) {
|
|
fs.unlinkSync(file.path);
|
|
}
|
|
res.status(500).send({ message: 'Error uploading file.' });
|
|
});
|
|
|
|
writeStream.on('error', (err) => {
|
|
console.error('Error writing the file:', err);
|
|
// Nettoyer le fichier temporaire en cas d'erreur
|
|
if (fs.existsSync(file.path)) {
|
|
fs.unlinkSync(file.path);
|
|
}
|
|
res.status(500).send({ message: 'Error uploading file.' });
|
|
});
|
|
});
|
|
});
|
|
|
|
module.exports = router; |