This commit is contained in:
@@ -1,121 +1,345 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Fichier sécurisé</title>
|
||||
<link rel="icon" href="/public/assets/homelab_logo.png" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<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://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--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%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.animate {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
animation: slideIn 0.4s forwards;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.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.75rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: relative;
|
||||
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.75rem 1.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@keyframes successShake {
|
||||
0%, 100% { transform: translateX(0); background-color: #10B981; }
|
||||
25% { transform: translateX(-2px); }
|
||||
75% { transform: translateX(2px); }
|
||||
}
|
||||
|
||||
@keyframes failShake {
|
||||
0%, 100% { transform: translateX(0); background-color: #EF4444; }
|
||||
20%, 60% { transform: translateX(-4px); }
|
||||
40%, 80% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
.success-animation {
|
||||
animation: successShake 0.5s ease;
|
||||
background-color: #10B981 !important;
|
||||
}
|
||||
|
||||
.fail-animation {
|
||||
animation: failShake 0.5s ease;
|
||||
background-color: #EF4444 !important;
|
||||
}
|
||||
|
||||
@keyframes loading-bar {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.animate-loading-bar {
|
||||
animation: loading-bar 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
from {
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) scale(4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.custom-btn {
|
||||
transition: transform 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
|
||||
color: #007BFF;
|
||||
background-color: transparent;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.25);
|
||||
border: 2px solid #007BFF;
|
||||
}
|
||||
|
||||
.custom-btn:hover {
|
||||
transform: scale(1.15);
|
||||
background-color: #007BFF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.animate {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
animation: slideIn 0.4s forwards;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="bg-gray-900 text-white">
|
||||
<div class="flex justify-center items-center h-screen animate">
|
||||
<div class="max-w-md mx-auto bg-gray-800 p-8 rounded shadow-md">
|
||||
<body class="animate">
|
||||
<div id="app" class="min-h-screen flex items-center justify-center">
|
||||
<div class="container mt-8">
|
||||
<div class="max-w-md mx-auto">
|
||||
<h1 class="text-3xl font-bold text-center mb-8">Entrer le mot de passe pour <%= filename %></h1>
|
||||
<p class="text-center text-red-500 mb-4">Le fichier <span class="text-blue-500"><%= filename %></span> est protégé. Veuillez entrer le mot de passe pour y accéder.</p>
|
||||
<form id="password-form" action="/attachments/<%= userId %>/<%= filename %>" method="post">
|
||||
<div class="mb-4">
|
||||
<label for="password" class="block text-gray-200 font-bold mb-2">Mot de passe :</label>
|
||||
<input type="password" id="password" name="password" required class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-200 bg-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<input type="submit" value="Soumettre" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline custom-btn">
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-container">
|
||||
<p class="text-center mb-4">Le fichier <span class="text-blue-500"><%= filename %></span> est protégé. Veuillez entrer le mot de passe pour y accéder.</p>
|
||||
<form id="password-form" action="/attachments/<%= userId %>/<%= filename %>" method="post">
|
||||
<div class="form-group mb-4">
|
||||
<label for="password" class="block font-bold mb-2">Mot de passe :</label>
|
||||
<input type="password" id="password" name="password" required class="form-control">
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa-solid fa-lock mr-2"></i>
|
||||
Soumettre
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="theme-switch" class="fixed top-4 right-4 btn 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>
|
||||
// Gestion du thème
|
||||
const body = document.body;
|
||||
const themeSwitcher = document.getElementById('theme-switch');
|
||||
|
||||
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('(prefers-color-scheme: dark)').matches) {
|
||||
setTheme('dark');
|
||||
}
|
||||
|
||||
themeSwitcher.addEventListener('click', () => {
|
||||
body.classList.contains('dark') ? setTheme('light') : setTheme('dark');
|
||||
});
|
||||
|
||||
// Gestion des cookies
|
||||
function setCookie(name, value, minutes) {
|
||||
const expires = new Date(Date.now() + minutes * 60 * 1000).toUTCString();
|
||||
document.cookie = `${name}=${value}; expires=${expires}; path=/`;
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
const cookies = document.cookie.split('; ');
|
||||
for (let cookie of cookies) {
|
||||
const [key, value] = cookie.split('=');
|
||||
if (key === name) return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Variables dynamiques
|
||||
const userId = '<%= userId %>';
|
||||
const filename = '<%= filename %>';
|
||||
const cookieName = `auth_${userId}_${filename}`;
|
||||
|
||||
// Vérification du cookie
|
||||
const savedPassword = getCookie(cookieName);
|
||||
if (savedPassword) {
|
||||
$.ajax({
|
||||
url: `/attachments/${userId}/${filename}`,
|
||||
method: 'POST',
|
||||
data: { password: savedPassword }
|
||||
}).done(function(response) {
|
||||
if (response.success) {
|
||||
window.location = `/attachments/${userId}/${filename}`;
|
||||
} else {
|
||||
// Si le cookie n'est plus valide, on le supprime
|
||||
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gestion du formulaire
|
||||
$('#password-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const userId = '<%= userId %>';
|
||||
const filename = '<%= filename %>';
|
||||
const enteredPassword = $('#password').val();
|
||||
const submitButton = $(this).find('button[type="submit"]');
|
||||
|
||||
if (!enteredPassword) {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'error',
|
||||
title: 'Password is required',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true
|
||||
});
|
||||
submitButton.addClass('fail-animation');
|
||||
submitButton.html('<i class="fas fa-times mr-2"></i>Mot de passe requis');
|
||||
|
||||
setTimeout(() => {
|
||||
submitButton.removeClass('fail-animation');
|
||||
submitButton.prop('disabled', false);
|
||||
submitButton.html('<i class="fa-solid fa-lock mr-2"></i>Soumettre');
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('/attachments/' + userId + '/' + filename, { password: enteredPassword })
|
||||
submitButton.prop('disabled', true);
|
||||
submitButton.html('<i class="fas fa-spinner fa-spin mr-2"></i>Vérification...');
|
||||
|
||||
$.post($(this).attr('action'), { password: enteredPassword })
|
||||
.done(function(data) {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'success',
|
||||
title: 'Mot de passe correct',
|
||||
text: 'Vous allez être redirigé vers le fichier !',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true
|
||||
}).then(() => {
|
||||
window.location.href = '/attachments/' + userId + '/' + filename;
|
||||
});
|
||||
setCookie(cookieName, enteredPassword, 15);
|
||||
|
||||
submitButton.addClass('success-animation');
|
||||
submitButton.html('<i class="fas fa-check mr-2"></i>Succès!');
|
||||
|
||||
const ripple = document.createElement('span');
|
||||
ripple.style.cssText = `
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
animation: ripple 1s ease-out;
|
||||
`;
|
||||
submitButton[0].appendChild(ripple);
|
||||
|
||||
setTimeout(() => {
|
||||
const formContainer = $('.form-container');
|
||||
const h1Title = $('h1');
|
||||
|
||||
formContainer.css({
|
||||
'opacity': '0',
|
||||
'transform': 'translateY(-20px) scale(0.98)',
|
||||
'transition': 'all 0.3s ease'
|
||||
});
|
||||
|
||||
h1Title.css({
|
||||
'transform': 'translateY(20px)',
|
||||
'opacity': '0',
|
||||
'transition': 'all 0.3s ease'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
formContainer.html(`
|
||||
<div class="text-center animate">
|
||||
<div class="flex items-center justify-center w-20 h-20 mx-auto mb-4 bg-green-500 rounded-full">
|
||||
<i class="fas fa-check text-4xl text-white"></i>
|
||||
</div>
|
||||
<h2 class="text-2xl font-semibold mb-3">Accès autorisé !</h2>
|
||||
<p class="text-gray-500 dark:text-gray-400 mb-4">Redirection vers le fichier...</p>
|
||||
<div class="w-full h-1 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
||||
<div class="h-full bg-green-500 animate-loading-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
formContainer.css({
|
||||
'opacity': '1',
|
||||
'transform': 'translateY(0) scale(1)'
|
||||
});
|
||||
|
||||
h1Title.text('Succès !');
|
||||
h1Title.css({
|
||||
'transform': 'translateY(0)',
|
||||
'opacity': '1'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.location = '/attachments/' + userId + '/' + filename;
|
||||
}, 1500);
|
||||
}, 300);
|
||||
}, 700);
|
||||
} else {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'error',
|
||||
title: 'Mot de passe incorrect',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true
|
||||
});
|
||||
submitButton.addClass('fail-animation');
|
||||
submitButton.html('<i class="fas fa-times mr-2"></i>Échec');
|
||||
|
||||
if ('vibrate' in navigator) {
|
||||
navigator.vibrate(100);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
submitButton.removeClass('fail-animation');
|
||||
submitButton.prop('disabled', false);
|
||||
submitButton.html('<i class="fa-solid fa-lock mr-2"></i>Soumettre');
|
||||
}, 1500);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'error',
|
||||
title: 'Erreur lors de la vérification du mot de passe',
|
||||
showConfirmButton: false,
|
||||
timer: 1800,
|
||||
toast: true
|
||||
});
|
||||
submitButton.addClass('fail-animation');
|
||||
submitButton.html('<i class="fas fa-times mr-2"></i>Erreur');
|
||||
|
||||
setTimeout(() => {
|
||||
submitButton.removeClass('fail-animation');
|
||||
submitButton.prop('disabled', false);
|
||||
submitButton.html('<i class="fa-solid fa-lock mr-2"></i>Soumettre');
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user