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
256 lines
10 KiB
JavaScript
256 lines
10 KiB
JavaScript
const express = require('express');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const router = express.Router();
|
|
const fileUpload = require('express-fileupload');
|
|
const authMiddleware = require('../../../Middlewares/authMiddleware');
|
|
const { loggers } = require('winston');
|
|
const ncp = require('ncp').ncp;
|
|
const configFile = fs.readFileSync(path.join(__dirname, '../../../data', 'setup.json'), 'utf-8')
|
|
const config = JSON.parse(configFile)[0];
|
|
const bodyParser = require('body-parser');
|
|
const crypto = require('crypto');
|
|
const os = require('os');
|
|
const { getUserData, getSetupData } = require('../../../Middlewares/watcherMiddleware');
|
|
|
|
let setupData = getSetupData();
|
|
let userData = getUserData();
|
|
router.use(bodyParser.json());
|
|
|
|
router.get('/shared/:ownerName/:folderName', authMiddleware, async (req, res) => {
|
|
const { ownerName, folderName } = req.params;
|
|
const userId = req.userData.id;
|
|
const userName = req.userData.name;
|
|
|
|
try {
|
|
// Vérifier l'accès collaboratif
|
|
const collaborationFilePath = path.join(__dirname, '../../../data', 'collaboration.json');
|
|
const collaborationData = JSON.parse(await fs.promises.readFile(collaborationFilePath, 'utf8'));
|
|
|
|
const itemId = `folder-${folderName}`;
|
|
const folderInfo = collaborationData.activeFiles[itemId];
|
|
|
|
if (!folderInfo || !folderInfo.isCollaborative ||
|
|
!folderInfo.activeUsers.some(u => u.id === userId)) {
|
|
return res.status(403).render('error-recovery-file', {
|
|
error: 'Vous n\'avez pas accès à ce dossier.'
|
|
});
|
|
}
|
|
|
|
// Accès au dossier partagé
|
|
const folderPath = path.join('cdn-files', ownerName, folderName);
|
|
const userFolderPath = path.join('cdn-files', ownerName);
|
|
const domain = config.domain || 'swiftlogic-labs.com';
|
|
|
|
// Lecture des fichiers
|
|
const entries = await fs.promises.readdir(folderPath, { withFileTypes: true });
|
|
const allEntries = await fs.promises.readdir(userFolderPath, { withFileTypes: true });
|
|
|
|
const folders = entries
|
|
.filter(entry => entry.isDirectory())
|
|
.map(entry => entry.name);
|
|
|
|
const allFolders = allEntries
|
|
.filter(entry => entry.isDirectory())
|
|
.map(entry => entry.name);
|
|
|
|
// Lecture des informations de fichiers
|
|
const fileInfoData = await fs.promises.readFile(
|
|
path.join(__dirname, '../../../data', 'file_info.json'),
|
|
'utf-8'
|
|
);
|
|
const fileInfo = JSON.parse(fileInfoData);
|
|
const fileInfoNames = fileInfo.map(file => file.fileName);
|
|
|
|
// Récupération des détails des fichiers
|
|
const fileDetails = await Promise.all(entries.map(async entry => {
|
|
const filePath = path.join(folderPath, entry.name);
|
|
const stats = await fs.promises.stat(filePath);
|
|
const encodedFileName = encodeURIComponent(entry.name);
|
|
const fileLink = `https://${domain}/attachments/${ownerName}/${encodedFileName}`;
|
|
|
|
return {
|
|
name: entry.name,
|
|
size: stats.size,
|
|
url: fileLink,
|
|
extension: path.extname(entry.name).toLowerCase(),
|
|
type: entry.isDirectory() ? 'folder' : 'file'
|
|
};
|
|
}));
|
|
|
|
const availableExtensions = Array.from(new Set(fileDetails.map(file => file.extension)));
|
|
|
|
// Determine if current user is owner
|
|
const isOwner = folderInfo.activeUsers.length > 0 && folderInfo.activeUsers[0].id === userId;
|
|
|
|
res.render('folder', {
|
|
user: req.userData,
|
|
files: fileDetails,
|
|
folders,
|
|
allFolders,
|
|
extensions: availableExtensions,
|
|
currentFolder: folderName,
|
|
folderName,
|
|
fileInfoNames,
|
|
userName,
|
|
isSharedFolder: true,
|
|
ownerName,
|
|
isCollaborativeFolder: true,
|
|
isOwner,
|
|
currentUserId: userId
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error accessing shared folder:', error);
|
|
res.status(500).render('error-recovery-file', {
|
|
error: 'Erreur lors de l\'accès au dossier partagé'
|
|
});
|
|
}
|
|
});
|
|
|
|
router.get('/:folderName', authMiddleware, async (req, res) => {
|
|
const userId = req.userData.name;
|
|
const userName = req.userData.name;
|
|
const folderName = req.params.folderName || '';
|
|
const folderPath = path.join('cdn-files', userId, folderName);
|
|
const userFolderPath = path.join('cdn-files', userId);
|
|
const domain = config.domain || 'swiftlogic-labs.com';
|
|
const currentFolderName = folderName || '';
|
|
|
|
const data = await fs.readFileSync(path.join(__dirname, '../../../data', 'user.json'), 'utf-8')
|
|
let users;
|
|
try {
|
|
users = JSON.parse(data);
|
|
} catch (error) {
|
|
console.error('Error parsing user.json:', error);
|
|
users = [];
|
|
}
|
|
|
|
if (!Array.isArray(users)) {
|
|
console.error('Error: users is not an array. Check the contents of setup.json');
|
|
users = [];
|
|
}
|
|
|
|
const user = users.find(user => user.name === userId);
|
|
|
|
if (!user) {
|
|
console.error(`User with ID ${userId} not found in user.json`);
|
|
return res.status(500).send(`User with ID ${userId} not found in user.json`);
|
|
}
|
|
|
|
const userRealId = user.id;
|
|
|
|
const fileInfoData = await fs.readFileSync(path.join(__dirname, '../../../data', 'file_info.json'), 'utf-8')
|
|
let fileInfo;
|
|
try {
|
|
fileInfo = JSON.parse(fileInfoData);
|
|
} catch (error) {
|
|
console.error('Error parsing file_info.json:', error);
|
|
fileInfo = [];
|
|
}
|
|
|
|
if (!Array.isArray(fileInfo)) {
|
|
console.error('Error: fileInfo is not an array. Check the contents of file_info.json');
|
|
fileInfo = [];
|
|
}
|
|
|
|
const fileInfoNames = fileInfo.map(file => file.fileName);
|
|
|
|
fs.readdir(folderPath, { withFileTypes: true }, (err, entries) => {
|
|
if (err) {
|
|
console.error('Error reading directory:', err);
|
|
return res.render('error-recovery-file');
|
|
}
|
|
|
|
const folders = entries
|
|
.filter(entry => entry.isDirectory())
|
|
.map(entry => entry.name);
|
|
|
|
fs.readdir(userFolderPath, { withFileTypes: true }, (err, allEntries) => {
|
|
if (err) {
|
|
console.error('Error reading user directory:', err);
|
|
return res.render('error-recovery-file');
|
|
}
|
|
|
|
const allFolders = allEntries
|
|
.filter(entry => entry.isDirectory())
|
|
.map(entry => entry.name);
|
|
|
|
const fileDetailsPromises = entries.map(entry => {
|
|
const filePath = path.join(folderPath, entry.name);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
fs.stat(filePath, (err, stats) => {
|
|
if (err) {
|
|
console.error('Error getting file stats:', err);
|
|
return reject(err);
|
|
}
|
|
|
|
const encodedFileName = encodeURIComponent(entry.name);
|
|
const fileLink = `https://${domain}/attachments/${userRealId}/${encodedFileName}`;
|
|
|
|
const fileType = entry.isDirectory() ? 'folder' : 'file';
|
|
|
|
resolve({
|
|
name: entry.name,
|
|
size: stats.size,
|
|
url: fileLink,
|
|
extension: path.extname(entry.name).toLowerCase(),
|
|
type: fileType
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
Promise.all(fileDetailsPromises)
|
|
.then(async fileDetails => {
|
|
const availableExtensions = Array.from(new Set(fileDetails.map(file => file.extension)));
|
|
|
|
// Check if current folder is collaborative
|
|
let isCollaborativeFolder = false;
|
|
let isOwner = true; // By default, user is owner of their own folder
|
|
let collaborators = [];
|
|
|
|
try {
|
|
const collaborationFilePath = path.join(__dirname, '../../../data', 'collaboration.json');
|
|
const collaborationData = JSON.parse(await fs.promises.readFile(collaborationFilePath, 'utf8'));
|
|
const itemId = `folder-${folderName}`;
|
|
const folderInfo = collaborationData.activeFiles[itemId];
|
|
|
|
if (folderInfo && folderInfo.isCollaborative && folderInfo.activeUsers) {
|
|
isCollaborativeFolder = true;
|
|
collaborators = folderInfo.activeUsers;
|
|
// First user in activeUsers array is the owner
|
|
if (collaborators.length > 0) {
|
|
isOwner = collaborators[0].id === userRealId;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// If collaboration.json doesn't exist or can't be read, continue without collaboration info
|
|
console.log('No collaboration data found:', error.message);
|
|
}
|
|
|
|
res.render('folder', {
|
|
user: user,
|
|
files: fileDetails,
|
|
folders,
|
|
allFolders,
|
|
extensions: availableExtensions,
|
|
currentFolder: currentFolderName,
|
|
folderName: folderName,
|
|
fileInfoNames,
|
|
userName,
|
|
isCollaborativeFolder,
|
|
isOwner,
|
|
currentUserId: userRealId
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error processing file details:', error);
|
|
res.status(500).send('Erreur lors du traitement des détails des fichiers.');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
module.exports = router; |