Nom du fichier : ${fileInfo.fileName}
`; + if (fileInfo.expiryDate) { + html += `Date de fin de disponibilité : ${fileInfo.expiryDate}
`; + } + if (fileInfo.password) { + html += `Mot de passe : Oui
`; + } + Swal.fire({ + title: 'Informations sur le fichier', + html: html, + confirmButtonText: 'Fermer' + }); +} \ No newline at end of file diff --git a/routes/attachments.js b/routes/attachments.js new file mode 100644 index 0000000..b4aa1d0 --- /dev/null +++ b/routes/attachments.js @@ -0,0 +1,194 @@ +const express = require('express'); +const router = express.Router(); +const path = require('path'); +const fs = require('fs').promises; +const mime = require('mime-types'); +const { logger, ErrorLogger } = require('../config/logs'); +const crypto = require('crypto'); + +const baseDir = 'cdn-files'; + +async function getSamAccountNameFromUserId(userId) { + const data = await fs.readFile(path.join(__dirname, '../user.json'), 'utf8'); + const users = JSON.parse(data); + const user = users.find(user => user.id === userId); + if (user) { + return user.name; + } else { + throw new Error('User not found'); + } +} + +async function findFileInUserDir(userId, filename) { + const samaccountname = await getSamAccountNameFromUserId(userId); + const userDir = path.join(baseDir, samaccountname); + return findFileInDir(userDir, filename); +} + +async function findFileInDir(dir, filename) { + const files = await fs.readdir(dir, { withFileTypes: true }); + + for (const file of files) { + const filePath = path.join(dir, file.name); + + if (file.name === filename && file.isFile()) { + return filePath; + } else if (file.isDirectory()) { + const found = await findFileInDir(filePath, filename); + if (found) { + return found; + } + } + } + + return null; +} + +router.get('/:userId', (req, res) => { + res.render('unauthorized'); +}); + + +router.get('/:userId/:filename', async (req, res) => { + const { userId, filename } = req.params; + + try { + const filePath = await findFileInUserDir(userId, filename); + + if (!filePath) { + return res.render('file-not-found'); + } + + const data = await fs.readFile('file_info.json', 'utf8'); + const fileInfoArray = JSON.parse(data); + + const fileInfo = fileInfoArray.find(info => info.fileName === filename && info.Id === userId); + + if (fileInfo) { + const expiryDate = new Date(fileInfo.expiryDate); + const now = new Date(); + + if (expiryDate < now) { + await fs.unlink(filePath); + return res.render('file-expired'); + } + + if (fileInfo.password && !req.session.passwordVerified) { + return res.render('password-check', { userId, filename }); + } + } + + const fileContent = await fs.readFile(filePath); + let mimeType = mime.lookup(filePath); + + if (!mimeType) { + if (filePath.endsWith('.txt')) { + mimeType = 'text/plain'; + } else if (filePath.endsWith('.pdf')) { + mimeType = 'application/pdf'; + } + } + + if (mimeType) { + res.setHeader('Content-Type', mimeType); + } + + if (mimeType === 'text/plain') { + res.end(fileContent); + } else { + res.send(fileContent); + } + + if (fileInfo) { + req.session.passwordVerified = false; + } + } catch (err) { + ErrorLogger.error('Error reading file:', err); + return res.status(500).send('Error reading file.'); + } +}); + +router.post('/:userId/:filename', async (req, res) => { + const { userId, filename } = req.params; + const enteredPassword = req.body.password; + + try { + const data = await fs.readFile('file_info.json', 'utf8'); + const fileInfoArray = JSON.parse(data); + + const fileInfo = fileInfoArray.find(info => info.fileName === filename && info.Id === userId); + + if (!fileInfo) { + return res.json({ success: false, message: 'File not found' }); + } + + const algorithm = 'aes-256-cbc'; + const key = crypto.scryptSync(enteredPassword, 'salt', 32); + const iv = Buffer.alloc(16, 0); + const cipher = crypto.createCipheriv(algorithm, key, iv); + const encryptedPassword = cipher.update('', 'utf8', 'hex') + cipher.final('hex'); + + if (fileInfo.password === encryptedPassword) { + req.session.passwordVerified = true; + const filePath = await findFileInUserDir(userId, filename); + const fileContent = await fs.readFile(filePath); + let mimeType = mime.lookup(filePath); + + if (!mimeType) { + if (filePath.endsWith('.txt')) { + mimeType = 'text/plain'; + } else if (filePath.endsWith('.pdf')) { + mimeType = 'application/pdf'; + } + } + + res.json({ success: true, fileContent: fileContent.toString('base64'), mimeType }); + } else { + res.json({ success: false, message: 'Incorrect password' }); + } + } catch (err) { + ErrorLogger.error('Error reading file:', err); + return res.status(500).send('Error reading file.'); + } +}); + +async function deleteExpiredFiles() { + let data = await fs.readFile('file_info.json', 'utf8'); + let fileInfoArray = JSON.parse(data); + + const now = new Date(); + let newFileInfoArray = []; + + for (let i = 0; i < fileInfoArray.length; i++) { + const fileInfo = fileInfoArray[i]; + let expiryDate; + if (fileInfo.expiryDate && fileInfo.expiryDate.trim() !== '') { + expiryDate = new Date(fileInfo.expiryDate); + } else { + continue; + } + + if (expiryDate < now) { + const samaccountname = await getSamAccountNameFromUserId(fileInfo.userId); + const userDir = path.join(baseDir, samaccountname); + const filePath = path.join(userDir, fileInfo.fileName); + try { + await fs.unlink(filePath); + } catch (err) { + ErrorLogger.error('Error deleting file:', err); + } + } else { + newFileInfoArray.push(fileInfo); + } + } + + try { + await fs.writeFile('file_info.json', JSON.stringify(newFileInfoArray, null, 2), 'utf8'); + } catch (err) { + ErrorLogger.error('Error writing to file_info.json:', err); + } +} + +setInterval(deleteExpiredFiles, 24 * 60 * 60 * 1000); + +module.exports = router; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..33a404c --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,110 @@ +const express = require('express'); +const router = express.Router(); +const passport = require('passport'); +const fs = require('fs'); +const { checkUserExistsAD } = require('../Middlewares/UserIDMiddlewareAD'); +const { checkUserExistsDiscord } = require('../Middlewares/UserIDMiddlewareDiscord'); +const path = require('path'); +const { getUserData, getSetupData } = require('../Middlewares/watcherMiddleware'); + +let setupData = getSetupData(); +let userData = getUserData(); + +let adStrategy; +if (setupData.ldap !== undefined) { + adStrategy = require('../models/Passport-ActiveDirectory'); +} + +let DiscordStrategy; +if (setupData.discord !== undefined) { + DiscordStrategy = require('../models/Passport-Discord'); +} + +let user = userData; +if (user.identifyURL) { + app.get("/auth/discord", (req, res) => { + res.redirect(user.identifyURL); + }); +} else { +} + +router.use(passport.initialize()); +router.use(passport.session()); + +router.get('/login', function(req, res) { + const setupFilePath = path.join('setup.json'); + const setupData = JSON.parse(fs.readFileSync(setupFilePath, 'utf-8')); + res.render('AuthLogin', { setupData, isAuthenticated: false, errorMessage: '', showActiveDirectoryForm: true, currentUrl: req.originalUrl }); +}); + +passport.deserializeUser((user, done) => { + done(null, user); +}); + +router.get('/logout', (req, res) => { + req.logout(function(err) { + if (err) { + return next(err); + } + res.redirect('/auth/login'); + }); +}); + +var opts = { failWithError: true } + +router.post('/activedirectory', (req, res, next) => { + passport.authenticate('ActiveDirectory', opts, (err, user, info) => { + if (err) { + return res.render('AuthLogin', { isAuthenticated: false, errorMessage: err.message, setupData: {}, showActiveDirectoryForm: true, currentUrl: req.originalUrl }); + } + if (!user) { + return res.render('AuthLogin', { isAuthenticated: false, errorMessage: 'L\'utilisateur n\'est pas autorisé.', setupData: {}, showActiveDirectoryForm: true, currentUrl: req.originalUrl }); + } + req.user = { + ...user._json, + name: user._json.sAMAccountName, + id: user._json.sAMAccountName, + }; + req.logIn(req.user, function(err) { + if (err) { return next(err); } + return next(); + }); + })(req, res, next); +}, checkUserExistsAD); + +router.get("/discord", (req, res) => { + res.redirect(setupData.discord.identifyURL); +}); + +router.get('/discord/callback', (req, res, next) => { + passport.authenticate('discord', (err, user, info) => { + if (err) { + return next(err); + } + + if (!user) { + return res.redirect('/auth/login'); + } + + req.logIn(user, (loginErr) => { + if (loginErr) { + return next(loginErr); + } + + checkUserExistsDiscord(req, res, () => { + if (req.userExists) { + return res.redirect('/dpanel/dashboard'); + } else { + createUser(req.user, (createErr) => { + if (createErr) { + return next(createErr); + } + return res.redirect('/dpanel/dashboard'); + }); + } + }); + }); + })(req, res, next); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/dpanel.js b/routes/dpanel.js new file mode 100644 index 0000000..a776004 --- /dev/null +++ b/routes/dpanel.js @@ -0,0 +1,698 @@ +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('setup.json'); +const config = JSON.parse(configFile); +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('/dashboard', authMiddleware, async (req, res) => { + const folderName = req.params.folderName || ''; + + if (!req.userData || !req.userData.name) { + return res.render('error-recovery-file', { error: 'User data is undefined or incomplete' }); + } + + const userId = req.userData.id; + const userName = req.userData.name; + const downloadDir = path.join('cdn-files', userName); + const domain = config.domain || 'mydomain.com'; + + if (!fs.existsSync(downloadDir)) { + fs.mkdirSync(downloadDir, { recursive: true }); + } + + try { + fs.accessSync(downloadDir, fs.constants.R_OK | fs.constants.W_OK); + } catch (err) { + console.error('No access!', err); + return res.render('error-recovery-file', { error: 'No access to directory' }); + } + + let fileInfoNames = []; + try { + const fileInfo = JSON.parse(fs.readFileSync('file_info.json', 'utf8')); + fileInfoNames = fileInfo.map(file => file.fileName); + } catch (err) { + console.error('Error reading file_info.json:', err); + } + + try { + const files = await fs.promises.readdir(downloadDir); + + const folders = files.filter(file => fs.statSync(path.join(downloadDir, file)).isDirectory()); + + const fileDetails = files.map(file => { + const filePath = path.join(downloadDir, file); + const stats = fs.statSync(filePath); + const fileExtension = path.extname(file).toLowerCase(); + const encodedFileName = encodeURIComponent(file); + const fileLink = `https://${domain}/attachments/${userId}/${encodedFileName}`; + + const fileType = stats.isDirectory() ? 'folder' : 'file'; + + return { + name: file, + size: stats.size, + url: fileLink, + extension: fileExtension, + type: fileType + }; + }); + + const availableExtensions = Array.from(new Set(fileDetails.map(file => file.extension))); + + res.render('dashboard', { files: fileDetails, folders, extensions: availableExtensions, allFolders: folders, folderName: folderName, fileInfoNames: fileInfoNames }); + } catch (err) { + console.error('Error reading directory:', err); + return res.render('error-recovery-file', { error: err.message }); + } +}); + +router.get('/dashboard/folder/:folderName', authMiddleware, async (req, res) => { + const userId = 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 || 'mydomain.com'; + const currentFolderName = folderName || ''; + + const data = await fs.promises.readFile('user.json', 'utf8'); + const users = JSON.parse(data); + const user = users.find(user => user.name === userId); + + if (!user) { + return res.status(500).send('User not found in user.json'); + } + + const userRealId = user.id; + + const fileInfoData = await fs.promises.readFile('file_info.json', 'utf8'); + const fileInfo = JSON.parse(fileInfoData); + + 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(fileDetails => { + const availableExtensions = Array.from(new Set(fileDetails.map(file => file.extension))); + + res.render('folder', { files: fileDetails, folders, allFolders, extensions: availableExtensions, currentFolder: currentFolderName, folderName: folderName, fileInfoNames }); + }) + .catch(error => { + console.error('Error processing file details:', error); + res.status(500).send('Erreur lors du traitement des détails des fichiers.'); + }); + }); + }); +}); + +router.post('/dashboard/newfolder', authMiddleware, (req, res) => { + try { + console.log('Received POST request to create a new folder.'); + + const userId = req.userData.name; + let { folderName } = req.body; + + console.log('Received folderName:', folderName); + + if (!folderName || typeof folderName !== 'string') { + console.log('Invalid folderName:', folderName); + return res.status(400).json({ message: 'Le nom du dossier ne peut pas être vide.' }); + } + + folderName = folderName.trim(); + + if (!folderName) { + console.log('Trimmed folderName is empty.'); + return res.status(400).json({ message: 'Le nom du dossier ne peut pas être vide.' }); + } + + const folderPath = path.join('cdn-files', userId, folderName); + + if (fs.existsSync(folderPath)) { + console.log('Folder already exists:', folderPath); + return res.status(400).json({ message: 'Le dossier existe déjà.' }); + } + + fs.mkdir(folderPath, (err) => { + if (err) { + console.error(err); + return res.status(500).json({ message: 'Erreur lors de la création du dossier.', error: err }); + } + console.log('Folder created successfully:', folderPath); + res.status(200).json({ message: 'Dossier créé avec succès.' }); + }); + } catch (error) { + console.error('Error creating folder:', error); + return res.status(500).json({ message: 'Erreur lors de la création du dossier.', error: error }); + } +}); + +router.post('/dashboard/rename', authMiddleware, async (req, res) => { + const userId = req.userData.name; + const { currentName, newName } = req.body; + + if (!currentName || !newName) { + return res.status(400).send('Both currentName and newName must be provided.'); + } + + const currentPath = path.join('cdn-files', userId || '', currentName); + const newPath = path.join('cdn-files', userId, newName); + + try { + await fs.promises.rename(currentPath, newPath); + + const data = await fs.promises.readFile('file_info.json', 'utf8'); + let fileInfo = JSON.parse(data); + + let found = false; + for (let i = 0; i < fileInfo.length; i++) { + if (fileInfo[i].fileName === currentName) { + fileInfo[i].fileName = newName; + found = true; + break; + } + } + + if (found) { + await fs.promises.writeFile('file_info.json', JSON.stringify(fileInfo, null, 2), 'utf8'); + } + + res.status(200).send('L\'opération a été effectuée avec succès.'); + } catch (err) { + console.error(err); + return res.status(500).send('Erreur lors du changement de nom du fichier.'); + } +}); + +router.post('/dashboard/rename/:filePath*', authMiddleware, async (req, res) => { + const userId = req.userData.name; + const { currentName, newName } = req.body; + const filePath = path.join(req.params.filePath, req.params[0] || ''); + + if (!currentName || !newName) { + return res.status(400).send('Both currentName and newName must be provided.'); + } + + const currentPath = path.join('cdn-files', userId || '', filePath, currentName); + const newPath = path.join('cdn-files', userId, filePath, newName); + + try { + await fs.promises.rename(currentPath, newPath); + + const data = await fs.promises.readFile('file_info.json', 'utf8'); + let fileInfo = JSON.parse(data); + + let found = false; + for (let i = 0; i < fileInfo.length; i++) { + if (fileInfo[i].fileName === currentName) { + fileInfo[i].fileName = newName; + found = true; + break; + } + } + + if (found) { + await fs.promises.writeFile('file_info.json', JSON.stringify(fileInfo, null, 2), 'utf8'); + } + + res.status(200).send('L\'opération a été effectuée avec succès.'); + } catch (err) { + console.error(err); + return res.status(500).send('Erreur lors du changement de nom du fichier.'); + } +}); + +router.post('/dashboard/delete', authMiddleware, (req, res) => { + const userId = req.userData.name; + const { filename } = req.body; + + if (!userId || !filename) { + return res.status(400).json({ message: 'Identifiant d\'utilisateur ou nom de fichier manquant pour la suppression du fichier.' }); + } + + const userFolderPath = path.join('cdn-files', userId); + + function findAndDeleteFile(folderPath) { + const filesInFolder = fs.readdirSync(folderPath); + + for (const file of filesInFolder) { + const filePath = path.join(folderPath, file); + + if (fs.statSync(filePath).isDirectory()) { + findAndDeleteFile(filePath); + } else if (file === filename) { + try { + fs.unlinkSync(filePath); + console.log('File deleted:', filePath); + return true; + } catch (error) { + console.error('Error deleting file:', error); + return false; + } + } + } + + return false; + } + + const fileDeleted = findAndDeleteFile(userFolderPath); + + if (fileDeleted) { + res.status(200).json({ status: 'success', message: 'Le fichier a été supprimé avec succès.' }); + } else { + res.status(404).json({ status: 'error', message: 'Le fichier que vous essayez de supprimer n\'existe pas.' }); + } +}); + +const ncpAsync = (source, destination) => { + return new Promise((resolve, reject) => { + ncp(source, destination, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +}; + +router.post('/dashboard/movefile', authMiddleware, async (req, res) => { + const fileName = req.body.fileName; + const folderName = req.body.folderName; + + const data = await fs.promises.readFile('user.json', 'utf8'); + const users = JSON.parse(data); + const user = users.find(user => user.id === req.user.id); + + if (!user) { + console.error('User not found in user.json'); + return res.status(500).send('Erreur lors du déplacement du fichier.'); + } + + const userId = user.name; + + if (!fileName || !userId) { + console.error('fileName or userId is undefined'); + return res.status(500).send('Erreur lors du déplacement du fichier.'); + } + + const sourcePath = path.join('cdn-files', userId, fileName); + + let destinationDir; + if (folderName && folderName.trim() !== '') { + destinationDir = path.join('cdn-files', userId, folderName); + } else { + destinationDir = path.join('cdn-files', userId); + } + + const destinationPath = path.join(destinationDir, fileName); + + try { + const normalizedSourcePath = path.normalize(sourcePath); + console.log('Full Source Path:', normalizedSourcePath); + + if (fs.existsSync(normalizedSourcePath)) { + await fs.promises.access(destinationDir); + + await ncpAsync(normalizedSourcePath, destinationPath); + + await fs.promises.unlink(normalizedSourcePath); + } else { + console.log('File does not exist'); + } + + res.redirect('/dpanel/dashboard'); + } catch (err) { + console.error(err); + return res.status(500).send('Erreur lors du déplacement du fichier.'); + } +}); + +router.post('/dashboard/movefile/:folderName', authMiddleware, async (req, res) => { + + const fileName = req.body.fileName; + const newFolderName = req.body.folderName; + const oldFolderName = req.params.folderName; + const userId = req.user && req.user._json ? req.userData.name : undefined; + + if (!fileName || !userId || !oldFolderName || !newFolderName) { + console.error('fileName, userId, oldFolderName, or newFolderName is undefined'); + return res.status(500).send('Erreur lors du déplacement du fichier.'); + } + + const sourcePath = path.join(process.cwd(), 'cdn-files', userId, oldFolderName, fileName); + const destinationDir = path.join(process.cwd(), 'cdn-files', userId, newFolderName); + const destinationPath = path.join(destinationDir, fileName); + + try { + const normalizedSourcePath = path.normalize(sourcePath); + console.log('Full Source Path:', normalizedSourcePath); + + if (fs.existsSync(normalizedSourcePath)) { + await fs.promises.access(destinationDir, fs.constants.W_OK); + + await fs.promises.rename(normalizedSourcePath, destinationPath); + } else { + console.log('File does not exist'); + } + + res.redirect('/dpanel/dashboard'); + } catch (err) { + console.error(err); + return res.status(500).send('Erreur lors du déplacement du fichier.'); + } +}); + +router.delete('/dashboard/deletefolder/:folderName', authMiddleware, (req, res) => { + const userId = req.userData.name; + const { filename } = req.body; + + const userFolderPath = path.join('cdn-files', userId || ''); + const folderPath = path.join(userFolderPath, req.params.folderName || ''); + + if (!fs.existsSync(folderPath)) { + return res.status(404).json({ error: 'Le dossier spécifié n\'existe pas.' }); + } + + fs.rmdirSync(folderPath, { recursive: true }); + + res.json({ deleted: true, success: 'Dossier supprimé avec succès.' }); +}); + +router.delete('/dashboard/deletefolder/:folderName', authMiddleware, (req, res) => { + const userId = req.userData.name; + const folderName = req.params.folderName; + const folderPath = path.join('cdn-files', userId, folderName); + + fs.rmdir(folderPath, { recursive: true }, (err) => { + if (err) { + console.error(err); + return res.status(500).json({ error: 'Erreur lors de la suppression du dossier.' }); + } + res.json({ deleted: true, success: 'Dossier supprimé avec succès.' }); + }); +}); + +router.post('/dashboard/deletefile/:folderName', authMiddleware, (req, res) => { + const userId = req.userData.name; + const { filename } = req.body; + + const userFolderPath = path.join('cdn-files', userId || ''); + const filePath = path.join(userFolderPath, req.params.folderName, filename || ''); + + if (!fs.existsSync(filePath)) { + return res.status(404).json({ error: 'Le fichier spécifié n\'existe pas.' }); + } + + fs.unlink(filePath, (err) => { + if (err) { + console.error(err); + return res.status(500).json({ error: 'Erreur lors de la suppression du fichier.' }); + } + res.json({ deleted: true, success: 'Fichier supprimé avec succès.' }); + }); +}); + +router.get('/upload', authMiddleware, (req, res) => { + res.render('upload'); +}); + +router.use(fileUpload({ + limits: { fileSize: 15 * 1024 * 1024 * 1024 }, +})); + +router.post('/upload', authMiddleware, async (req, res) => { + try { + if (!req.files || Object.keys(req.files).length === 0) { + return res.status(400).send('5410 - Erreur de téléchargement, veuillez retenter ultérieurement.'); + } + + const file = req.files.file; + const userId = req.userData.name; + const Id = req.userData.id; + const uploadDir = path.join('cdn-files', userId); + const originalFileName = file.name; + const domain = config.domain || 'mydomain.com'; + let expiryDate = req.body.expiryDate; + let password = req.body.password; + + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + + file.mv(path.join(uploadDir, originalFileName), async (err) => { + if (err) { + console.error(err); + return res.status(500).send({ message: 'Erreur lors du téléchargement du fichier.' }); + } + + const fileExtension = path.extname(originalFileName).toLowerCase(); + + let encryptedPassword = ''; + if (password) { + const algorithm = 'aes-256-cbc'; + const key = crypto.scryptSync(password, 'salt', 32); + const iv = Buffer.alloc(16, 0); + const cipher = crypto.createCipheriv(algorithm, key, iv); + encryptedPassword = cipher.update('', 'utf8', 'hex'); + encryptedPassword += cipher.final('hex'); + } + + const fileInfo = { + fileName: originalFileName, + expiryDate: expiryDate || '', + password: encryptedPassword, + Id: Id, + path: path.join(uploadDir, originalFileName) + }; + + if (expiryDate || password) { + let data = []; + if (fs.existsSync('file_info.json')) { + const existingData = await fs.promises.readFile('file_info.json', 'utf8'); + data = JSON.parse(existingData); + } + data.push(fileInfo); + await fs.promises.writeFile('file_info.json', JSON.stringify(data, null, 2)); + } + + res.redirect('/dpanel/dashboard'); + }); + } catch (error) { + console.error(error); + return res.status(500).send({ message: 'Erreur lors du téléchargement du fichier.' }); + } +}); + +const User = require('../user.json'); +const setup = JSON.parse(fs.readFileSync(path.join(__dirname, '../setup.json'), 'utf8')); + +router.get('/dashboard/admin', authMiddleware, async (req, res) => { + try { + res.render('paramAdmin', { users: User, setup: setup }); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + + +router.get('/dashboard/admin/users', authMiddleware, async (req, res) => { + try { + let currentPage = Number(req.query.page) || 1; + let limit = Number(req.query.limit) || 10; + + let rawdata = fs.readFileSync(path.join(__dirname, '../user.json')); + let users = JSON.parse(rawdata); + + let totalUsers = users.length; + let pages = Math.ceil(totalUsers / limit); + + let start = (currentPage - 1) * limit; + let end = start + limit; + let usersForPage = users.slice(start, end); + + res.render('paramAdminUser', { users: usersForPage, setup: setup, pages: pages, currentPage: currentPage, limit: limit }); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +router.get('/dashboard/admin/settingsetup', authMiddleware, async (req, res) => { + try { + res.render('paramAdminSettingSetup', { users: User, setup: setup }); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +const osUtils = require('os-utils'); + +const Convert = require('ansi-to-html'); +const convert = new Convert(); + +router.get('/dashboard/admin/stats&logs', authMiddleware, async (req, res) => { + try { + const uptime = os.uptime(); + const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024; + + osUtils.cpuUsage(function(cpuUsage) { + fs.readdir('./logs', (err, files) => { + if (err) { + console.error(err); + res.status(500).send('Error reading logs'); + return; + } + + const logs = files.map(file => { + return fs.promises.readFile(path.join('./logs', file), 'utf8') + .then(content => { + content = convert.toHtml(content); + return { name: file, content: content }; + }) + .catch(err => { + console.error(err); + }); + }); + + Promise.all(logs).then(completed => { + res.render('paramAdminStats&Logs', { users: User, setup: setup, uptime, memoryUsage, cpuUsage, logs: completed }); + }); + }); + }); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +router.get('/dashboard/admin/Privacy&Security', authMiddleware, async (req, res) => { + try { + const files = await fs.promises.readdir('./report'); + const reports = files.filter(file => file.endsWith('.json')).map(file => { + return fs.promises.readFile(path.join('./report', file), 'utf8') + .then(content => { + return { name: file, content: content }; + }) + .catch(err => { + console.error(err); + }); + }); + + Promise.all([Promise.all(reports)]).then(([completedReports]) => { + res.render('paramAdminPrivacy&Security', { users: User, reports: completedReports }); + }); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +router.post('/dashboard/update-role', authMiddleware, async (req, res) => { + try { + const { id, role } = req.body; + + const user = User.find(user => user.id === id); + + if (user) { + user.role = role; + } + + fs.writeFileSync(path.join(__dirname, '../user.json'), JSON.stringify(User, null, 2)); + + res.redirect('/dpanel/dashboard/admin'); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +router.post('/dashboard/update-setup', authMiddleware, async (req, res) => { + try { + let setup = JSON.parse(fs.readFileSync(path.join(__dirname, '../../setup.json'))); + + if (!req.body.ldap || !req.body.ldap.enabled) { + delete setup.ldap; + } else { + setup.ldap = req.body.ldap; + } + + if (!req.body.discord || !req.body.discord.enabled) { + delete setup.discord; + } else { + setup.discord = req.body.discord; + } + + setup.domain = req.body.domain; + setup.uptime = req.body.uptime; + + fs.writeFileSync(path.join(__dirname, '../../setup.json'), JSON.stringify(setup, null, 2)); + + res.redirect('/dpanel/dashboard/admin'); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } +}); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..9dcb344 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,71 @@ +const express = require('express'); +const router = express.Router(); +const path = require('path'); +const { checkUpdates } = require('../Middlewares/checkUpdate'); +const { applyUpdate, restartCDN } = require('../models/updateManager'); +const { logger, ErrorLogger, logRequestInfo } = require('../config/logs'); +const util = require('util'); +const exec = util.promisify(require('child_process').exec); +const authMiddleware = require('../Middlewares/authMiddleware'); +const fs = require('fs'); + +router.get('/', (req, res) => { + res.render('acces-denied'); +}); + +router.get('/attachments', (req, res) => { + res.render('acces-denied'); +}); + +router.get('/checkupdate',authMiddleware, checkUpdates); + +router.get('/applyupdate',authMiddleware, async (req, res) => { + const updateUrl = 'https://apollon.dinawo.fr/api/download/all'; + const updateFolder = path.join(__dirname, '..'); + + try { + logger.info('------Before applying the update------'); + await applyUpdate(updateUrl, updateFolder); + + logger.info('------After applying the update------'); + res.json({ + success: true, + message: 'Mise à jour appliquée avec succès. Pensé à redémarrer le serveur pour que la MàJ soit prise en compte. (systemctl restart cdn).' + }); + } catch (error) { + ErrorLogger.error('Error applying update:', error); + + return res.status(500).json({ success: false, message: 'Erreur lors de l\'application de la mise à jour.' }); + } +}); + +router.get('/translateAll', async (req, res) => { + const targetLanguage = req.query.lang || 'en'; + const viewsFolder = path.join(__dirname, '../views'); + + try { + const translatedFiles = []; + + const files = fs.readdirSync(viewsFolder); + for (const file of files) { + if (file.endsWith('.ejs')) { + const filePath = path.join(viewsFolder, file); + const translatedContent = await translateEJSFile(filePath, targetLanguage); + + if (translatedContent !== null) { + translatedFiles.push({ + fileName: file, + translatedContent, + }); + } + } + } + + res.json(translatedFiles); + } catch (error) { + console.error('Erreur lors de la traduction de tous les fichiers EJS :', error.message); + res.status(500).json({ error: 'Erreur de traduction' }); + } +}); + +module.exports = router; diff --git a/server.js b/server.js new file mode 100644 index 0000000..7bc941d --- /dev/null +++ b/server.js @@ -0,0 +1,128 @@ +const express = require('express'); +const session = require('express-session'); +const passport = require('passport'); +const bodyParser = require('body-parser'); +const { logger, logRequestInfo, ErrorLogger } = require('./config/logs'); +const path = require("path"); +require('dotenv').config(); +const { version } = require('./package.json'); +const axios = require('axios'); +const app = express(); +const flash = require('connect-flash'); +const fs = require('fs'); +const SystemReport = require('./models/reportManager.js'); + +let setup; +try { + setup = JSON.parse(fs.readFileSync('setup.json', 'utf8')); +} catch (err) { + console.error('Error reading setup.json:', err); + process.exit(1); +} + +if (setup.discord !== undefined) { + require('./models/Passport-Discord.js'); +} + +if (setup.ldap !== undefined) { + require('./models/Passport-ActiveDirectory.js'); +} + +app.use(express.static(path.join(__dirname, 'public'))); + +app.get(['/user.json', '/file_info.json', '/setup.json'], (req, res) => { + res.status(403).json({ error: 'Access Denied' }); +}); +app.use(express.urlencoded({ extended: true })); + +app.use(session({ + secret: '63a69c252dfe0bb20650b6365b48dc99ad6c7eac19faed62670e73a071c54236e2faf04ee009919592def437b98d3c726c40a56ef1d8759878c1703a93244aa3', + resave: false, + saveUninitialized: true, + cookie: { secure: false } +}));app.use(passport.initialize()); +app.use(passport.session()); + +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); +app.use(flash()); + +const indexRoute = require('./routes/index.js'); +const AuthRoute = require('./routes/auth.js'); +const DpanelRoute = require('./routes/dpanel.js'); +const AttachmentsRoute = require('./routes/attachments.js'); + +app.set('view engine', 'ejs'); +app.set('views', __dirname + '/views'); + +app.use('/', indexRoute); +app.use('/auth', AuthRoute); +app.use('/dpanel', DpanelRoute); +app.use('/attachments', AttachmentsRoute); +app.use('/public', express.static(path.join(__dirname, 'public'))); + +app.use(logRequestInfo); + +const cron = require('node-cron'); + +cron.schedule('00 03 * * *', async () => { + try { + const report = await SystemReport.generate(); + if (report !== null) { + logger.info('System error report generated successfully'); + } + } catch (err) { + ErrorLogger.error('Error generating report :', err); + } +}); + +cron.schedule('0 * * * *', async () => { + try { + const fileInfoData = await fs.promises.readFile(path.join(__dirname, 'file_info.json'), 'utf8'); + const fileInfo = JSON.parse(fileInfoData); + + const now = new Date(); + + for (let index = fileInfo.length - 1; index >= 0; index--) { + const file = fileInfo[index]; + const expiry = new Date(file.expiryDate); + + if ((file.expiryDate && expiry <= now) || !(await fileExists(file.path))) { + fileInfo.splice(index, 1); + } + } + + await fs.promises.writeFile(path.join(__dirname, 'file_info.json'), JSON.stringify(fileInfo, null, 2), 'utf8'); + logger.info('Successfully checked file expirations and updated file_info.json'); + } catch (err) { + ErrorLogger.error(`Failed to check file expirations: ${err}`); + } +}); + +async function fileExists(filePath) { + try { + await fs.promises.access(filePath); + return true; + } catch { + return false; + } +} + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.clear(); + if (logger) { + logger.info(`🚀 Your server is available and running on port ${PORT}`); + logger.info(`⚜️ Application developed by Dinawo, part of the SwiftLogic Labs group`); + logger.info(`♨️ Version: ${version}`); + console.log(''); + const filesData = fs.readFileSync('files.json', 'utf8'); + const files = JSON.parse(filesData); + + const numberOfFiles = Object.values(files).flat().length; + + logger.info(`🔰 Number of files activated during server startup: ${numberOfFiles}`); + } else { + console.error('🔴 Logger is not initialized'); + } +}); diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9ac8c56 --- /dev/null +++ b/setup.py @@ -0,0 +1,95 @@ +import json +import os +from getpass import getpass + +setup_file_path = '/home/cdn-app/setup.json' # Specify the full path for setup.json + +setup_data = {} + +def print_with_color(text, color): + colors = { + 'reset': '\033[0m', + 'green': '\033[92m', + 'yellow': '\033[93m', + 'cyan': '\033[96m', + 'bold': '\033[1m' + } + print(f"{colors[color]}{text}{colors['reset']}") + +def create_service(): + service_name = 'cdn.service' + # Create the service file + service_file_path = f'/etc/systemd/system/{service_name}' + with open(service_file_path, 'w') as service_file: + service_file.write(f''' +[Unit] +Description=CDN-APP, dinawoSR Inc +Documentation=https://cdn-app.dinawo.fr + +[Service] +ExecStart=/root/.nvm/versions/node/v16.9.0/bin/node /home/cdn-app/server.js +Restart=always +User=root +Environment=PATH=/usr/bin:/usr/local/bin:/root/.nvm/versions/node/v16.9.0/bin +Environment=NODE_ENV=production +WorkingDirectory=/home/cdn-app + + +[Install] +WantedBy=multi-user.target +''') + + # Restart systemd to acknowledge the new service + os.system('systemctl daemon-reload') + os.system('systemctl enable cdn.service') + os.system('systemctl start cdn.service') + + print_with_color(f"Service {service_name} created successfully.", 'green') + +# Loader +print_with_color("Welcome to server configuration script.", 'cyan') + +# Change the current working directory to /home/cdn-app +os.chdir('/home/cdn-app') + +# LDAP information input +setup_data['ldap'] = { + 'url': input("LDAP server URL (ldap://your-ip): "), + 'baseDN': input("Base DN (DC=DINAWOSRINC,DC=LAN): "), + 'username': input("Username (Account Sync): "), + 'password': getpass("Password (Password Account Sync): ") +} + +# Prompt for domain name +setup_data['domain'] = input("Enter the domain name (cdn.domain.com): ") + +# Prompt for uptime link +setup_data['uptime'] = input("Enter the uptime link (uptime.domain.com): ") + +# Check Node.js version +node_version = os.popen('node -v').read().strip() +required_node_version = 'v16.9.0' +if node_version != required_node_version: + print_with_color(f"Error: Incorrect Node.js version. Required: {required_node_version}, Found: {node_version}.", 'yellow') + exit(1) + + +# Update npm packages with proper permissions +os.system('npm install') + + +# Set full permissions for /home/cdn-app +os.system('chown -R root:root /home/cdn-app') +os.system('chmod -R 777 /home/cdn-app') + +# Check and modify file permissions +os.system('chmod +x /home/cdn-app/server.js') + +# Save information in the setup.json file +with open(setup_file_path, 'w') as file: + json.dump(setup_data, file) + +print_with_color(f"Configuration saved in the {setup_file_path} file.", 'green') + +# Create the service +create_service() diff --git a/views/AuthLogin.ejs b/views/AuthLogin.ejs new file mode 100644 index 0000000..d55964b --- /dev/null +++ b/views/AuthLogin.ejs @@ -0,0 +1,149 @@ + + + + + + + + + +
+
+ The modern CDN for secure file transfer
+Power your project with our self-hosted CDN
+Experience exceptional performance and ultra-fast content distribution.
Created by SwiftLogic Labs.
+ Join our Discord Server +| Nom du fichier | +Taille | +Action | +|
|---|---|---|---|
| <%= file.name %> | + <% } else { %> +<%= file.name %> | + <% } %>+ <% if (file.type === 'folder') { %> + Dossier + <% } else { %> + <% + const fileSizeInBytes = file.size; + let fileSize; + if (fileSizeInBytes < 1024 * 1024) { + fileSize = `${(fileSizeInBytes / 1024).toFixed(2)} Ko`; + } else if (fileSizeInBytes < 1024 * 1024 * 1024) { + fileSize = `${(fileSizeInBytes / (1024 * 1024)).toFixed(2)} Mo`; + } else { + fileSize = `${(fileSizeInBytes / (1024 * 1024 * 1024)).toFixed(2)} Go`; + } + %> + <%= fileSize %> + <% } %> + | ++ <% if (file.type === 'folder') { %> + + + Accéder + + <% } else { %> + + + + + + <% } %> + | +
Code d'erreur: U5nV4l1dUs3r_FileRetrievalError
+