Note: We appreciate your feedback and bug reports to continue improving our platform. Thank you for your continued support!
This commit is contained in:
@@ -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
|
||||
|
||||
5
.idea/.gitignore
generated
vendored
5
.idea/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
12
.idea/CDN-APP-INSIDER.iml
generated
12
.idea/CDN-APP-INSIDER.iml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
7
.idea/discord.xml
generated
7
.idea/discord.xml
generated
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/jsLibraryMappings.xml
generated
6
.idea/jsLibraryMappings.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/CDN-APP-INSIDER.iml" filepath="$PROJECT_DIR$/.idea/CDN-APP-INSIDER.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,3 +194,5 @@ body.white-theme .animated-button:focus {
|
||||
.rainbow-effect {
|
||||
animation: rainbowBorder 2s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 = '</svg><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-brightness-high-fill" viewBox="0 0 16 16"><path d="M12 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/></svg>';
|
||||
} else {
|
||||
icon.classList.remove('bi-moon-fill');
|
||||
icon.classList.add('bi-brightness-high-fill');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-fill" viewBox="0 0 16 16"><path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278"/>';
|
||||
}
|
||||
}
|
||||
|
||||
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 = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-brightness-high-fill" viewBox="0 0 16 16"><path d="M12 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/></svg>';
|
||||
} else {
|
||||
icon.classList.remove('bi-moon-fill');
|
||||
icon.classList.add('bi-brightness-high-fill');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-fill" viewBox="0 0 16 16"><path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278"/></svg>';
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
document.getElementById('searchButton').addEventListener('click', searchFiles);
|
||||
document.getElementById('newFolderBtn').addEventListener('click', showNewFolderModal);
|
||||
|
||||
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.querySelectorAll('.delete-folder-button').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const folderName = this.getAttribute('data-folder-name');
|
||||
confirmDeleteFolder(folderName);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.delete-file-button').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const fileName = this.getAttribute('data-file-name');
|
||||
confirmDelete(fileName);
|
||||
});
|
||||
});
|
||||
|
||||
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('.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');
|
||||
function searchFiles() {
|
||||
const input = document.getElementById('searchInput');
|
||||
const filter = input.value.toUpperCase();
|
||||
const table = document.getElementById('fileTable');
|
||||
const tr = table.getElementsByTagName('tr');
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', function (e) {
|
||||
if (!$('#accountDropdownBtnGroup').is(e.target) && $('#accountDropdownBtnGroup').has(e.target).length === 0) {
|
||||
$('#accountDropdownMenu').removeClass('show');
|
||||
}
|
||||
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) {
|
||||
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 = `<a href="#" onclick="showFileInfo('${file.fileName}')">${file.fileName}</a>`;
|
||||
}
|
||||
}
|
||||
|
||||
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 = `<p>File name: ${fileInfo.fileName}</p>`;
|
||||
if (fileInfo.expiryDate) {
|
||||
html += `<p>Expiry date: ${fileInfo.expiryDate}</p>`;
|
||||
}
|
||||
if (fileInfo.password) {
|
||||
html += `<p>Password: Yes</p>`;
|
||||
}
|
||||
if (fileInfo.userId) {
|
||||
html += `<p>User: <a href="/api/dpanel/dashboard/getuser/${fileInfo.userId}" target="_blank">${fileInfo.userId}</a></p>`;
|
||||
}
|
||||
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 = `<p>Nom du fichier : ${fileInfo.fileName}</p>`;
|
||||
if (fileInfo.expiryDate) {
|
||||
html += `<p>Date d'expiration : ${fileInfo.expiryDate}</p>`;
|
||||
}
|
||||
if (fileInfo.password) {
|
||||
html += `<p>Mot de passe : Oui</p>`;
|
||||
}
|
||||
if (fileInfo.userId) {
|
||||
html += `<p>Utilisateur : <a href="/api/dpanel/dashboard/getuser/${fileInfo.userId}" target="_blank">${fileInfo.userId}</a></p>`;
|
||||
}
|
||||
|
||||
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'
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 });
|
||||
|
||||
14
routes/denied.js
Normal file
14
routes/denied.js
Normal file
@@ -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;
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
@@ -1,287 +1,253 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Connexion</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.icon-spacing {
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Connexion</h1>
|
||||
|
||||
<style>
|
||||
.custom-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
font-size: 17px;
|
||||
padding: 0.8em 1.5em;
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 20em;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 150px;
|
||||
min-height: 50px;
|
||||
}
|
||||
<div class="form-container">
|
||||
<form id="loginForm" class="mb-4 animate">
|
||||
<div class="form-group animate">
|
||||
<label for="username" class="block mb-2">Nom d'utilisateur</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required>
|
||||
</div>
|
||||
|
||||
.custom-btn:hover {
|
||||
box-shadow: 0 0.5em 1.5em -0.5em #4d36d0be;
|
||||
}
|
||||
<div class="form-group animate">
|
||||
<label for="password" class="block mb-2">Mot de passe</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
.custom-btn span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
<button type="submit" id="loginButton" class="btn btn-primary w-full py-2 mt-4">
|
||||
<i class="fa-solid fa-user icon-spacing"></i>
|
||||
Se connecter
|
||||
</button>
|
||||
|
||||
.custom-btn.processing {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.custom-btn.success {
|
||||
background: linear-gradient(0deg, rgba(40,167,69,1) 0%, rgba(76,203,112,1) 100%);
|
||||
box-shadow: 0 0.7em 1.5em -0.5em rgba(40,167,69,0.6);
|
||||
}
|
||||
|
||||
.custom-btn.failure {
|
||||
background: linear-gradient(0deg, rgba(220,53,69,1) 0%, rgba(255,107,114,1) 100%);
|
||||
box-shadow: 0 0.7em 1.5em -0.5em rgba(220,53,69,0.6);
|
||||
}
|
||||
|
||||
.custom-btn svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-btn.success svg, .custom-btn.failure svg {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
|
||||
.checkmark__circle, .crossmark__circle {
|
||||
stroke-dasharray: 166;
|
||||
stroke-dashoffset: 166;
|
||||
stroke-width: 2;
|
||||
stroke-miterlimit: 10;
|
||||
stroke: #fff;
|
||||
fill: none;
|
||||
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
|
||||
}
|
||||
|
||||
.checkmark__check, .crossmark__check {
|
||||
transform-origin: 50% 50%;
|
||||
stroke-dasharray: 48;
|
||||
stroke-dashoffset: 48;
|
||||
stroke: #fff;
|
||||
stroke-width: 3;
|
||||
fill: none;
|
||||
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
|
||||
}
|
||||
|
||||
@keyframes stroke {
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutContent {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shakeButton {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
10%, 30%, 50%, 70%, 90% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
20%, 40%, 60%, 80% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-btn.processing span, .custom-btn.success span, .custom-btn.failure span {
|
||||
animation: fadeOutContent 0.3s forwards;
|
||||
}
|
||||
|
||||
.custom-btn.failure {
|
||||
animation: shakeButton 0.5s cubic-bezier(.36,.07,.19,.97) both;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="alert alert-primary text-center" role="alert">
|
||||
Un nouveau look sera bientôt disponible pour la page de connexion. Myaxrin Labs va améliorer son application pour la rendre plus rapide et plus facile à utiliser.
|
||||
</div>
|
||||
<div class="container mt-4 animate">
|
||||
<h1 class="title text-center animate">Connexion</h1>
|
||||
<% if (currentUrl === '/auth/activedirectory' || (setupData[0] && setupData[0].hasOwnProperty('ldap'))) { %>
|
||||
<h3 class="text-center animate">Connexion avec Active Directory</h3>
|
||||
<form id="loginForm" class="mb-4 animate">
|
||||
<% if (typeof errorMessage !== 'undefined' && errorMessage) { %>
|
||||
<% } %>
|
||||
|
||||
<div class="form-group animate">
|
||||
<label for="username" class="animate">Nom d'utilisateur :</label>
|
||||
<input type="text" id="username" name="username" class="form-control animate" required>
|
||||
<% if (typeof setupData !== 'undefined' && Array.isArray(setupData)) { %>
|
||||
<% setupData.forEach(config => { %>
|
||||
<% if (config.hasOwnProperty('discord') && config.discord.enabled === 'on' && config.discord.identifyURL) { %>
|
||||
<button onclick="redirectToDiscord('<%= config.discord.identifyURL %>')" id="discordButton" class="btn btn-secondary w-full py-2 mt-4">
|
||||
<i class="fab fa-discord mr-2"></i>
|
||||
Se connecter avec Discord
|
||||
</button>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="form-group animate">
|
||||
<label for="password" class="animate">Mot de passe :</label>
|
||||
<input type="password" id="password" name="password" class="form-control animate" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="loginButton" class="btn btn-primary d-flex align-items-center justify-content-center mx-auto d-block custom-btn">
|
||||
<span class="ml-2 animate">Se connecter</span>
|
||||
</button>
|
||||
</form>
|
||||
<% } %>
|
||||
<% if (Array.isArray(setupData)) { %>
|
||||
<% setupData.forEach(config => { %>
|
||||
<% if (config.hasOwnProperty('discord') && config.discord.enabled === 'on') { %>
|
||||
<% if (config.hasOwnProperty('ldap') && config.ldap.enabled === 'on') { %>
|
||||
<h3 class="text-center animate">Ou</h3>
|
||||
<% } %>
|
||||
<button onclick="redirectToDiscord('<%= config.discord.identifyURL %>')" class="btn btn-primary d-flex align-items-center justify-content-center mx-auto d-block custom-btn" id="discordButton">
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" width="24" height="24">
|
||||
<title>Discord</title>
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152..."></path>
|
||||
</svg> Se connecter avec Discord
|
||||
</button>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
<br>
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate">Changer de Thème</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
body.classList.toggle('dark-mode');
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
document.getElementById('loginForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const loginButton = document.getElementById('loginButton');
|
||||
|
||||
loginForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
loginButton.disabled = true;
|
||||
loginButton.innerHTML = 'Connexion en cours...';
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
loginButton.disabled = true;
|
||||
loginButton.innerHTML = '<span>Connexion en cours...</span>';
|
||||
loginButton.classList.add('processing');
|
||||
|
||||
fetch('/auth/activedirectory', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
redirect: 'follow',
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Statut de la réponse:', response.status);
|
||||
console.log('URL finale:', response.url);
|
||||
if (response.url.includes('/dpanel/dashboard')) {
|
||||
handleAuthResult(loginButton, true);
|
||||
} else {
|
||||
return response.text().then(text => {
|
||||
console.log('Texte reçu:', text);
|
||||
handleAuthResult(loginButton, false);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur:', error);
|
||||
handleAuthResult(loginButton, false);
|
||||
});
|
||||
});
|
||||
|
||||
function handleAuthResult(loginButton, isSuccess) {
|
||||
loginButton.classList.remove('processing');
|
||||
loginButton.classList.add(isSuccess ? 'success' : 'failure');
|
||||
|
||||
const iconSVG = isSuccess ? `
|
||||
<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" width="20" height="20">
|
||||
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none"/>
|
||||
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
|
||||
</svg>
|
||||
` : `
|
||||
<svg class="crossmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" width="20" height="20">
|
||||
<circle class="crossmark__circle" cx="26" cy="26" r="25" fill="none"/>
|
||||
<path class="crossmark__check" fill="none" d="M16 16 36 36 M36 16 16 36"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
loginButton.innerHTML = iconSVG;
|
||||
|
||||
if (isSuccess) {
|
||||
setTimeout(() => {
|
||||
console.log("Redirection vers /dpanel/dashboard");
|
||||
window.location.replace('/dpanel/dashboard');
|
||||
}, 1000);
|
||||
fetch('/auth/activedirectory', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
redirect: 'follow',
|
||||
})
|
||||
.then(response => {
|
||||
if (response.url.includes('/dpanel/dashboard')) {
|
||||
loginButton.innerHTML = 'Succès!';
|
||||
setTimeout(() => window.location.replace('/dpanel/dashboard'), 1000);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
loginButton.classList.remove('failure');
|
||||
loginButton.innerHTML = '<span>Se connecter</span>';
|
||||
loginButton.disabled = false;
|
||||
}, 1500);
|
||||
throw new Error('Échec de la connexion');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
loginButton.innerHTML = 'Échec';
|
||||
setTimeout(() => {
|
||||
loginButton.disabled = false;
|
||||
loginButton.innerHTML = 'Se connecter';
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
|
||||
function redirectToDiscord(url) {
|
||||
const discordButton = document.getElementById('discordButton');
|
||||
|
||||
discordButton.classList.add('btn-fill');
|
||||
discordButton.innerHTML = '<span>Redirection en cours...</span>';
|
||||
discordButton.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = url;
|
||||
}, 1000);
|
||||
if (discordButton) {
|
||||
discordButton.innerHTML = 'Redirection en cours...';
|
||||
discordButton.disabled = true;
|
||||
setTimeout(() => window.location.href = url, 1000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>403 ERROR</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 1000;
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 50%;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 3rem;
|
||||
color: #ef4444;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate dark">
|
||||
<button id="themeSwitcher">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" width="24" height="24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
<div class="max-w-lg w-full">
|
||||
<div class="form-container text-center">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl font-semibold mb-6">403 ERROR</h1>
|
||||
|
||||
<div class="space-y-4">
|
||||
<p class="text-lg">La requête n'a pas pu aboutir.</p>
|
||||
<p class="text-sm opacity-80">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.</p>
|
||||
|
||||
<div class="mt-8 pt-4 border-t border-gray-700 space-y-2">
|
||||
<p class="text-sm opacity-60">ID de la requête: <span id="request-id"></span></p>
|
||||
<p class="text-sm opacity-60">Horodatage: <span id="timestamp"></span></p>
|
||||
</div>
|
||||
|
||||
<button onclick="window.history.back()" class="w-full mt-6 px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md transition-colors flex items-center justify-center space-x-2">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
<span>Retour</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function generateRequestId() {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
@@ -18,21 +119,37 @@
|
||||
return new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, 'Z');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const requestId = generateRequestId();
|
||||
const timestamp = generateTimestamp();
|
||||
|
||||
document.getElementById('request-id').innerText = requestId;
|
||||
document.getElementById('timestamp').innerText = timestamp;
|
||||
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme) {
|
||||
setTheme(savedTheme);
|
||||
}
|
||||
});
|
||||
|
||||
themeSwitcher.addEventListener('click', function() {
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>403 ERROR</h1>
|
||||
<p>The request could not be satisfied.</p>
|
||||
<p>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.</p>
|
||||
<p>Generated by Myaxrin Nexus (Security Department)</p>
|
||||
<p>Request ID: <span id="request-id"></span></p>
|
||||
<p>Timestamp: <span id="timestamp"></span></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,108 +1,417 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
|
||||
integrity="sha384-GLhlTQ8iRABdZLl6O5oVMWSktQOp6b7In1Zl3/JiR3eZB1+nHN/8u8UqXj2l1tji" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
||||
<script src="/public/js/dashboard.js"></script>
|
||||
<link rel="stylesheet" href="/public/css/styles.css" />
|
||||
<title>Dashboard</title>
|
||||
<title>Dashboard CDN</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/public/assets/homelab_logo@2x.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/public/assets/homelab_logo@3x.png">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
</head>
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: hsl(var(--destructive));
|
||||
color: hsl(var(--destructive-foreground));
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 0.75rem;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: hsl(var(--background));
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
display: none;
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
color: hsl(var(--foreground));
|
||||
padding: 0.5rem 1rem;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: hsl(var(--muted));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-shrink: 0;
|
||||
background-color: hsl(var(--background));
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: hsl(var(--card));
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
padding: 20px;
|
||||
max-width: 90%;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.modal-header, .modal-body, .modal-footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: none;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.modal .close {
|
||||
font-size: 28px;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.modal .close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
/* Alignement des boutons à droite */
|
||||
.navbar {
|
||||
padding: 0.5rem 1rem !important;
|
||||
}
|
||||
|
||||
<body class="light-mode">
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-light header">
|
||||
<a class="navbar-brand">
|
||||
Dashboard CDN
|
||||
<span class="badge badge-info ml-1">Beta</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<form action="/dpanel/upload" class="form-inline">
|
||||
<button class="btn btn-primary btn-round mr-2 animated-button">
|
||||
<i class="fas fa-cloud-upload-alt"></i> Téléverser un fichier
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/dpanel/dashboard">
|
||||
Dashboard CDN
|
||||
<span class="bg-purple-100 text-purple-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-purple-900 dark:text-purple-300">Beta</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<a href="/dpanel/upload" class="btn btn-primary">
|
||||
<i class="fas fa-cloud-upload-alt"></i> Téléverser
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button type="button" class="btn btn-success" id="newFolderBtn">
|
||||
<i class="fas fa-folder-open"></i> Nouveau
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<form id="newFolderModalForm" class="form-inline ml-2">
|
||||
<button type="submit" class="btn btn-success btn-round ml-2 animated-button" id="newFolderModalBtn" data-toggle="modal" data-target="#newFolderModal">
|
||||
<i class="fas fa-folder-open"></i> Nouveau Dossier
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button id="styleSwitcher" class="btn btn-link btn-round animated-button">
|
||||
<i id="themeIcon"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="btn dropdown-toggle" id="accountDropdownBtn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img
|
||||
src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + user.name %>"
|
||||
alt="User Icon"
|
||||
class="rounded-circle"
|
||||
style="width: 30px; height: 30px;"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right p-4 bg-dark text-white" id="accountDropdownMenu">
|
||||
<div class="mb-3 text-center">
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/profil" id="logoutLink">
|
||||
<i class="fas fa-sign-out-alt text-white"></i> Mon profile
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="btn dropdown-toggle nav-btn" id="accountDropdownBtn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img
|
||||
src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + encodeURIComponent(user.name) %>"
|
||||
alt="User Icon"
|
||||
class="rounded-full"
|
||||
style="max-width: 50px; max-height: 50px;"
|
||||
/>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" id="accountDropdownMenu">
|
||||
<a class="dropdown-item" href="/dpanel/dashboard/profil">
|
||||
<i class="fas fa-user"></i> Mon profil
|
||||
</a>
|
||||
<a class="dropdown-item" href="/dpanel/dashboard/admin">
|
||||
<i class="fas fa-user-shield"></i> Administration du site
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="/auth/logout">
|
||||
<i class="fas fa-sign-out-alt"></i> Déconnexion
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown-divider mb-2"></div>
|
||||
<% if (user.role === 'admin') { %>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin" id="adminLink">
|
||||
<i class="fas fa-user-shield"></i> Administration du site
|
||||
</a><br>
|
||||
<% } %>
|
||||
<br><a class="dropdown-item text-center text-white no-hover custom-btn" href="/auth/logout" id="logoutLink">
|
||||
<i class="fas fa-sign-out-alt text-white"></i> Déconnexion
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="alert alert-primary text-center" role="alert">
|
||||
Un nouveau look sera bientôt disponible pour le tableau de bord. Myaxrin Labs va améliorer son application pour la rendre plus rapide et plus facile à utiliser.
|
||||
|
||||
|
||||
<div class="container mt-4 animate">
|
||||
<div class="form-container">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<input type="text" id="searchInput" class="form-control w-1/2" placeholder="Rechercher par nom de fichier">
|
||||
<button id="searchButton" class="btn btn-primary">Rechercher</button>
|
||||
</div>
|
||||
|
||||
<div class="container mt-4 table-container">
|
||||
<div class="table-responsive">
|
||||
<table class="table w-100 table table-hover">
|
||||
<table class="table" id="fileTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom du fichier</th>
|
||||
@@ -122,161 +431,104 @@
|
||||
<% 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 %>
|
||||
<span class="file-size" data-size="<%= file.size %>"><%= file.size %> octets</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td class="d-flex justify-content-end align-items-center">
|
||||
<% if (file.type === 'folder') { %>
|
||||
<form class="file-actions mb-2">
|
||||
<input type="hidden" name="folderName" value="<%= file.name %>">
|
||||
<button class="delete-folder-button btn btn-danger btn-round animated-button" type="button">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
<a href="/dpanel/dashboard/folder/<%= file.name %>" class="btn btn-primary btn-round mb-2 animated-button">
|
||||
<i class="fas fa-folder-open"></i> Accéder
|
||||
</a>
|
||||
<% } else { %>
|
||||
<button class="btn btn-primary btn-round animated-button" onclick="renameFile('<%= folderName %>', '<%= file.name %>')">
|
||||
<i class="fas fa-edit"></i> Renommer
|
||||
</button>
|
||||
<form class="file-actions mb-2" id="deleteForm" action="/api/dpanel/dashboard/delete" method="post">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="filename" value="<%= file.name %>">
|
||||
<button class="delete-button btn btn-danger btn-round animated-button" type="button" onclick="confirmDelete('<%= file.name %>')">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form class="file-actions mb-2">
|
||||
<div class="copy-link-container d-flex align-items-center">
|
||||
<input type="text" class="file-link form-control rounded mr-2" value="<%= file.url %>" readonly style="display: none;">
|
||||
<button class="button copy-button btn btn-success btn-round animated-button" data-file="<%= file.name %>">
|
||||
<td class="text-right">
|
||||
<div class="action-buttons">
|
||||
<% if (file.type === 'folder') { %>
|
||||
<button class="delete-folder-button btn btn-danger btn-sm" data-folder-name="<%= file.name %>">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
<a href="/dpanel/dashboard/folder/<%= file.name %>" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
</a>
|
||||
<% } else { %>
|
||||
<button class="btn btn-primary btn-sm rename-file-btn" data-file-name="<%= file.name %>" data-folder-name="<%= folderName %>">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="delete-file-button btn btn-danger btn-sm" data-file-name="<%= file.name %>">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
<button class="copy-button btn btn-success btn-sm" data-file-url="<%= file.url %>">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<form id="moveFileForm" class="file-actions d-flex align-items-center mb-2" onsubmit="event.preventDefault(); moveFile(this.folderName.value, this.fileName.value);">
|
||||
<input type="hidden" name="fileName" value="<%= file.name %>">
|
||||
<select class="form-control rounded mr-2 custom-dropdown" name="folderName">
|
||||
<option value="" disabled selected>Déplacer vers...</option>
|
||||
<% allFolders.forEach(folder => { %>
|
||||
<option value="<%= folder %>"><%= folder %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
<button type="submit" id="movefilebutton" class="btn btn-success btn-round animated-button">Déplacer</button>
|
||||
</form>
|
||||
<% } %>
|
||||
</td>
|
||||
<button class="btn btn-secondary btn-sm move-file-btn" data-file-name="<%= file.name %>">
|
||||
<i class="fas fa-file-export"></i>
|
||||
</button>
|
||||
<% } %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="patchNoteModal" tabindex="-1" role="dialog" aria-labelledby="patchNoteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg rounded-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<footer class="footer mt-auto py-3 bg-light">
|
||||
<div class="container">
|
||||
<span class="text-muted">Version: 1.0.0-beta.16 | © 2024 Myaxrin Labs</span>
|
||||
<a href="#" class="float-right" onclick="displayMetadata()">Metadata</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="modal fade" id="metadataModal" tabindex="-1" role="dialog" aria-labelledby="metadataModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="patchNoteModalLabel">Patch Note<span class="badge badge-info ml-1">v1.0.0-beta.15</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h5 class="modal-title" id="metadataModalLabel">Metadata</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="patch-note-item">
|
||||
<p><i class="fas fa-tools"></i> Nouvelles Fonctionnalités :</p>
|
||||
<ul>
|
||||
<li>Intégration de l'authentification Discord<span class="badge badge-success ml-1">AMÉLIORATION MINEURE</span></li>
|
||||
<li>Personnalisation de l'arrière-plan de l'application<span class="badge badge-success ml-1">AMÉLIORATION MAJEURE</span></li>
|
||||
<li>Nouveau design pour l'affichage du token dans le panneau administrateur<span class="badge badge-success ml-1">AMÉLIORATION MAJEURE</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="patch-note-item">
|
||||
<p><i class="fas fa-wrench"></i> Améliorations :</p>
|
||||
<ul>
|
||||
<li>Optimisation des performances de l'application<span class="badge badge-success ml-1">AMÉLIORATION MINEURE</span></li>
|
||||
<li>Amélioration de l'interface utilisateur pour une meilleure expérience<span class="badge badge-success ml-1">AMÉLIORATION MAJEURE</span></li>
|
||||
<li>Mise à jour des dépendances pour une meilleure stabilité<span class="badge badge-success ml-1">AMÉLIORATION MINEURE</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="patch-note-item">
|
||||
<p><i class="fas fa-bug"></i> Corrections de Bugs :</p>
|
||||
<ul>
|
||||
<li>Correction des erreurs d'affichage dans certains navigateurs<span class="badge badge-success ml-1">CORRECTION MINEURE</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="patch-note-item">
|
||||
<p><i class="fas fa-lock"></i> Sécurité :</p>
|
||||
<ul>
|
||||
<li>Renforcement de la sécurité de l'authentification<span class="badge badge-success ml-1">AMÉLIORATION MAJEURE</span></li>
|
||||
<li>Mise à jour des protocoles de chiffrement<span class="badge badge-success ml-1">AMÉLIORATION MINEURE</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p class="note">Remarque : Nous sommes toujours à l'écoute de vos retours pour améliorer notre plateforme. N'hésitez pas à nous faire part de vos commentaires et suggestions !</p>
|
||||
<p><i class="fas fa-code-branch"></i> Version de Build: <span id="buildVersion"></span></p>
|
||||
<p><i class="fab fa-node"></i> Version de Node.js: <span id="nodeVersion"></span></p>
|
||||
<p><i class="fas fa-server"></i> Version de Express.js: <span id="expressVersion"></span></p>
|
||||
<p><i class="fas fa-hashtag"></i> SHA de Build: <span id="buildSha"></span></p>
|
||||
<p><i class="fas fa-windows"></i> Type d'OS: <span id="osType"></span></p>
|
||||
<p><i class="fas fa-laptop-code"></i> Version d'OS: <span id="osRelease"></span></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Fermer</button>
|
||||
<button type="button" class="btn btn-primary" onclick="displayMetadata()">Afficher les métadonnées</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="metadataModal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Metadata</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" onclick="closeModal()" aria-label="Fermer">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><i class="fas fa-code-branch"></i> Version de Build: <span id="buildVersion"></span></p>
|
||||
<p><i class="fab fa-node"></i> Version de Node.js: <span id="nodeVersion"></span></p>
|
||||
<p><i class="fas fa-server"></i> Version de Express.js: <span id="expressVersion"></span></p>
|
||||
<p><i class="fas fa-hashtag"></i> SHA de Build: <span id="buildSha"></span></p>
|
||||
<p><i class="fas fa-windows"></i> Type d'OS: <span id="osType"></span></p>
|
||||
<p><i class="fas fa-laptop-code"></i> Version d'OS: <span id="osRelease"></span></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary btn-block" data-dismiss="modal" onclick="closeModal()">OK</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<footer class="py-3 my-4">
|
||||
<ul class="nav justify-content-center border-bottom pb-3 mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-2 text-muted" href="#" data-toggle="modal" data-target="#patchNoteModal">
|
||||
Version: 1.0.0-beta.15
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<p class="text-center text-muted mb-0">© 2024 Myaxrin Labs</p>
|
||||
<div class="ml-3">
|
||||
</div>
|
||||
<div class="modal fade" id="moveFileModal" tabindex="-1" role="dialog" aria-labelledby="moveFileModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Déplacer le fichier</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Fermer">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="modal-body">
|
||||
<form id="moveFileForm">
|
||||
<input type="hidden" id="moveFileName" name="fileName">
|
||||
<select class="form-control" id="moveFolderSelect" name="folderName">
|
||||
<option value="" disabled selected>Choisir un dossier...</option>
|
||||
<% allFolders.forEach(folder => { %>
|
||||
<option value="<%= folder %>"><%= folder %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmMoveFile">Déplacer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<script src="/public/js/dashboard.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -64,7 +64,7 @@
|
||||
</div>
|
||||
</nav>
|
||||
<div class="alert alert-primary text-center" role="alert">
|
||||
Un nouveau look sera bientôt disponible pour le tableau de bord. Myaxrin Labs va améliorer son application pour la rendre plus rapide et plus facile à utiliser.
|
||||
La refonte est presque terminée ! Il ne reste qu'une seule page à finaliser. Myaxrin Labs s'excuse pour le délai et travaille activement à l'amélioration de l'application pour vous offrir une meilleure expérience très prochainement.
|
||||
</div>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb custom-breadcrumb">
|
||||
@@ -191,7 +191,7 @@
|
||||
<div class="container">
|
||||
<footer class="py-3 my-4">
|
||||
<ul class="nav justify-content-center border-bottom pb-3 mb-3">
|
||||
<li class="nav-item"><a class="nav-link px-2 text-muted">Version: 1.0.0-beta.15</a></li>
|
||||
<li class="nav-item"><a class="nav-link px-2 text-muted">Version: 1.0.0-beta.16</a></li>
|
||||
</ul>
|
||||
<p class="text-center text-muted">© 2024 Myaxrin Labs</p>
|
||||
</footer>
|
||||
|
||||
@@ -1,103 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Parameter Admin</title>
|
||||
<title>Paramètres Admin</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.custom-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
padding: 0.8em 1.6em; /* Réduit le padding */
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 20em;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 50px; /* Réduit la largeur minimale */
|
||||
min-height: 30px; /* Réduit la hauteur minimale */
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.animate {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
animation: slideIn 0.4s forwards;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>'); /* Placeholder for dynamic background */
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="container mt-4 animate">
|
||||
<h1 class="title text-center animate">Bienvenue dans les parametres admin</h1>
|
||||
<h4 class="title text-center animate">Que souhaitez vous faire ?</h4><br>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/users?page=1&limit=10" id="adminLink1">
|
||||
<i class="fas fa-users"></i> Gérer les utilisateurs
|
||||
</a><br>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/settingsetup" id="adminLink2">
|
||||
<i class="fas fa-cogs"></i> Modifier les parametres de configuration
|
||||
</a><br>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/stats-logs" id="adminLink3">
|
||||
<i class="fas fa-chart-bar"></i> Afficher les statistiques & les logs
|
||||
</a><br>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/Privacy-Security" id="adminLink4">
|
||||
<i class="fas fa-shield-alt"></i> Confidentialité & Sécurité
|
||||
</a><br><br>
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/" id="adminLink5">
|
||||
<i class="fas fa-home"></i> Retourner au dashboard
|
||||
</a>
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate ">Changer de Thème</button>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
width: 100%; /* Full width */
|
||||
padding: 0.75rem; /* Adjust padding as needed */
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 1000; /* Ensure it stays above other elements */
|
||||
background-color: hsl(var(--secondary));
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
padding: 0.5rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
#themeSwitcher:hover {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
#themeSwitcher svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.icon-spacing {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.swal2-toast {
|
||||
background-color: hsl(var(--card));
|
||||
color: hsl(var(--foreground));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark .swal2-toast {
|
||||
background-color: #000; /* Fond noir en mode sombre */
|
||||
color: #fff; /* Texte blanc */
|
||||
border: 1px solid #333; /* Bordure grise foncée */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate dark">
|
||||
<button id="themeSwitcher">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center">Paramètres Admin</h1>
|
||||
|
||||
<div class="form-container">
|
||||
<div class="flex flex-col gap-4">
|
||||
<a href="/dpanel/dashboard/admin/users?page=1&limit=10" class="btn btn-primary flex items-center justify-center space-x-2">
|
||||
<i class="fas fa-users icon-spacing"></i>
|
||||
<span>Gérer les utilisateurs</span>
|
||||
</a>
|
||||
|
||||
<a href="/dpanel/dashboard/admin/settingsetup" class="btn btn-primary flex items-center justify-center space-x-2">
|
||||
<i class="fas fa-cogs icon-spacing"></i>
|
||||
<span>Modifier les paramètres de configuration</span>
|
||||
</a>
|
||||
|
||||
<a href="/dpanel/dashboard/admin/stats-logs" class="btn btn-primary flex items-center justify-center space-x-2">
|
||||
<i class="fas fa-chart-bar icon-spacing"></i>
|
||||
<span>Afficher les statistiques & logs</span>
|
||||
</a>
|
||||
|
||||
<a href="/dpanel/dashboard/admin/Privacy-Security" class="btn btn-primary flex items-center justify-center space-x-2">
|
||||
<i class="fas fa-shield-alt icon-spacing"></i>
|
||||
<span>Confidentialité & Sécurité</span>
|
||||
</a>
|
||||
|
||||
<button onclick="window.location.href='/dpanel/dashboard';" class="btn btn-secondary w-full py-2 mt-4">
|
||||
<i class="fas fa-arrow-left icon-spacing"></i>
|
||||
Retour au Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
body.classList.toggle('dark-mode');
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
function showToast(icon, title) {
|
||||
Swal.fire({
|
||||
icon: icon,
|
||||
title: title,
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
customClass: {
|
||||
container: 'swal2-toast'
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,43 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Confidentialité et Sécurité</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1; /* Sit on top */
|
||||
padding-top: 100px; /* Location of the box */
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background-color: rgb(0,0,0); /* Fallback color */
|
||||
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
/* Modal Content */
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
background-color: hsl(var(--background));
|
||||
margin: 15% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
border: 1px solid hsl(var(--border));
|
||||
width: 80%;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
/* The Close Button */
|
||||
.close {
|
||||
color: #aaaaaa;
|
||||
color: hsl(var(--muted-foreground));
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
@@ -45,156 +161,108 @@
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: #000;
|
||||
color: hsl(var(--foreground));
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<div id="app" class="min-h-screen">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Confidentialité et Sécurité</h1>
|
||||
|
||||
.custom-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* réduit la taille de la police */
|
||||
padding: 0.6em 1.2em; /* réduit le padding */
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 15em; /* réduit le rayon de la bordure */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
<div class="form-container">
|
||||
<h2 class="text-2xl font-semibold mb-4">Données d'analyse</h2>
|
||||
|
||||
<% if (reports && reports.length > 0) { %>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<% reports.forEach((report, index) => { %>
|
||||
<% if (report) { %>
|
||||
<div class="text-center">
|
||||
<button class="btn btn-primary w-full reportName" data-index="<%= index %>"><%= report.name %></button>
|
||||
</div>
|
||||
<div id="myReportModal<%= index %>" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" data-index="<%= index %>">×</span>
|
||||
<pre class="whitespace-pre-wrap"><%= report.content %></pre>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<p class="text-center text-lg text-gray-500">Aucun rapport disponible pour le moment.</p>
|
||||
<% } %>
|
||||
|
||||
<div class="text-center">
|
||||
<br><button onclick="window.location.href='/dpanel/dashboard/admin';" class="btn btn-secondary w-full py-2 mt-4">
|
||||
<i class="fas fa-arrow-left icon-spacing"></i>
|
||||
Retour au Dashboard Admin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
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');
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="container mt-4 table-container animate">
|
||||
<h2 class="text-center">Confidentialité et Sécurité</h2><br>
|
||||
|
||||
|
||||
<h2>Données d'analyse</h2>
|
||||
<div class="row">
|
||||
<% reports && reports.forEach((report, index) => { %>
|
||||
<% if (report) { %>
|
||||
<div class="col-md-4 text-center">
|
||||
<button class="btn btn-primary custom-btn reportName" data-index="<%= index %>"><%= report.name %></button>
|
||||
</div>
|
||||
<% if ((index + 1) % 3 === 0) { %>
|
||||
</div><div class="row">
|
||||
<% } %>
|
||||
<div id="myReportModal<%= index %>" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" data-index="<%= index %>">×</span>
|
||||
<pre><%= report.content %></pre>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</div>
|
||||
|
||||
<br><div class="d-flex justify-content-center align-items-center">
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/" style="max-width: 250px; padding: 10px;">
|
||||
<i class="fas fa-sign-out-alt text-white "></i> Retourner au dashboard admin
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate ">Changer de Thème</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var logNames = document.getElementsByClassName("logName");
|
||||
var closeButtons = document.getElementsByClassName("close");
|
||||
|
||||
// Add event listeners to all log names
|
||||
for (var i = 0; i < logNames.length; i++) {
|
||||
logNames[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "block";
|
||||
});
|
||||
themeSwitcher.addEventListener('click', function() {
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Add event listeners to all close buttons
|
||||
for (var i = 0; i < closeButtons.length; i++) {
|
||||
closeButtons[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "none";
|
||||
});
|
||||
}
|
||||
var reportNames = document.getElementsByClassName("reportName");
|
||||
var reportCloseButtons = document.getElementsByClassName("close");
|
||||
|
||||
// When the user clicks anywhere outside of the modal, close it
|
||||
window.onclick = function(event) {
|
||||
if (event.target.className === "modal") {
|
||||
event.target.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
var reportNames = document.getElementsByClassName("reportName");
|
||||
var reportCloseButtons = document.getElementsByClassName("close");
|
||||
|
||||
// Add event listeners to all report names
|
||||
for (var i = 0; i < reportNames.length; i++) {
|
||||
reportNames[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myReportModal" + index);
|
||||
modal.style.display = "block";
|
||||
});
|
||||
}
|
||||
|
||||
// Add event listeners to all close buttons
|
||||
for (var i = 0; i < reportCloseButtons.length; i++) {
|
||||
reportCloseButtons[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myReportModal" + index);
|
||||
modal.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
// When the user clicks anywhere outside of the modal, close it
|
||||
window.onclick = function(event) {
|
||||
if (event.target.className === "modal") {
|
||||
event.target.style.display = "none";
|
||||
}
|
||||
}
|
||||
const body = document.body;
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
if (body.classList.contains('dark-mode')) {
|
||||
body.classList.remove('dark-mode');
|
||||
body.classList.add('light-mode');
|
||||
} else {
|
||||
body.classList.remove('light-mode');
|
||||
body.classList.add('dark-mode');
|
||||
}
|
||||
for (var i = 0; i < reportNames.length; i++) {
|
||||
reportNames[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myReportModal" + index);
|
||||
modal.style.display = "block";
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
});
|
||||
for (var i = 0; i < reportCloseButtons.length; i++) {
|
||||
reportCloseButtons[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myReportModal" + index);
|
||||
modal.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (event.target.className === "modal") {
|
||||
event.target.style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,254 +1,464 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Parameter Admin</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
.custom-btn {
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1830px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* réduit la taille de la police */
|
||||
padding: 0.6em 1.2em; /* réduit le padding */
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 15em; /* réduit le rayon de la bordure */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 5px 15px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-bottom: 2px solid #007BFF;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
border-bottom: 2px solid #555;
|
||||
}
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: hsl(var(--muted));
|
||||
transition: .4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: hsl(var(--background));
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
input:checked + .slider {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
animation: fadeOut 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-10px); }
|
||||
}
|
||||
|
||||
.ip-error {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Gestion de la configuration</h1>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="container mt-4 table-container animate">
|
||||
<h2 class="text-center">Gestion de la configuration</h2><br>
|
||||
<div class="table-responsive">
|
||||
<div class="form-container">
|
||||
<form id="setupForm" action="/api/dpanel/dashboard/admin/update-setup" method="POST" class="mb-4 animate">
|
||||
<h2 class="text-2xl font-semibold mb-4">Paramètres LDAP</h2>
|
||||
<div class="form-group animate flex items-center justify-between">
|
||||
<label for="ldapEnabled" class="block mb-2">Activer LDAP :</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="ldapEnabled" name="ldap[enabled]" <%= setup.ldap && setup.ldap.enabled === 'on' ? 'checked' : '' %> onchange="toggleForm('ldapForm', this)">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="ldapForm" style="display: <%= setup.ldap && setup.ldap.enabled === 'on' ? 'block' : 'none' %>">
|
||||
<div class="form-group animate">
|
||||
<label for="ldapUrl" class="block mb-2">URL :</label>
|
||||
<input type="text" id="ldapUrl" name="ldap[url]" class="form-control" value="<%= setup.ldap ? setup.ldap.url : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="ldapBaseDN" class="block mb-2">Base DN :</label>
|
||||
<input type="text" id="ldapBaseDN" name="ldap[baseDN]" class="form-control" value="<%= setup.ldap ? setup.ldap.baseDN : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="ldapUsername" class="block mb-2">Nom d'utilisateur :</label>
|
||||
<input type="text" id="ldapUsername" name="ldap[username]" class="form-control" value="<%= setup.ldap ? setup.ldap.username : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="ldapPassword" class="block mb-2">Mot de passe :</label>
|
||||
<input type="password" id="ldapPassword" name="ldap[password]" class="form-control" value="<%= setup.ldap ? setup.ldap.password : '' %>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="/api/dpanel/dashboard/admin/update-setup" method="POST">
|
||||
<table class="table w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paramètre</th>
|
||||
<th>Valeur</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2"><h2>LDAP Settings</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Activer LDAP:</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input type="checkbox" name="ldap[enabled]" <%= setup.ldap ? 'checked' : '' %>
|
||||
onchange="toggleForm('ldapForm', this)">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody id="ldapForm" style="display: block">
|
||||
<tr>
|
||||
<td>URL:</td>
|
||||
<td><input type="text" name="ldap[url]" value="<%= setup.ldap ? setup.ldap.url : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Base DN:</td>
|
||||
<td><input type="text" name="ldap[baseDN]" value="<%= setup.ldap ? setup.ldap.baseDN : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Username:</td>
|
||||
<td><input type="text" name="ldap[username]" value="<%= setup.ldap ? setup.ldap.username : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Password:</td>
|
||||
<td><input type="text" name="ldap[password]" value="<%= setup.ldap ? setup.ldap.password : '' %>"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<h2 class="text-2xl font-semibold mb-4 mt-8">Paramètres Discord</h2>
|
||||
<div class="form-group animate flex items-center justify-between">
|
||||
<label for="discordEnabled" class="block mb-2">Activer Discord :</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="discordEnabled" name="discord[enabled]" <%= setup.discord && setup.discord.enabled === 'on' ? 'checked' : '' %> onchange="toggleForm('discordForm', this)">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="discordForm" style="<%= `display: ${setup.discord?.enabled === 'on' ? 'block' : 'none'}` %>">
|
||||
<div class="form-group animate">
|
||||
<label for="discordClientID" class="block mb-2">ID Client :</label>
|
||||
<input type="text" id="discordClientID" name="discord[clientID]" class="form-control" value="<%= setup.discord ? setup.discord.clientID : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="discordClientSecret" class="block mb-2">Secret Client :</label>
|
||||
<input type="password" id="discordClientSecret" name="discord[clientSecret]" class="form-control" value="<%= setup.discord ? setup.discord.clientSecret : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="discordIdentifyURL" class="block mb-2">URL d'identification :</label>
|
||||
<input type="text" id="discordIdentifyURL" name="discord[identifyURL]" class="form-control" value="<%= setup.discord ? setup.discord.identifyURL : '' %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="discordAuthorizedIDs" class="block mb-2">IDs Autorisés :</label>
|
||||
<input type="text" id="discordAuthorizedIDs" name="discord[authorizedIDs]" class="form-control" value="<%= setup.discord ? setup.discord.authorizedIDs : '' %>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h2>Discord Settings</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Activer Discord:</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input type="checkbox" name="discord[enabled]" <%= setup.discord ? 'checked' : '' %>
|
||||
onchange="toggleForm('discordForm', this)">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody id="discordForm" style="display: block">
|
||||
<tr>
|
||||
<td>Client ID:</td>
|
||||
<td><input type="text" name="discord[clientID]" value="<%= setup.discord ? setup.discord.clientID : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client Secret:</td>
|
||||
<td><input type="text" name="discord[clientSecret]" value="<%= setup.discord ? setup.discord.clientSecret : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Identify URL:</td>
|
||||
<td><input type="text" name="discord[identifyURL]" value="<%= setup.discord ? setup.discord.identifyURL : '' %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Authorized IDs:</td>
|
||||
<td><input type="text" name="discord[authorizedIDs]" value="<%= setup.discord ? setup.discord.authorizedIDs : '' %>">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<h2 class="text-2xl font-semibold mb-4 mt-8">Autres Paramètres</h2>
|
||||
<div class="form-group animate">
|
||||
<label for="domain" class="block mb-2">Domaine :</label>
|
||||
<input type="text" id="domain" name="domain" class="form-control" value="<%= setup.domain %>">
|
||||
</div>
|
||||
<div class="form-group animate">
|
||||
<label for="webhooksDiscord" class="block mb-2">Webhooks Discord :</label>
|
||||
<input type="text" id="webhooksDiscord" name="webhooks_discord" class="form-control" value="<%= setup.webhooks_discord %>">
|
||||
</div>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h2>Other Settings</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain:</td>
|
||||
<td><input type="text" name="domain" value="<%= setup.domain %>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Discord WebHooks:</td>
|
||||
<td><input type="text" name="webhooks_discord" value="<%= setup.webhooks_discord %>"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-primary custom-btn">Mettre à jour</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/" style="max-width: 250px; padding: 10px;">
|
||||
<i class="fas fa-sign-out-alt text-white "></i> Retourner au dashboard admin
|
||||
</a>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate ">Changer de Thème</button>
|
||||
<div class="form-group animate">
|
||||
<label class="block mb-2">IPs Autorisées pour les Webhooks :</label>
|
||||
<div class="space-y-2">
|
||||
<div id="ipList" class="space-y-2">
|
||||
<% if (setup.allowedIps && setup.allowedIps.length > 0) { %>
|
||||
<% setup.allowedIps.forEach((ip, index) => { %>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="text" name="allowedIps[]" class="form-control flex-1" value="<%= ip %>">
|
||||
<button type="button" class="btn btn-secondary p-2" onclick="removeIpField(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="text" name="allowedIps[]" class="form-control flex-1"
|
||||
placeholder="IPv4/IPv6 (ex: 192.168.1.1 ou 2001:db8::1 ou CIDR)">
|
||||
<button type="button" class="btn btn-secondary p-2" onclick="removeIpField(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary w-full py-2" onclick="addIpField()">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Ajouter une IP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-full py-2 mt-4">
|
||||
<i class="fas fa-save mr-2"></i>
|
||||
Mettre à jour
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a href="/dpanel/dashboard/admin/" class="btn btn-secondary w-full py-2 mt-4 text-center">
|
||||
<i class="fas fa-arrow-left mr-2"></i>
|
||||
Retour au tableau de bord admin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
if (body.classList.contains('dark-mode')) {
|
||||
body.classList.remove('dark-mode');
|
||||
body.classList.add('light-mode');
|
||||
} else {
|
||||
body.classList.remove('light-mode');
|
||||
body.classList.add('dark-mode');
|
||||
// Gestion du thème
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
// Gestion des formulaires LDAP et Discord
|
||||
function toggleForm(formId, checkbox) {
|
||||
const form = document.getElementById(formId);
|
||||
form.style.display = checkbox.checked ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// Fonctions pour la gestion des IPs
|
||||
function addIpField() {
|
||||
const ipList = document.getElementById('ipList');
|
||||
const newField = document.createElement('div');
|
||||
newField.className = 'flex items-center space-x-2 animate';
|
||||
newField.innerHTML = `
|
||||
<input type="text" name="allowedIps[]" class="form-control flex-1"
|
||||
placeholder="ex: 10.20.0.34 ou 10.20.0.0/24">
|
||||
<button type="button" class="btn btn-secondary p-2" onclick="removeIpField(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
`;
|
||||
ipList.appendChild(newField);
|
||||
}
|
||||
|
||||
function removeIpField(button) {
|
||||
const fieldContainer = button.parentElement;
|
||||
fieldContainer.classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
fieldContainer.remove();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Fonctions de validation IP
|
||||
function validateIP(ip) {
|
||||
if (!ip) return false;
|
||||
|
||||
// Gestion CIDR
|
||||
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;
|
||||
}
|
||||
|
||||
// IP simple
|
||||
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) {
|
||||
// Regex pour IPv6 standard et compressé
|
||||
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);
|
||||
}
|
||||
|
||||
// Modification de la validation du formulaire
|
||||
document.getElementById('setupForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const ipInputs = document.querySelectorAll('input[name="allowedIps[]"]');
|
||||
let hasError = false;
|
||||
|
||||
// Réinitialiser les styles d'erreur
|
||||
ipInputs.forEach(input => {
|
||||
input.classList.remove('ip-error');
|
||||
});
|
||||
|
||||
// Valider chaque IP
|
||||
ipInputs.forEach(input => {
|
||||
const value = input.value.trim();
|
||||
if (value && !validateIP(value)) {
|
||||
input.classList.add('ip-error');
|
||||
hasError = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
if (hasError) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Erreur de validation',
|
||||
html: `
|
||||
Veuillez vérifier le format des IPs saisies.<br><br>
|
||||
Formats acceptés :<br>
|
||||
- IPv4 (ex: 192.168.1.1)<br>
|
||||
- IPv4 CIDR (ex: 192.168.1.0/24)<br>
|
||||
- IPv6 (ex: 2001:db8::1)<br>
|
||||
- IPv6 CIDR (ex: 2001:db8::/32)<br>
|
||||
- IPv6 locale (ex: fe80::1)<br>
|
||||
- IPv4-mapped IPv6 (ex: ::ffff:192.168.1.1)
|
||||
`
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
function toggleForm(formId, checkbox) {
|
||||
const form = document.getElementById(formId);
|
||||
form.style.display = checkbox.checked ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
// Soumettre le formulaire si tout est valide
|
||||
this.submit();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,43 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Statistiques du serveur</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1250px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1; /* Sit on top */
|
||||
padding-top: 100px; /* Location of the box */
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background-color: rgb(0,0,0); /* Fallback color */
|
||||
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
/* Modal Content */
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
background-color: hsl(var(--background));
|
||||
margin: 15% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
border: 1px solid hsl(var(--border));
|
||||
width: 80%;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
/* The Close Button */
|
||||
.close {
|
||||
color: #aaaaaa;
|
||||
color: hsl(var(--muted-foreground));
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
@@ -45,148 +161,127 @@
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: #000;
|
||||
color: hsl(var(--foreground));
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Statistiques du serveur</h1>
|
||||
|
||||
.custom-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* réduit la taille de la police */
|
||||
padding: 0.6em 1.2em; /* réduit le padding */
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 15em; /* réduit le rayon de la bordure */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
<div class="card mb-8">
|
||||
<h2 class="text-2xl font-semibold mb-4">Informations générales</h2>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Paramètre</th>
|
||||
<th class="text-left">Valeur</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Temps de fonctionnement</td>
|
||||
<td><%= Math.floor(uptime / 86400) %> jours, <%= Math.floor(uptime % 86400 / 3600) %> heures, <%= Math.floor(uptime % 3600 / 60) %> minutes, <%= uptime % 60 %> secondes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Utilisation de la mémoire</td>
|
||||
<td><%= memoryUsage.toFixed(2) %> Mo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Utilisation du processeur</td>
|
||||
<td><%= (cpuUsage * 100).toFixed(2) %> %</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="text-2xl font-semibold mb-4">Journaux</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<% logs && logs.forEach((log, index) => { %>
|
||||
<% if (log) { %>
|
||||
<button class="btn btn-primary logName" data-index="<%= index %>"><%= log.name %></button>
|
||||
<div id="myModal<%= index %>" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" data-index="<%= index %>">×</span>
|
||||
<pre class="whitespace-pre-wrap"><%= log.content %></pre>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex justify-center">
|
||||
<a href="/dpanel/dashboard/admin/" class="btn btn-secondary w-full py-2 mt-4 text-center">
|
||||
<i class="fas fa-arrow-left mr-2"></i>
|
||||
Retourner au dashboard admin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
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');
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="container mt-4 table-container animate">
|
||||
<h2 class="text-center">Statistiques du serveur</h2><br>
|
||||
<div class="table-responsive">
|
||||
<table class="table w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paramètre</th>
|
||||
<th>Valeur</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Temps de fonctionnement</td>
|
||||
<td><%= Math.floor(uptime / 86400) %> jours, <%= Math.floor(uptime % 86400 / 3600) %> heures, <%= Math.floor(uptime % 3600 / 60) %> minutes, <%= uptime % 60 %> secondes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Utilisation de la mémoire</td>
|
||||
<td><%= memoryUsage.toFixed(2) %> Mo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Utilisation du processeur</td>
|
||||
<td><%= (cpuUsage * 100).toFixed(2) %> %</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Journaux</h2>
|
||||
<div class="row">
|
||||
<% logs && logs.forEach((log, index) => { %>
|
||||
<% if (log) { %>
|
||||
<div class="col-md-4 text-center">
|
||||
<button class="btn btn-primary custom-btn logName" data-index="<%= index %>"><%= log.name %></button>
|
||||
</div>
|
||||
<% if ((index + 1) % 3 === 0) { %>
|
||||
</div><div class="row">
|
||||
<% } %>
|
||||
<div id="myModal<%= index %>" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" data-index="<%= index %>">×</span>
|
||||
<pre><%= log.content %></pre>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate ">Changer de Thème</button>
|
||||
</div>
|
||||
|
||||
<br><div class="d-flex justify-content-center align-items-center">
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/" style="max-width: 250px; padding: 10px;">
|
||||
<i class="fas fa-sign-out-alt text-white "></i> Retourner au dashboard admin
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var logNames = document.getElementsByClassName("logName");
|
||||
var closeButtons = document.getElementsByClassName("close");
|
||||
|
||||
// Add event listeners to all log names
|
||||
for (var i = 0; i < logNames.length; i++) {
|
||||
logNames[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "block";
|
||||
});
|
||||
themeSwitcher.addEventListener('click', function() {
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Add event listeners to all close buttons
|
||||
for (var i = 0; i < closeButtons.length; i++) {
|
||||
closeButtons[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "none";
|
||||
});
|
||||
}
|
||||
var logNames = document.getElementsByClassName("logName");
|
||||
var closeButtons = document.getElementsByClassName("close");
|
||||
|
||||
// When the user clicks anywhere outside of the modal, close it
|
||||
window.onclick = function(event) {
|
||||
if (event.target.className === "modal") {
|
||||
event.target.style.display = "none";
|
||||
}
|
||||
}
|
||||
const body = document.body;
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
if (body.classList.contains('dark-mode')) {
|
||||
body.classList.remove('dark-mode');
|
||||
body.classList.add('light-mode');
|
||||
} else {
|
||||
body.classList.remove('light-mode');
|
||||
body.classList.add('dark-mode');
|
||||
}
|
||||
for (var i = 0; i < logNames.length; i++) {
|
||||
logNames[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "block";
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
});
|
||||
for (var i = 0; i < closeButtons.length; i++) {
|
||||
closeButtons[i].addEventListener("click", function(event) {
|
||||
var index = event.target.getAttribute("data-index");
|
||||
var modal = document.getElementById("myModal" + index);
|
||||
modal.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (event.target.className === "modal") {
|
||||
event.target.style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,142 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<link rel="stylesheet" href="/public/css/login.css">
|
||||
<title>Parameter Admin</title>
|
||||
<title>Gestion des utilisateurs</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 0.75rem;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.page-item.active .page-link {
|
||||
z-index: 1;
|
||||
color: hsl(var(--primary-foreground));
|
||||
background-color: hsl(var(--primary));
|
||||
border-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.page-link {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-left: -1px;
|
||||
line-height: 1.25;
|
||||
color: hsl(var(--primary));
|
||||
background-color: hsl(var(--background));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate">
|
||||
<div class="container mt-4 animate">
|
||||
<h2 class="text-3xl font-semibold mb-6 text-center">Gestion des utilisateurs</h2>
|
||||
|
||||
<style>
|
||||
.pagination .active .page-link {
|
||||
background-color: #007bff !important;
|
||||
color: white !important;
|
||||
}
|
||||
<div class="form-container">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<input type="text" id="searchInput" class="form-control w-1/2" placeholder="Rechercher par nom ou ID">
|
||||
<span id="shortcutHint" class="text-sm text-gray-500"></span>
|
||||
<button id="searchButton" class="btn btn-primary">Rechercher</button>
|
||||
<select id="usersPerPage" class="form-control w-auto">
|
||||
<option value="10">10</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
<option value="500">500</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
.table-container {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
#searchInput {
|
||||
flex-grow: 1;
|
||||
}
|
||||
#searchButton {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.custom-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* réduit la taille de la police */
|
||||
padding: 0.6em 1.2em; /* réduit le padding */
|
||||
color: white;
|
||||
background: linear-gradient(0deg, rgba(77,54,208,1) 0%, rgba(132,116,254,1) 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.7em 1.5em -0.5em #4d36d0be;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 15em; /* réduit le rayon de la bordure */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body class="light-mode animate">
|
||||
<div class="container mt-4 table-container animate">
|
||||
<h2 class="text-center">Gestion des utilisateurs</h2><br>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<input type="text" id="searchInput" class="form-control rounded mr-2" placeholder="Rechercher par nom ou ID">
|
||||
<span id="shortcutHint" class="mr-2 align-self-center"></span>
|
||||
<button id="searchButton" class="btn btn-primary custom-btn">Rechercher</button>
|
||||
<select id="usersPerPage" class="form-control rounded ml-2" style="width: auto;">
|
||||
<option value="10">10</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
<option value="500">500</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="table-responsive mt-2">
|
||||
<table class="table w-100 table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nom</th>
|
||||
<th>Rôle</th>
|
||||
<th class="text-right">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% users.forEach(user => { %>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><%= user.id %></td>
|
||||
<td><%= user.name %></td>
|
||||
<td><%= user.role %></td>
|
||||
<td>
|
||||
<form id="update-role-form" action="/api/dpanel/dashboard/admin/update-role" method="POST" class="d-flex align-items-center">
|
||||
<input type="hidden" name="id" value="<%= user.id %>">
|
||||
<input type="hidden" name="name" value="<%= user.name %>">
|
||||
<select class="form-control rounded mr-2" name="role">
|
||||
<option value="user" <%= user.role === 'user' ? 'selected' : '' %>>User</option>
|
||||
<option value="admin" <%= user.role === 'admin' ? 'selected' : '' %>>Admin</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary btn-round custom-btn">Mettre à jour</button>
|
||||
</form>
|
||||
<form id="generate-token-form" action="/api/dpanel/generate-token" method="POST" class="d-flex align-items-center mt-2">
|
||||
<input type="hidden" name="id" value="<%= user.id %>">
|
||||
<input type="hidden" name="name" value="<%= user.name %>">
|
||||
<button type="submit" class="btn btn-secondary btn-round custom-btn">Générer Token</button>
|
||||
</form>
|
||||
</td>
|
||||
<th>ID</th>
|
||||
<th>Nom</th>
|
||||
<th>Rôle</th>
|
||||
<th>Photo</th>
|
||||
<th class="text-right">Action</th>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav aria-label="Page navigation example">
|
||||
<ul class="pagination justify-content-center">
|
||||
<% for(let i = 1; i <= pages; i++) { %>
|
||||
<li class="page-item <%= i === currentPage ? 'active' : '' %>">
|
||||
<a class="page-link" href="/dpanel/dashboard/admin/users?page=<%= i %>&limit=<%= limit %>"><%= i %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<a class="dropdown-item text-center text-white no-hover custom-btn" href="/dpanel/dashboard/admin/" style="max-width: 250px; padding: 10px;">
|
||||
<i class="fas fa-sign-out-alt text-white "></i> Retourner au dashboard admin
|
||||
</a>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% users.forEach(user => { %>
|
||||
<tr>
|
||||
<td><%= user.id %></td>
|
||||
<td><%= user.name %></td>
|
||||
<td><%= user.role %></td>
|
||||
<td>
|
||||
<div class="profile-picture text-center mb-6">
|
||||
<a class="btn btn-secondary" id="accountDropdownBtn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img
|
||||
src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + user.name %>"
|
||||
alt="User Icon"
|
||||
class="rounded-full"
|
||||
style="max-width: 90px; max-height: 80px;"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<form id="update-role-form" action="/api/dpanel/dashboard/admin/update-role" method="POST" class="flex items-center">
|
||||
<input type="hidden" name="id" value="<%= user.id %>">
|
||||
<input type="hidden" name="name" value="<%= user.name %>">
|
||||
<select class="form-control mr-2" name="role">
|
||||
<option value="user" <%= user.role === 'user' ? 'selected' : '' %>>User</option>
|
||||
<option value="admin" <%= user.role === 'admin' ? 'selected' : '' %>>Admin</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Mettre à jour</button>
|
||||
</form>
|
||||
<form id="generate-token-form" action="/api/dpanel/generate-token" method="POST" class="flex items-center mt-2">
|
||||
<input type="hidden" name="id" value="<%= user.id %>">
|
||||
<input type="hidden" name="name" value="<%= user.name %>">
|
||||
<button type="submit" class="btn btn-secondary">Générer Token</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-center">
|
||||
<% for(let i = 1; i <= pages; i++) { %>
|
||||
<li class="page-item <%= i === currentPage ? 'active' : '' %>">
|
||||
<a class="page-link" href="/dpanel/dashboard/admin/users?page=<%= i %>&limit=<%= limit %>"><%= i %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="flex justify-center mt-4">
|
||||
<a href="/dpanel/dashboard/admin/" class="btn btn-secondary">
|
||||
<i class="fas fa-sign-out-alt mr-2"></i> Retourner au dashboard admin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center animate">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 animate ">Changer de Thème</button>
|
||||
</div>
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
document.body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
document.body.classList.toggle('dark-mode', e.matches);
|
||||
});
|
||||
});
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
document.body.classList.toggle('dark-mode');
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('usersPerPage').addEventListener('change', function () {
|
||||
@@ -178,7 +352,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Ajout d'un événement pour les formulaires de génération de token
|
||||
document.querySelectorAll('form[id="generate-token-form"]').forEach(form => {
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>En maintenance...</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png"/>
|
||||
<style>
|
||||
body {
|
||||
background-color: #000;
|
||||
text-align: center;
|
||||
padding: 150px; }
|
||||
h1 {
|
||||
font-size: 50px; }
|
||||
body {
|
||||
font: 20px Helvetica, sans-serif; color: #fff;
|
||||
}
|
||||
img{
|
||||
width : 300px;
|
||||
height : 300px;
|
||||
}
|
||||
a{
|
||||
color: #FFF
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img id="img" src="/client/assets/img/homelab_logo.png">
|
||||
<h1>Désolé, nous sommes en maintenance.</h1>
|
||||
<h2>Nous serons bientôt de retour..</h2>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,6 +4,10 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Profil Utilisateur</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
@@ -14,115 +18,255 @@
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin: 10px 0 5px;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: calc(100% - 22px);
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
.user-info {
|
||||
margin: 20px 0;
|
||||
text-align: left;
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.profile-picture {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
.profile-picture img {
|
||||
max-width: 150px;
|
||||
border-radius: 50%;
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.return-btn {
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border-radius: 12px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.icon-spacing {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.swal2-toast {
|
||||
background-color: hsl(var(--card));
|
||||
color: hsl(var(--foreground));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark .swal2-toast {
|
||||
background-color: #000; /* Fond noir en mode sombre */
|
||||
color: #fff; /* Texte blanc */
|
||||
border: 1px solid #333; /* Bordure grise foncée */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="profile-picture">
|
||||
<a class="btn dropdown-toggle" id="accountDropdownBtn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img
|
||||
src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + user.name %>"
|
||||
alt="User Icon"
|
||||
class="rounded-circle"
|
||||
style="width: 30px; height: 30px;"
|
||||
/>
|
||||
</a> </div>
|
||||
<h1>Bienvenue, <span id="userName"><%= user.name %></span> !</h1>
|
||||
<body class="animate dark">
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Profil Utilisateur</h1>
|
||||
|
||||
<div class="user-info">
|
||||
<p><strong>ID :</strong> <span id="userId"><%= user.id %></span></p>
|
||||
<p><strong>Rôle :</strong> <span id="userRole" class="badge"><%= user.role %></span></p>
|
||||
</div>
|
||||
<div class="form-container">
|
||||
<div class="profile-picture text-center mb-6">
|
||||
<a class="btn btn-secondary" id="accountDropdownBtn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img
|
||||
src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + user.name %>"
|
||||
alt="User Icon"
|
||||
class="rounded-full"
|
||||
style="max-width: 120px; max-height: 120px;"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form id="wallpaperForm">
|
||||
<div>
|
||||
<label for="wallpaperUrl">Entrez l'URL du fond d'écran :</label>
|
||||
<input type="text" id="wallpaperUrl" name="wallpaperUrl" placeholder="http://example.com/image.jpg">
|
||||
<div class="text-center mb-6">
|
||||
<h2 class="text-2xl font-semibold mb-2">Bienvenue, <span id="userName"><%= user.name %></span> !</h2>
|
||||
<p><strong>ID :</strong> <span id="userId"><%= user.id %></span></p>
|
||||
<p><strong>Rôle :</strong> <span id="userRole" class="badge bg-blue-500 text-white px-2 py-1 rounded-full"><%= user.role %></span></p>
|
||||
</div>
|
||||
|
||||
<form id="wallpaperForm" class="mb-4">
|
||||
<div class="form-group">
|
||||
<label for="wallpaperUrl" class="block mb-2">Entrez l'URL du fond d'écran :</label>
|
||||
<input type="text" id="wallpaperUrl" name="wallpaperUrl" placeholder="http://example.com/image.jpg" class="form-control">
|
||||
</div>
|
||||
<button type="submit" id="updateWallpaper" class="btn btn-primary w-full py-2 mt-4">
|
||||
<i class="fas fa-image icon-spacing"></i>
|
||||
Mettre à jour le fond d'écran
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form id="profilePictureForm" class="mb-4">
|
||||
<div class="form-group">
|
||||
<label for="profilePictureUrl" class="block mb-2">Entrez l'URL de la photo de profil :</label>
|
||||
<input type="text" id="profilePictureUrl" name="profilePictureUrl" placeholder="http://example.com/path/to/profile.jpg" class="form-control">
|
||||
</div>
|
||||
<button type="submit" id="updateProfilePicture" class="btn btn-primary w-full py-2 mt-4">
|
||||
<i class="fas fa-user-circle icon-spacing"></i>
|
||||
Mettre à jour la photo de profil
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<button onclick="window.location.href='/dpanel/dashboard';" class="btn btn-secondary w-full py-2 mt-4">
|
||||
<i class="fas fa-arrow-left icon-spacing"></i>
|
||||
Retour au Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit">Mettre à jour le fond d'écran</button>
|
||||
</form>
|
||||
|
||||
<form id="profilePictureForm">
|
||||
<div>
|
||||
<label for="profilePictureUrl">Entrez l'URL de la photo de profil :</label>
|
||||
<input type="text" id="profilePictureUrl" name="profilePictureUrl" placeholder="http://example.com/path/to/profile.jpg">
|
||||
</div>
|
||||
<button type="submit">Mettre à jour la photo de profil</button>
|
||||
</form>
|
||||
|
||||
<div class="return-btn">
|
||||
<button onclick="window.location.href='/dpanel/dashboard';">Retour au Dashboard</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
const userId = '<%= user.id %>'; // Ensure this is set correctly by EJS
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
document.getElementById('wallpaperForm').addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
function showToast(icon, title) {
|
||||
Swal.fire({
|
||||
icon: icon,
|
||||
title: title,
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
customClass: {
|
||||
container: 'swal2-toast'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('wallpaperForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const urlInput = document.getElementById('wallpaperUrl').value;
|
||||
|
||||
if (urlInput) {
|
||||
@@ -134,23 +278,22 @@
|
||||
},
|
||||
body: JSON.stringify({
|
||||
wallpaperUrl: urlInput,
|
||||
userId: userId // Ensure this is passed correctly
|
||||
userId: '<%= user.id %>'
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
document.body.style.backgroundImage = `url('${data.wallpaper}')`;
|
||||
alert('Fond d\'écran mis à jour avec succès !');
|
||||
showToast('success', 'Fond d\'écran mis à jour avec succès !');
|
||||
} else {
|
||||
alert('Échec de la mise à jour du fond d\'écran');
|
||||
showToast('error', 'Échec de la mise à jour du fond d\'écran');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('profilePictureForm').addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
document.getElementById('profilePictureForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const urlInput = document.getElementById('profilePictureUrl').value;
|
||||
|
||||
if (urlInput) {
|
||||
@@ -162,19 +305,20 @@
|
||||
},
|
||||
body: JSON.stringify({
|
||||
profilePictureUrl: urlInput,
|
||||
userId: userId
|
||||
userId: '<%= user.id %>'
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
document.getElementById('currentProfilePicture').src = data.profilePicture;
|
||||
alert('Photo de profil mise à jour avec succès !');
|
||||
document.querySelector('.profile-picture img').src = data.profilePicture;
|
||||
showToast('success', 'Photo de profil mise à jour avec succès !');
|
||||
} else {
|
||||
alert('Échec de la mise à jour de la photo de profil');
|
||||
showToast('error', 'Échec de la mise à jour de la photo de profil');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
190
views/promote.ejs
Normal file
190
views/promote.ejs
Normal file
@@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CDN - Myaxrin Labs</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
animation: fadeInDown 0.6s ease-out;
|
||||
}
|
||||
|
||||
.promo-container {
|
||||
background: #111111;
|
||||
border-radius: 12px;
|
||||
max-width: 800px;
|
||||
width: 90%;
|
||||
padding: 2.5rem;
|
||||
animation: fadeIn 0.8s ease-out;
|
||||
transform: translateY(20px);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: #1a1a1a;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.discord-button {
|
||||
background-color: #5865F2;
|
||||
transition: all 0.2s ease;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.discord-button:hover {
|
||||
background-color: #4752c4;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.title-color {
|
||||
color: #3b82f6;
|
||||
background: linear-gradient(45deg, #3b82f6, #60a5fa);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.selfhosted-badge {
|
||||
background: linear-gradient(45deg, #3b82f6, #60a5fa);
|
||||
animation: fadeInRight 0.6s ease-out;
|
||||
}
|
||||
|
||||
.try-button {
|
||||
background: linear-gradient(45deg, #3b82f6, #60a5fa);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.try-button:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(88, 101, 242, 0.4);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(88, 101, 242, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(88, 101, 242, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex items-center justify-center p-4">
|
||||
<a href="/" class="absolute top-6 left-6">
|
||||
<img src="https://cdn.dinawo.fr/public/assets/homelab_logo.png" alt="Logo" class="logo">
|
||||
</a>
|
||||
|
||||
<div class="promo-container">
|
||||
<div class="text-center mb-12">
|
||||
<img src="https://cdn.dinawo.fr/public/assets/homelab_logo.png" alt="Logo" class="logo mx-auto mb-6 animate-float">
|
||||
<h1 class="text-3xl font-bold mb-4 title-color">Bienvenue sur le CDN de Myaxrin Labs</h1>
|
||||
<div class="inline-block selfhosted-badge px-3 py-1 rounded-full text-sm font-medium mb-4">
|
||||
100% Self-Hosted
|
||||
</div>
|
||||
<p class="text-gray-300 mb-2">✨ Découvrez notre infrastructure de distribution de contenu nouvelle génération.</p>
|
||||
<p class="text-gray-400">Ce service est propulsé par Myaxrin Labs, un laboratoire dédié à l'innovation technologique.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-12">
|
||||
<div class="feature-card text-center" style="animation: fadeIn 0.8s ease-out 0.2s both;">
|
||||
<div class="text-4xl mb-4 text-blue-500">🚀</div>
|
||||
<h3 class="text-lg font-semibold mb-2">Performance</h3>
|
||||
<p class="text-gray-400 text-sm">Distribution ultra-rapide de vos contenus avec une infrastructure optimisée</p>
|
||||
</div>
|
||||
<div class="feature-card text-center" style="animation: fadeIn 0.8s ease-out 0.4s both;">
|
||||
<div class="text-4xl mb-4 text-blue-500">🛡️</div>
|
||||
<h3 class="text-lg font-semibold mb-2">Sécurité</h3>
|
||||
<p class="text-gray-400 text-sm">Protection avancée de vos données avec chiffrement de bout en bout</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center mb-8" style="animation: fadeIn 0.8s ease-out 0.6s both;">
|
||||
<a href="https://cdn-app.dinawo.fr" target="_blank"
|
||||
class="try-button inline-flex items-center px-6 py-3 rounded-lg text-white font-medium">
|
||||
Essayer maintenant
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="text-center border-t border-gray-800 pt-8" style="animation: fadeIn 0.8s ease-out 0.8s both;">
|
||||
<p class="text-gray-300 mb-6">✨ Rejoignez notre communauté pour suivre nos dernières innovations !</p>
|
||||
<a href="https://discord.gg/k6r96Tmtgx" target="_blank"
|
||||
class="discord-button inline-flex items-center px-6 py-3 rounded-lg text-white font-medium">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 71 55" fill="currentColor">
|
||||
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"/>
|
||||
</svg>
|
||||
Rejoindre le Discord
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
363
views/upload.ejs
363
views/upload.ejs
@@ -1,98 +1,236 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/public/css/upload.css">
|
||||
<link rel="stylesheet" href="/public/css/styles.css">
|
||||
<title>Upload</title>
|
||||
<title>Upload de Fichiers</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#themeSwitcher {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.icon-spacing {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 2px;
|
||||
background-color: hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar div {
|
||||
height: 100%;
|
||||
background-color: hsl(var(--primary));
|
||||
width: 0%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="animate dark">
|
||||
<button id="themeSwitcher" class="btn btn-secondary p-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-image: url('<%= user.wallpaper %>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<h1 class="text-3xl font-semibold mb-6 text-center animate">Upload de Fichiers</h1>
|
||||
|
||||
<div class="form-container">
|
||||
<form id="uploadForm">
|
||||
<div class="form-group">
|
||||
<label for="file" class="block mb-2">Sélectionnez un fichier :</label>
|
||||
<input type="file" name="file" id="fileInput" accept=".zip, .pdf, .txt, .jpg, .jpeg, .png, .gif, .iso, .mp4" class="form-control">
|
||||
</div>
|
||||
|
||||
<body class="light-mode">
|
||||
<div class="alert alert-primary text-center" role="alert">
|
||||
Un nouveau look sera bientôt disponible pour la page de téléversement. Myaxrin Labs va améliorer son application pour la rendre plus rapide et plus facile à utiliser.
|
||||
</div>
|
||||
<div class="container mt-4 flex-column">
|
||||
<h1 class="mb-4">Upload de Fichiers</h1>
|
||||
<form id="uploadForm" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="file">Sélectionnez un fichier :</label>
|
||||
<input class="form-control" type="file" name="file" id="fileInput"
|
||||
accept=".zip, .pdf, .txt, .jpg, .jpeg, .png, .gif, .iso, .mp4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="expiryDate" class="block mb-2">Date d'expiration :</label>
|
||||
<input type="date" name="expiryDate" id="expiryDate" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="expiryDate">Date d'expiration :</label>
|
||||
<input class="form-control" type="date" name="expiryDate" id="expiryDate">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Mot de passe :</label>
|
||||
<input class="form-control" type="password" name="password" id="password">
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group">
|
||||
<label for="password" class="block mb-2">Mot de passe :</label>
|
||||
<input type="password" name="password" id="password" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="progress mt-4 bg-white shadow-sm form-control">
|
||||
<div class="progress-bar" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<div class="mt-2 progress-info">
|
||||
<small class="text-muted" id="progressText">0%</small>
|
||||
<small class="text-muted ml-3" id="estimatedTime">Temps estimé : Calcul en cours...</small>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label class="block mb-2">Progression :</label>
|
||||
<div class="progress-bar relative">
|
||||
<div id="progressBar"></div>
|
||||
<div id="progressText" class="absolute inset-0 flex items-center justify-center text-white font-semibold">0%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<button onclick="window.location.href='/dpanel/dashboard';" class="btn btn-primary mt-3 custom-btn">Retourner au Dashboard</button>
|
||||
<div class="form-group mb-4">
|
||||
<p id="estimatedTime" class="text-sm text-gray-400">Temps estimé : 0 min 0 sec</p>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="darkModeSwitch">
|
||||
<button id="themeSwitcher" class="btn btn-warning mt-3 ml-2">Changer de Thème</button>
|
||||
<button type="submit" id="uploadButton" class="btn btn-primary w-full py-2 mt-4">
|
||||
<i class="fas fa-upload icon-spacing"></i>
|
||||
Téléverser
|
||||
</button>
|
||||
</form>
|
||||
<div class="text-center">
|
||||
<button onclick="window.location.href='/dpanel/dashboard';" class="btn btn-secondary w-full py-2 mt-4">
|
||||
<i class="fas fa-arrow-left icon-spacing"></i>
|
||||
Retour au Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" form="uploadForm" class="btn btn-primary mt-3 custom-btn">Envoyer</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const uploadForm = document.getElementById('uploadForm');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
const progressText = document.getElementById('progressText');
|
||||
const estimatedTime = document.getElementById('estimatedTime');
|
||||
const uploadForm = document.getElementById('uploadForm');
|
||||
const body = document.body;
|
||||
|
||||
document.getElementById('themeSwitcher').addEventListener('click', function () {
|
||||
body.classList.toggle('dark-mode');
|
||||
});
|
||||
|
||||
uploadForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
uploadForm.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const file = fileInput.files[0];
|
||||
|
||||
if (!file) {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'error',
|
||||
title: 'Veuillez sélectionner \nun fichier avant de soumettre.',
|
||||
title: 'Aucun fichier sélectionné',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true,
|
||||
@@ -100,94 +238,79 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const originalFileName = file.name;
|
||||
|
||||
const expiryDate = document.getElementById('expiryDate').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('expiryDate', expiryDate);
|
||||
formData.append('password', password);
|
||||
formData.append('expiryDate', document.getElementById('expiryDate').value);
|
||||
formData.append('password', document.getElementById('password').value);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/api/dpanel/upload', true);
|
||||
|
||||
xhr.upload.onprogress = (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const percentage = Math.round((event.loaded / event.total) * 100);
|
||||
const remainingTime = calculateRemainingTime(event.loaded, event.total, event.timeStamp);
|
||||
updateProgress(percentage, remainingTime);
|
||||
xhr.upload.addEventListener('progress', function(e) {
|
||||
if (e.lengthComputable) {
|
||||
const percentComplete = Math.round((e.loaded / e.total) * 100);
|
||||
const remainingBytes = e.total - e.loaded;
|
||||
const bytesPerSecond = e.loaded / (Date.now() - startTime);
|
||||
const remainingSeconds = Math.ceil(remainingBytes / bytesPerSecond);
|
||||
const minutes = Math.floor(remainingSeconds / 60);
|
||||
const seconds = remainingSeconds % 60;
|
||||
|
||||
progressBar.style.width = `${percentComplete}%`;
|
||||
progressText.textContent = `${percentComplete}%`;
|
||||
estimatedTime.textContent = `Temps estimé : ${minutes} min ${seconds} sec`;
|
||||
}
|
||||
};
|
||||
xhr.onload = () => {
|
||||
});
|
||||
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'success',
|
||||
title: `Votre fichier ${originalFileName} a été téléchargé avec succès.`,
|
||||
title: 'Fichier téléchargé avec succès !',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true,
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'error',
|
||||
title: 'Erreur lors du téléchargement du fichier.',
|
||||
title: 'Échec du téléchargement',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true,
|
||||
});
|
||||
console.error('Erreur lors du téléchargement du fichier.', xhr.status, xhr.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
const startTime = Date.now();
|
||||
});
|
||||
|
||||
function calculateRemainingTime(loaded, total, timeStamp) {
|
||||
const bytesPerSecond = loaded / (timeStamp / 1000);
|
||||
const remainingBytes = total - loaded;
|
||||
const remainingSeconds = Math.round(remainingBytes / bytesPerSecond);
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('themeSwitcher');
|
||||
|
||||
if (remainingSeconds < 60) {
|
||||
return remainingSeconds + ' seconde' + (remainingSeconds !== 1 ? 's' : '');
|
||||
} else if (remainingSeconds < 3600) {
|
||||
const remainingMinutes = Math.floor(remainingSeconds / 60);
|
||||
const remainingSecondsPart = remainingSeconds % 60;
|
||||
return remainingMinutes + ' minute' + (remainingMinutes !== 1 ? 's' : '') + ' et ' + remainingSecondsPart + ' seconde' + (remainingSecondsPart !== 1 ? 's' : '');
|
||||
function setTheme(theme) {
|
||||
if (theme === 'dark') {
|
||||
body.classList.add('dark');
|
||||
} else {
|
||||
const remainingHours = Math.floor(remainingSeconds / 3600);
|
||||
const remainingMinutes = Math.floor((remainingSeconds % 3600) / 60);
|
||||
const remainingSecondsPart = remainingSeconds % 60;
|
||||
return remainingHours + ' heure' + (remainingHours !== 1 ? 's' : '') + ' ' + remainingMinutes + ' minute' + (remainingMinutes !== 1 ? 's' : '') + ' et ' + remainingSecondsPart + ' seconde' + (remainingSecondsPart !== 1 ? 's' : '');
|
||||
body.classList.remove('dark');
|
||||
}
|
||||
localStorage.setItem('theme', theme);
|
||||
}
|
||||
|
||||
function updateProgress(percentage, timeRemaining) {
|
||||
progressBar.style.width = percentage + '%';
|
||||
progressBar.setAttribute('aria-valuenow', percentage);
|
||||
progressText.textContent = percentage + '%';
|
||||
estimatedTime.textContent = 'Temps estimé : ' + timeRemaining;
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme) {
|
||||
setTheme(savedTheme);
|
||||
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
setTheme('dark');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const darkModeSwitch = document.getElementById('darkModeSwitch');
|
||||
const body = document.body;
|
||||
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
body.classList.toggle('dark-mode', darkModeMediaQuery.matches);
|
||||
|
||||
darkModeMediaQuery.addListener(function (e) {
|
||||
body.classList.toggle('dark-mode', e.matches);
|
||||
});
|
||||
themeSwitcher.addEventListener('click', function() {
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user