diff --git a/.drone.yml b/.drone.yml index b072763..74f76ce 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,7 +20,7 @@ steps: repo: swiftlogiclabs/cdn-app-insider tags: - latest - - v1.0.0-beta.14 + - v1.0.0-beta.16 dockerfile: Dockerfile username: from_secret: docker_username diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/CDN-APP-INSIDER.iml b/.idea/CDN-APP-INSIDER.iml deleted file mode 100644 index 0c8867d..0000000 --- a/.idea/CDN-APP-INSIDER.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml deleted file mode 100644 index d8e9561..0000000 --- a/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml deleted file mode 100644 index d23208f..0000000 --- a/.idea/jsLibraryMappings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 7588260..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Middlewares/discordWebhookSuspisiousAlertMiddleware.js b/Middlewares/discordWebhookSuspisiousAlertMiddleware.js index df27991..8ecac13 100644 --- a/Middlewares/discordWebhookSuspisiousAlertMiddleware.js +++ b/Middlewares/discordWebhookSuspisiousAlertMiddleware.js @@ -3,25 +3,47 @@ const fs = require('fs'); const path = require('path'); const setupFilePath = path.join(__dirname, '../data', 'setup.json'); +function isIpAllowed(ip, allowedIps) { + return allowedIps.some(allowedIp => { + + if (allowedIp.includes('/')) { + const [network, bits] = allowedIp.split('/'); + const ipLong = ip2long(ip); + const networkLong = ip2long(network); + const mask = ~(2 ** (32 - bits) - 1); + return (ipLong & mask) === (networkLong & mask); + } + + return ip === allowedIp; + }); +} + +function ip2long(ip) { + return ip.split('.') + .reduce((long, octet) => (long << 8) + parseInt(octet), 0) >>> 0; +} + function sendDiscordWebhook(url, req, statusCode) { if (!url) { return; } - const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`; - + const setupData = JSON.parse(fs.readFileSync(setupFilePath, 'utf-8')); + const allowedIps = setupData[0].allowedIps || []; const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + if (isIpAllowed(ip, allowedIps)) { + return; + } else { + } + + const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`; const statusEmoji = [200, 302].includes(statusCode) ? '✅' : '❌'; const statusMessage = `**Statut:** ${statusEmoji} (${statusCode})`; - const timestamp = new Date().toLocaleString('fr-FR', { timeZone: 'UTC', hour12: false }); const userAgent = req.get('User-Agent'); - const { v4: uuidv4 } = require('uuid'); - const webhookId = uuidv4(); - const userId = req.user ? req.user.id : 'Inconnu'; const userName = req.user ? req.user.name : 'Inconnu'; @@ -40,17 +62,19 @@ function sendDiscordWebhook(url, req, statusCode) { } }] }; + axios.post(url, data) .then(response => { }) .catch(error => { + console.error('Erreur lors de l\'envoi du webhook:', error); }); } + function discordWebhookSuspisiousAlertMiddleware(req, res, next) { const setupData = JSON.parse(fs.readFileSync(setupFilePath, 'utf-8')); - - + res.on('finish', () => { const discordWebhookUrl = setupData[0].webhooks_discord; sendDiscordWebhook(discordWebhookUrl, req, res.statusCode); diff --git a/models/Passport-ActiveDirectory.js b/models/Passport-ActiveDirectory.js index ac1c421..931597a 100644 --- a/models/Passport-ActiveDirectory.js +++ b/models/Passport-ActiveDirectory.js @@ -5,6 +5,7 @@ const path = require('path'); const { getUserData } = require('../Middlewares/watcherMiddleware'); const setupFilePath = path.join(__dirname, '../data', 'setup.json'); +const userFilePath = path.join(__dirname, '../data', 'user.json'); const setupData = JSON.parse(fs.readFileSync(setupFilePath, 'utf-8')); @@ -40,9 +41,19 @@ passport.deserializeUser(async (id, done) => { if (user) { return done(null, user); } else { - return done(new Error('User not valid'), null); + const newUser = { + id: Date.now().toString(), + name: id, + role: 'user' + }; + + users.push(newUser); + + fs.writeFile(userFilePath, JSON.stringify(users, null, 2), (err) => { + if (err) return done(err); + return done(null, newUser); + }); } }); module.exports = passport; - diff --git a/package-lock.json b/package-lock.json index 195d46e..dd8c0f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@cdn-app/insider-swiftlogic-labs-dinawo", - "version": "1.0.0-beta.14", + "name": "@cdn-app/insider-myaxrin-labs-dinawo", + "version": "1.0.0-beta.16", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@cdn-app/insider-swiftlogic-labs-dinawo", - "version": "1.0.0-beta.14", + "name": "@cdn-app/insider-myaxrin-labs-dinawo", + "version": "1.0.0-beta.16", "license": "ISC", "dependencies": { "@auth/express": "^0.5.1", @@ -64,7 +64,7 @@ }, "devDependencies": { "daisyui": "^4.5.0", - "nodemon": "^3.1.0" + "nodemon": "^3.1.4" } }, "node_modules/@alloc/quick-lru": { @@ -3199,9 +3199,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", - "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", + "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -7701,9 +7701,9 @@ "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==" }, "nodemon": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", - "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", + "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==", "dev": true, "requires": { "chokidar": "^3.5.2", diff --git a/package.json b/package.json index 35fd156..7c35d85 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@cdn-app/insider-myaxrin-labs-dinawo", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "description": "", "main": "server.js", "scripts": { - "start": "node server.js" + "start": "nodemon server.js", + "dev": "node server.js" }, "author": "Dinawo - Group Myaxrin Labs", "license": "ISC", @@ -64,6 +65,6 @@ }, "devDependencies": { "daisyui": "^4.5.0", - "nodemon": "^3.1.0" + "nodemon": "^3.1.4" } } diff --git a/public/css/styles.css b/public/css/styles.css index 35dc5b8..40e7f70 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -193,4 +193,6 @@ body.white-theme .animated-button:focus { .rainbow-effect { animation: rainbowBorder 2s linear infinite; -} \ No newline at end of file +} + + diff --git a/public/js/dashboard.js b/public/js/dashboard.js index 6e34042..926ed3b 100644 --- a/public/js/dashboard.js +++ b/public/js/dashboard.js @@ -1,210 +1,227 @@ - - document.addEventListener('DOMContentLoaded', function () { - const copyButtons = document.querySelectorAll('.copy-button'); - - copyButtons.forEach(copyButton => { - copyButton.addEventListener("click", () => { - const fileContainer = copyButton.closest('tr'); - const fileLink = fileContainer.querySelector('.file-link'); - fileLink.style.display = "block"; - fileLink.select(); - document.execCommand("copy"); - fileLink.style.display = "none"; - copyButton.textContent = "Lien copié !"; - setTimeout(() => { - copyButton.textContent = "Copier le lien"; - }, 2000); - }); - }); - - const filterForm = document.getElementById('filterForm'); - const extensionFilter = document.getElementById('extensionFilter'); - const fileSearchInput = document.getElementById('fileSearch'); - const styleSwitcherButton = document.getElementById('styleSwitcher'); - const icon = document.getElementById('themeIcon'); - - const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - let isDarkMode = darkModeMediaQuery.matches; - - function toggleDarkMode() { - isDarkMode = !isDarkMode; - document.body.classList.toggle('dark-mode', isDarkMode); - - if (isDarkMode) { - icon.classList.remove('bi-brightness-high-fill'); - icon.classList.add('bi-moon-fill'); - icon.innerHTML = ''; - } else { - icon.classList.remove('bi-moon-fill'); - icon.classList.add('bi-brightness-high-fill'); - icon.innerHTML = ''; - } - } - - function applyStyleMode() { - document.body.classList.toggle('dark-mode', isDarkMode); - - if (isDarkMode) { - icon.classList.remove('bi-brightness-high-fill'); - icon.classList.add('bi-moon-fill'); - icon.innerHTML = ''; - } else { - icon.classList.remove('bi-moon-fill'); - icon.classList.add('bi-brightness-high-fill'); - icon.innerHTML = ''; - } - } - - darkModeMediaQuery.addListener(applyStyleMode); - applyStyleMode(); - - styleSwitcherButton.addEventListener('click', toggleDarkMode); - + document.addEventListener('DOMContentLoaded', function() { + document.querySelectorAll('.file-size').forEach(function(element) { + const size = parseInt(element.getAttribute('data-size')); + element.textContent = formatFileSize(size); }); - document.addEventListener('DOMContentLoaded', function () { - const deleteFolderButtons = document.querySelectorAll('.delete-folder-button'); - - deleteFolderButtons.forEach(deleteButton => { - deleteButton.addEventListener('click', async () => { - const fileContainer = deleteButton.closest('tr'); - const folderName = fileContainer.querySelector('td:first-child').textContent; - - Swal.fire({ - title: 'Êtes-vous sûr?', - text: `La suppression du dossier "${folderName}" est irréversible!`, - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#d33', - cancelButtonColor: '#3085d6', - confirmButtonText: 'Supprimer', - cancelButtonText: 'Annuler', - }).then(async (result) => { - if (result.isConfirmed) { - try { - const response = await fetch(`/api/dpanel/dashboard/deletefolder/${folderName}`, { - method: 'DELETE', - }); - - if (response.ok) { - fileContainer.remove(); - Swal.fire({ - position: 'top', - icon: 'success', - title: 'Le dossier a été supprimé avec succès.', - showConfirmButton: false, - timer: 1800, - toast: true - }); - } else { - Swal.fire({ - position: 'top', - icon: 'error', - title: 'La suppression du dossier a échoué', - showConfirmButton: false, - timer: 1800, - toast: true - }); - } - } catch (error) { - console.error('Erreur lors de la suppression du dossier:', error); - Swal.fire('Erreur!', 'Une erreur s\'est produite lors de la suppression du dossier.', 'error'); - } - } - }); - }); + document.getElementById('searchButton').addEventListener('click', searchFiles); + document.getElementById('newFolderBtn').addEventListener('click', showNewFolderModal); + + document.querySelectorAll('.delete-folder-button').forEach(button => { + button.addEventListener('click', function() { + const folderName = this.getAttribute('data-folder-name'); + confirmDeleteFolder(folderName); }); }); - - - window.onload = function() { - var newFolderModalBtn = document.getElementById('newFolderModalBtn'); - if(newFolderModalBtn) { - newFolderModalBtn.addEventListener('click', function (e) { - e.preventDefault(); - Swal.fire({ - title: 'Nouveau dossier', - input: 'text', - inputPlaceholder: 'Entrer le nom du nouveau dossier', - confirmButtonText: 'Créer', - showCancelButton: true, - cancelButtonText: 'Annuler', - preConfirm: (folderName) => { - if (!folderName) { - Swal.showValidationMessage('Le nom du dossier ne peut pas être vide.'); - } - return folderName; - } - }).then(result => { - if (result.isConfirmed) { - const folderName = result.value.trim(); - fetch('/api/dpanel/dashboard/newfolder', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ folderName }), - }) - .then(response => { - if (response.ok) { - return response.json(); - } else { - return response.json().then(error => Promise.reject(error)); - } - }) - .then(result => { - Swal.fire({ - position: 'top', - icon: 'success', - title: 'Le dossier a été créé avec succès.', - showConfirmButton: false, - timer: 2000, - toast: true - }) - .then(() => { - location.reload(); - }); - }) - .catch(error => { - Swal.fire({ - position: 'top', - icon: 'error', - title: 'Erreur lors de la création du dossier.', - text: error.message, - showConfirmButton: false, - timer: 2350, - toast: true - }) - }); - } - }); + + document.querySelectorAll('.delete-file-button').forEach(button => { + button.addEventListener('click', function() { + const fileName = this.getAttribute('data-file-name'); + confirmDelete(fileName); + }); + }); + + document.querySelectorAll('.copy-button').forEach(button => { + button.addEventListener('click', function() { + const fileUrl = this.getAttribute('data-file-url'); + copyFileLink(fileUrl); + }); + }); + + document.querySelectorAll('.rename-file-btn').forEach(button => { + button.addEventListener('click', function() { + const fileName = this.getAttribute('data-file-name'); + const folderName = this.getAttribute('data-folder-name'); + renameFile(folderName, fileName); + }); + }); + + document.querySelectorAll('.move-file-btn').forEach(button => { + button.addEventListener('click', function() { + const fileName = this.getAttribute('data-file-name'); + showMoveFileModal(fileName); + }); + }); + + document.getElementById('confirmMoveFile').addEventListener('click', moveFile); + document.getElementById('themeSwitcher').addEventListener('click', toggleDarkMode); + + initTheme(); + + document.addEventListener('DOMContentLoaded', function () { + const accountDropdownBtn = document.getElementById('accountDropdownBtn'); + const accountDropdownMenu = document.getElementById('accountDropdownMenu'); + + accountDropdownBtn.addEventListener('click', function (e) { + e.stopPropagation(); + accountDropdownMenu.classList.toggle('show'); + }); + + document.addEventListener('click', function (e) { + if (!accountDropdownBtn.contains(e.target) && !accountDropdownMenu.contains(e.target)) { + accountDropdownMenu.classList.remove('show'); + } + }); + }); + + $('.modal').modal({ + show: false + }); + + const metadataLink = document.querySelector('a[onclick="displayMetadata()"]'); + if (metadataLink) { + metadataLink.addEventListener('click', function(event) { + event.preventDefault(); + displayMetadata(); }); } - }; + document.querySelectorAll('[onclick^="showFileInfo"]').forEach(link => { + link.addEventListener('click', function(event) { + event.preventDefault(); + const fileName = this.getAttribute('onclick').match(/'([^']+)'/)[1]; + showFileInfo(fileName); + }); + }); + }); - var modal = document.getElementById('patchNoteModal'); - - if (isDarkMode()) { - modal.classList.add('dark-mode'); - } else { - modal.classList.add('light-mode'); + function formatFileSize(fileSizeInBytes) { + if (fileSizeInBytes < 1024) return fileSizeInBytes + ' octets'; + else if (fileSizeInBytes < 1048576) return (fileSizeInBytes / 1024).toFixed(2) + ' Ko'; + else if (fileSizeInBytes < 1073741824) return (fileSizeInBytes / 1048576).toFixed(2) + ' Mo'; + else return (fileSizeInBytes / 1073741824).toFixed(2) + ' Go'; } - - $(document).ready(function () { - $('#accountDropdownBtn').on('click', function () { - $('#accountDropdownMenu').toggleClass('show'); - }); - - $(document).on('click', function (e) { - if (!$('#accountDropdownBtnGroup').is(e.target) && $('#accountDropdownBtnGroup').has(e.target).length === 0) { - $('#accountDropdownMenu').removeClass('show'); - } - }); - }); + function searchFiles() { + const input = document.getElementById('searchInput'); + const filter = input.value.toUpperCase(); + const table = document.getElementById('fileTable'); + const tr = table.getElementsByTagName('tr'); - function confirmDelete(filename) { + for (let i = 1; i < tr.length; i++) { + const td = tr[i].getElementsByTagName('td')[0]; + if (td) { + const txtValue = td.textContent || td.innerText; + if (txtValue.toUpperCase().indexOf(filter) > -1) { + tr[i].style.display = ""; + } else { + tr[i].style.display = "none"; + } + } + } + } + + function showNewFolderModal() { + Swal.fire({ + title: 'Nouveau dossier', + input: 'text', + inputPlaceholder: 'Entrer le nom du nouveau dossier', + confirmButtonText: 'Créer', + showCancelButton: true, + cancelButtonText: 'Annuler', + preConfirm: (folderName) => { + if (!folderName) { + Swal.showValidationMessage('Le nom du dossier ne peut pas être vide.'); + } + return folderName; + } + }).then(result => { + if (result.isConfirmed) { + createNewFolder(result.value); + } + }); + } + + function createNewFolder(folderName) { + fetch('/api/dpanel/dashboard/newfolder', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ folderName }), + }) + .then(response => { + if (response.ok) { + return response.json(); + } else { + return response.json().then(error => Promise.reject(error)); + } + }) + .then(result => { + Swal.fire({ + position: 'top', + icon: 'success', + title: 'Le dossier a été créé avec succès.', + showConfirmButton: false, + timer: 2000, + toast: true + }).then(() => { + location.reload(); + }); + }) + .catch(error => { + Swal.fire({ + position: 'top', + icon: 'error', + title: 'Erreur lors de la création du dossier.', + text: error.message, + showConfirmButton: false, + timer: 2350, + toast: true + }); + }); + } + + function confirmDeleteFolder(folderName) { + Swal.fire({ + title: 'Êtes-vous sûr?', + text: `La suppression du dossier "${folderName}" est irréversible!`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#d33', + cancelButtonColor: '#3085d6', + confirmButtonText: 'Supprimer', + cancelButtonText: 'Annuler', + }).then((result) => { + if (result.isConfirmed) { + deleteFolder(folderName); + } + }); + } + + function deleteFolder(folderName) { + fetch(`/api/dpanel/dashboard/deletefolder/${folderName}`, { + method: 'DELETE', + }) + .then(response => { + if (response.ok) { + Swal.fire({ + position: 'top', + icon: 'success', + title: 'Le dossier a été supprimé avec succès.', + showConfirmButton: false, + timer: 1800, + toast: true + }).then(() => { + location.reload(); + }); + } else { + throw new Error('La suppression du dossier a échoué'); + } + }) + .catch(error => { + Swal.fire({ + position: 'top', + icon: 'error', + title: error.message, + showConfirmButton: false, + timer: 1800, + toast: true + }); + }); + } + + function confirmDelete(filename) { Swal.fire({ title: 'Êtes-vous sûr de vouloir supprimer ce fichier?', text: 'Cette action est irréversible!', @@ -213,53 +230,67 @@ confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Supprimer' - }).then(async (result) => { + }).then((result) => { if (result.isConfirmed) { - try { - const response = await fetch('/api/dpanel/dashboard/delete', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - filename: filename, - }), - }); - - if (response.ok) { - Swal.fire({ - position: 'top', - icon: 'success', - title: 'Le fichier a été supprimé avec succès.', - showConfirmButton: false, - timer: 1800, - toast: true - }) - .then(() => { - location.reload(); - }); - } else { - Swal.fire({ - position: 'top', - icon: 'error', - title: 'La suppression du fichier a échoué.', - showConfirmButton: false, - timer: 1800, - toast: true - }); - } - } catch (error) { - console.error('Erreur lors de la suppression du fichier:', error); - Swal.fire('Erreur!', 'Une erreur s\'est produite lors de la suppression du fichier.', 'error'); - } + deleteFile(filename); } }); } - function renameFile(folderName, currentName) { - const fileExtensionIndex = currentName.lastIndexOf('.'); - const fileExtension = currentName.substring(fileExtensionIndex); + function deleteFile(filename) { + fetch('/api/dpanel/dashboard/delete', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + filename: filename, + }), + }) + .then(response => { + if (response.ok) { + Swal.fire({ + position: 'top', + icon: 'success', + title: 'Le fichier a été supprimé avec succès.', + showConfirmButton: false, + timer: 1800, + toast: true + }).then(() => { + location.reload(); + }); + } else { + throw new Error('La suppression du fichier a échoué'); + } + }) + .catch(error => { + Swal.fire({ + position: 'top', + icon: 'error', + title: error.message, + showConfirmButton: false, + timer: 1800, + toast: true + }); + }); + } + function copyFileLink(fileUrl) { + navigator.clipboard.writeText(fileUrl).then(() => { + Swal.fire({ + position: 'top', + icon: 'success', + title: 'Lien copié !', + showConfirmButton: false, + timer: 1500, + toast: true + }); + }, (err) => { + console.error('Erreur lors de la copie: ', err); + }); + } + + function renameFile(folderName, currentName) { Swal.fire({ title: 'Entrez le nouveau nom', input: 'text', @@ -268,17 +299,14 @@ showCancelButton: true, confirmButtonText: 'Renommer', cancelButtonText: 'Annuler', - onOpen: (el) => { - setTimeout(() => { - const input = Swal.getInput(); - const pos = input.value.lastIndexOf('.'); - input.setSelectionRange(0, pos); - }, 0); + inputValidator: (value) => { + if (!value) { + return 'Vous devez entrer un nom de fichier'; + } } }).then((result) => { if (result.isConfirmed) { const newName = result.value; - fetch(`/api/dpanel/dashboard/rename/${folderName}`, { method: 'POST', headers: { @@ -286,261 +314,191 @@ }, body: JSON.stringify({ currentName: currentName, newName: newName }), }) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.text(); - }) - .then(data => { + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + Swal.fire({ + position: 'top', + icon: 'success', + title: 'Le fichier a été renommé avec succès.', + showConfirmButton: false, + timer: 1800, + toast: true, + }).then(() => { + location.reload(); + }); + }) + .catch((error) => { + Swal.fire({ + position: 'top', + icon: 'error', + title: 'Erreur lors du renommage du fichier.', + text: error.message, + showConfirmButton: false, + timer: 1800, + toast: true, + }); + }); + } + }); + } + + function showMoveFileModal(fileName) { + document.getElementById('moveFileName').value = fileName; + $('#moveFileModal').modal('show'); + } + + function moveFile() { + const fileName = document.getElementById('moveFileName').value; + const folderName = document.getElementById('moveFolderSelect').value; + + fetch('/api/dpanel/dashboard/movefile', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ fileName: fileName, folderName: folderName }), + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + if (data.message === "File moved successfully") { Swal.fire({ position: 'top', icon: 'success', - title: 'Le fichier a été renommé avec succès.', + title: 'Le fichier a été déplacé avec succès.', showConfirmButton: false, timer: 1800, toast: true, }).then(() => { location.reload(); }); - }) - .catch((error) => { - Swal.fire({ - position: 'top', - icon: 'error', - title: 'Erreur lors du renommage du fichier.', - showConfirmButton: false, - timer: 1800, - toast: true, - }); - }); - } - }); - } - -async function getUserIdFromFolder(username) { - if (!username) { - console.error('Username is not defined'); - return null; - } - - let users; - try { - let response = await fetch(`/user.json`); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - users = await response.json(); - } catch (error) { - console.error('Error:', error); - return null; - } - - let user = users.find(user => user.name.toLowerCase() === username.toLowerCase()); - - return user ? user.id : null; -} - -window.onload = async function() { - console.log("Page loaded, fetching file info..."); - - let data; - try { - let response = await fetch('/api/dpanel/dashboard/getmetadatafile/file_info', { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }); - console.log("Response from fetch:", response); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - data = await response.json(); - console.log("Data from response:", data); - } catch (error) { - console.log("Error in fetch:", error); - Swal.fire({ - position: 'top', - icon: 'error', - title: 'Les informations sur le fichier ne sont pas disponibles pour le moment. Veuillez réessayer plus tard.', - text: `Error: ${error.message}`, - showConfirmButton: false, - timer: 1800, - toast: true, - }); - return; - } - - let table = document.getElementById("fileTable"); - for (let file of data) { - let row = table.insertRow(); - let cell = row.insertCell(); - cell.innerHTML = `${file.fileName}`; - } -} - -async function showFileInfo(fileLink) { - console.log("showFileInfo called with fileLink:", fileLink); - let data; - try { - let response = await fetch('/api/dpanel/dashboard/getmetadatafile/file_info', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - fileLink: fileLink, - }) - }); - console.log("Response from fetch:", response); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - data = await response.json(); - console.log("Data from response:", data); - } catch (error) { - console.log("Error in fetch:", error); - Swal.fire({ - position: 'top', - icon: 'error', - title: 'Les informations sur le fichier ne sont pas disponibles pour le moment. Veuillez réessayer plus tard.', - text: `Error: ${error.message}`, - showConfirmButton: false, - timer: 1800, - toast: true, - }); - return; - } - - let pathParts = fileLink.split('/'); - console.log("pathParts:", pathParts); - if (pathParts.length < 4) { - Swal.fire({ - position: 'top', - icon: 'error', - title: `The file link ${fileLink} is not valid.`, - showConfirmButton: false, - timer: 1800, - toast: true, - }); - return; - } - let fileName = pathParts.pop(); - let userId = pathParts.pop(); - - let fileInfo = data.find(file => file.fileName === fileName && file.Id === userId); - console.log('Found fileInfo:', fileInfo); - if (!fileInfo) { - Swal.fire({ - position: 'top', - icon: 'error', - title: `No information found for the file ${fileName} for the user with ID ${userId}.`, - showConfirmButton: false, - timer: 1800, - toast: true, - }); - return; - } - - console.log('Found fileInfo:', fileInfo); - - let html = `

File name: ${fileInfo.fileName}

`; - if (fileInfo.expiryDate) { - html += `

Expiry date: ${fileInfo.expiryDate}

`; - } - if (fileInfo.password) { - html += `

Password: Yes

`; - } - if (fileInfo.userId) { - html += `

User: ${fileInfo.userId}

`; - } - Swal.fire({ - title: 'File Information', - html: html, - confirmButtonText: 'Close' - }); -} - -async function displayMetadata() { - const response = await fetch('/build-metadata'); - if (!response.ok) { - console.error('Failed to fetch metadata'); - return; - } - - const metadata = await response.json(); - if (!metadata) { - console.error('No metadata provided'); - return; - } - - document.getElementById('buildVersion').textContent = metadata.build_version; - document.getElementById('nodeVersion').textContent = metadata.node_version; - document.getElementById('expressVersion').textContent = metadata.express_version; - document.getElementById('buildSha').textContent = metadata.build_sha; - document.getElementById('osType').textContent = metadata.os_type; - document.getElementById('osRelease').textContent = metadata.os_release; - - const modal = document.getElementById('metadataModal'); - modal.style.display = 'block'; -} - -function closeModal() { - const modal = document.getElementById('metadataModal'); - modal.style.display = 'none'; -} - - function moveFile(folderName, fileName) { - Swal.fire({ - title: 'Confirmer le déplacement du fichier', - text: `Voulez-vous déplacer le fichier ${fileName} vers ${folderName} ?`, - icon: 'warning', - showCancelButton: true, - confirmButtonText: 'Déplacer', - cancelButtonText: 'Annuler', - }).then((result) => { - if (result.isConfirmed) { - fetch('/api/dpanel/dashboard/movefile', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ fileName: fileName, folderName: folderName }), - }) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - if (data.message === "File moved successfully") { - Swal.fire({ - position: 'top', - icon: 'success', - title: 'Le fichier a été déplacé avec succès.', - showConfirmButton: false, - timer: 1800, - toast: true, - }).then(() => { - location.reload(); - }); - } else { - throw new Error(data.error || 'Une erreur est survenue'); - } - }) - .catch((error) => { - Swal.fire({ - position: 'top', - icon: 'error', - title: 'Erreur lors du déplacement du fichier.', - showConfirmButton: false, - timer: 1800, - toast: true, - }); - }); + } else { + throw new Error(data.error || 'Une erreur est survenue'); } + }) + .catch((error) => { + Swal.fire({ + position: 'top', + icon: 'error', + title: 'Erreur lors du déplacement du fichier.', + text: error.message, + showConfirmButton: false, + timer: 1800, + toast: true, + }); + }); + + $('#moveFileModal').modal('hide'); + } + + const body = document.body; + const themeSwitcher = document.getElementById('themeSwitcher'); + + function setTheme(theme) { + if (theme === 'dark') { + body.classList.add('dark'); + } else { + body.classList.remove('dark'); + } + localStorage.setItem('theme', theme); + } + + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) { + setTheme(savedTheme); + } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + setTheme('dark'); + } + + themeSwitcher.addEventListener('click', function() { + if (body.classList.contains('dark')) { + setTheme('light'); + } else { + setTheme('dark'); + } + }); + + async function showFileInfo(fileName) { + try { + const response = await fetch('/api/dpanel/dashboard/getmetadatafile/file_info', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + fileLink: fileName, + }) + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + const fileInfo = data.find(file => file.fileName === fileName); + + if (!fileInfo) { + throw new Error(`No information found for the file ${fileName}.`); + } + + let html = `

Nom du fichier : ${fileInfo.fileName}

`; + if (fileInfo.expiryDate) { + html += `

Date d'expiration : ${fileInfo.expiryDate}

`; + } + if (fileInfo.password) { + html += `

Mot de passe : Oui

`; + } + if (fileInfo.userId) { + html += `

Utilisateur : ${fileInfo.userId}

`; + } + + Swal.fire({ + title: 'Informations sur le fichier', + html: html, + confirmButtonText: 'Fermer' + }); + } catch (error) { + console.error('Error in showFileInfo:', error); + Swal.fire({ + position: 'top', + icon: 'error', + title: 'Les informations sur le fichier ne sont pas disponibles pour le moment.', + text: `Erreur : ${error.message}`, + showConfirmButton: false, + timer: 1800, + toast: true, }); } + } + function displayMetadata() { + fetch('/build-metadata') + .then(response => response.json()) + .then(metadata => { + document.getElementById('buildVersion').textContent = metadata.build_version; + document.getElementById('nodeVersion').textContent = metadata.node_version; + document.getElementById('expressVersion').textContent = metadata.express_version; + document.getElementById('buildSha').textContent = metadata.build_sha; + document.getElementById('osType').textContent = metadata.os_type; + document.getElementById('osRelease').textContent = metadata.os_release; + + $('#metadataModal').modal('show'); + }) + .catch(error => { + console.error('Failed to fetch metadata:', error); + Swal.fire({ + icon: 'error', + title: 'Erreur', + text: 'Impossible de récupérer les métadonnées' + }); + }); + } \ No newline at end of file diff --git a/public/js/folder.js b/public/js/folder.js index 728680d..9eb1333 100644 --- a/public/js/folder.js +++ b/public/js/folder.js @@ -341,7 +341,7 @@ async function showFileInfo(fileName) { const moveFileForm = document.getElementById('moveFileForm'); moveFileForm.addEventListener('submit', function (event) { - event.preventDefault(); // Empêche la soumission par défaut du formulaire + event.preventDefault(); const fileName = this.querySelector('input[name="fileName"]').value; const userName = this.querySelector('input[name="userName"]').value; diff --git a/routes/Dpanel/API/Update-Setup-Admin.js b/routes/Dpanel/API/Update-Setup-Admin.js index c1e3b9c..0120543 100644 --- a/routes/Dpanel/API/Update-Setup-Admin.js +++ b/routes/Dpanel/API/Update-Setup-Admin.js @@ -4,33 +4,16 @@ 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'); -const util = require('util'); -const ncpAsync = util.promisify(ncp.ncp); -const configFile = fs.readFileSync(path.join(__dirname, '../../../data', 'setup.json'), 'utf-8') -const config = JSON.parse(configFile); const bodyParser = require('body-parser'); const crypto = require('crypto'); const os = require('os'); const osUtils = require('os-utils'); const Convert = require('ansi-to-html'); -const convert = new Convert() +const convert = new Convert(); const { getUserData, getSetupData } = require('../../../Middlewares/watcherMiddleware'); -const { logger, logRequestInfo, ErrorLogger, authLogger } = require('../../../config/logs'); -let setupData = getSetupData(); -let userData = getUserData(); router.use(bodyParser.json()); -const User = require('../../../data/user.json'); -const setup = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../data', 'setup.json'), 'utf-8')); - -router.get('/', (req, res) => { - res.status(400).json({ error: 'Bad Request. The request cannot be fulfilled due to bad syntax or missing parameters.' }); -}); - - function clean(obj) { for (var propName in obj) { if (obj[propName] === null || obj[propName] === undefined || obj[propName] === '') { @@ -44,20 +27,74 @@ function clean(obj) { } } +function validateIP(ip) { + if (!ip) return false; + + if (ip.includes('/')) { + const [addr, bits] = ip.split('/'); + const bitsNum = parseInt(bits); + + if (isIPv4(addr)) { + return bitsNum >= 0 && bitsNum <= 32; + } else if (isIPv6(addr)) { + return bitsNum >= 0 && bitsNum <= 128; + } + return false; + } + + return isIPv4(ip) || isIPv6(ip); +} + +function isIPv4(ip) { + const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/; + if (!ipv4Regex.test(ip)) return false; + + const parts = ip.split('.'); + return parts.every(part => { + const num = parseInt(part); + return num >= 0 && num <= 255; + }); +} + +function isIPv6(ip) { + const ipv6Regex = /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; + return ipv6Regex.test(ip); +} + +router.get('/', (req, res) => { + res.status(400).json({ error: 'Bad Request. The request cannot be fulfilled due to bad syntax or missing parameters.' }); +}); + router.post('/', authMiddleware, async (req, res) => { try { - let setup = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../data', 'setup.json'), 'utf-8')); + + if (req.body.allowedIps) { + const ipsArray = Array.isArray(req.body.allowedIps) ? req.body.allowedIps : [req.body.allowedIps]; + + req.body.allowedIps = ipsArray + .filter(ip => ip && ip.trim()) + .filter(ip => validateIP(ip.trim())) + .map(ip => ip.trim()); + + console.log('IPs validées:', req.body.allowedIps); + } clean(req.body); - setup[0] = req.body; + setup[0] = { + ...setup[0], + ...req.body + }; - - fs.writeFileSync(path.join(__dirname, '../../../data', 'setup.json'), JSON.stringify(setup, null, 2), 'utf-8'); + fs.writeFileSync( + path.join(__dirname, '../../../data', 'setup.json'), + JSON.stringify(setup, null, 2), + 'utf-8' + ); res.redirect('/dpanel/dashboard/admin/settingsetup'); } catch (err) { - console.error(err); + console.error('Erreur lors de la mise à jour de la configuration:', err); res.status(500).send('Server Error'); } }); diff --git a/routes/Dpanel/API/Upload-Role-Admin.js b/routes/Dpanel/API/Upload-Role-Admin.js index 7111b4e..af0db5e 100644 --- a/routes/Dpanel/API/Upload-Role-Admin.js +++ b/routes/Dpanel/API/Upload-Role-Admin.js @@ -40,9 +40,9 @@ router.post('/', authMiddleware, async (req, res) => { user.role = role; } - fs.writeFileSync(path.join(__dirname, '../../../data/user.js'), JSON.stringify(User, null, 2)); + fs.writeFileSync(path.join(__dirname, '../../../data/user.json'), JSON.stringify(User, null, 2)); - res.redirect('/dpanel/dashboard/admin'); + res.status(200).json({ message: 'Operation successful' }); } catch (err) { console.error(err); res.status(500).send('Server Error'); diff --git a/routes/Dpanel/Dashboard/index.js b/routes/Dpanel/Dashboard/index.js index fb432db..46fdbf6 100644 --- a/routes/Dpanel/Dashboard/index.js +++ b/routes/Dpanel/Dashboard/index.js @@ -81,6 +81,17 @@ router.get('/', authMiddleware, async (req, res) => { }; }); + function formatFileSize(fileSizeInBytes) { + if (fileSizeInBytes < 1024 * 1024) { + return `${(fileSizeInBytes / 1024).toFixed(2)} Ko`; + } else if (fileSizeInBytes < 1024 * 1024 * 1024) { + return `${(fileSizeInBytes / (1024 * 1024)).toFixed(2)} Mo`; + } else { + return `${(fileSizeInBytes / (1024 * 1024 * 1024)).toFixed(2)} Go`; + } + } + + 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 }); diff --git a/routes/denied.js b/routes/denied.js new file mode 100644 index 0000000..fbac985 --- /dev/null +++ b/routes/denied.js @@ -0,0 +1,14 @@ +const express = require('express'); +const router = express.Router(); +const path = require('path'); +const { logger, ErrorLogger, logRequestInfo } = require('../config/logs'); +const util = require('util'); +const fs = require('fs'); + +router.use(express.json()); + +router.get('/', (req, res) => { + res.render('acces-denied'); +}); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js index aa73cbc..1dacc32 100644 --- a/routes/index.js +++ b/routes/index.js @@ -8,7 +8,7 @@ const fs = require('fs'); router.use(express.json()); router.get('/', (req, res) => { - res.render('acces-denied'); + res.render('promote'); }); router.get('/attachments', (req, res) => { diff --git a/routes/routes.js b/routes/routes.js index a1580b6..b325e1f 100644 --- a/routes/routes.js +++ b/routes/routes.js @@ -5,6 +5,7 @@ const { logApiRequest } = require('../config/logs.js'); const discordWebhookSuspisiousAlertMiddleware = require('../Middlewares/discordWebhookSuspisiousAlertMiddleware.js'); const indexRoute = require('./index.js'); +const deniedRoute = require('./denied.js'); const DpanelDashboardRoute = require('./Dpanel/Dashboard/index.js'); const DpanelFolderRoute = require('./Dpanel/Folder/index.js'); const DpanelUploadRoute = require('./Dpanel/Upload.js'); @@ -78,6 +79,6 @@ router.use('/auth/activedirectory',discordWebhookSuspisiousAlertMiddleware, acti router.use('/auth/discord',discordWebhookSuspisiousAlertMiddleware, discordRoute); -router.use('/*', logAndBanSuspiciousActivity, indexRoute); +router.use('/*', logAndBanSuspiciousActivity, deniedRoute); module.exports = router; \ No newline at end of file diff --git a/views/AuthLogin.ejs b/views/AuthLogin.ejs index 20ef749..36df369 100644 --- a/views/AuthLogin.ejs +++ b/views/AuthLogin.ejs @@ -1,287 +1,253 @@ - - - - Connexion + + + + + +
+
+

Connexion

- - - - -
-

Connexion

- <% if (currentUrl === '/auth/activedirectory' || (setupData[0] && setupData[0].hasOwnProperty('ldap'))) { %> -

Connexion avec Active Directory

-
- <% if (typeof errorMessage !== 'undefined' && errorMessage) { %> - <% } %> - -
- - + <% if (typeof setupData !== 'undefined' && Array.isArray(setupData)) { %> + <% setupData.forEach(config => { %> + <% if (config.hasOwnProperty('discord') && config.discord.enabled === 'on' && config.discord.identifyURL) { %> + + <% } %> + <% }); %> + <% } %> +
- -
- - -
- - - - <% } %> - <% if (Array.isArray(setupData)) { %> - <% setupData.forEach(config => { %> - <% if (config.hasOwnProperty('discord') && config.discord.enabled === 'on') { %> - <% if (config.hasOwnProperty('ldap') && config.ldap.enabled === 'on') { %> -

Ou

- <% } %> - - <% } %> - <% }); %> - <% } %> -
-
- +
-
- - - - + - - \ No newline at end of file + diff --git a/views/acces-denied.ejs b/views/acces-denied.ejs index 70bd24b..9744239 100644 --- a/views/acces-denied.ejs +++ b/views/acces-denied.ejs @@ -1,9 +1,110 @@ - + 403 ERROR + + + + + + + +
+
+
+
+ +
+ +

403 ERROR

+ +
+

La requête n'a pas pu aboutir.

+

Requête bloquée. Nous ne pouvons pas nous connecter au serveur de cette application ou de ce site web pour le moment. Il y a peut-être trop de trafic ou une erreur de configuration.

+ +
+

ID de la requête:

+

Horodatage:

+
+ + +
+
+
+
+ - - -

403 ERROR

-

The request could not be satisfied.

-

Request blocked. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.

-

Generated by Myaxrin Nexus (Security Department)

-

Request ID:

-

Timestamp:

- + \ No newline at end of file diff --git a/views/dashboard.ejs b/views/dashboard.ejs index bc7f197..0631ae4 100644 --- a/views/dashboard.ejs +++ b/views/dashboard.ejs @@ -1,108 +1,417 @@ - - + - - - - - - - - - - Dashboard + Dashboard CDN - - + + + + + /* Alignement des boutons à droite */ + .navbar { + padding: 0.5rem 1rem !important; + } - + .container-fluid { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + } + + /* Alignement des boutons à droite */ + .navbar-nav { + display: flex !important; + align-items: center !important; + margin-left: auto !important; + } + + .navbar-nav .nav-item { + margin-left: 10px !important; + } + + /* Style pour les boutons de la navbar */ + .nav-btn { + padding: 0.375rem 0.75rem !important; + font-size: 1rem !important; + line-height: 1.5 !important; + border-radius: 0.25rem !important; + } + + /* Style pour l'avatar de l'utilisateur */ + .user-avatar { + width: 40px !important; + height: 40px !important; + object-fit: cover !important; + border-radius: 50% !important; + } + + /* Style pour le bouton du dropdown */ + #accountDropdownBtn { + padding: 0 !important; + border: none !important; + background: none !important; + } + + /* Style pour le menu dropdown */ + .dropdown-menu { + right: 0 !important; + left: auto !important; + } + + /* Assurez-vous que le dropdown est positionné correctement */ + .dropdown { + position: relative !important; + } + + /* Ajustement pour les écrans plus petits */ + @media (max-width: 768px) { + .navbar-nav { + flex-direction: row !important; + justify-content: flex-end !important; + width: 100% !important; + } + + .navbar-nav .nav-item { + margin-left: 5px !important; + } + + } + + +
-