diff --git a/.DS_Store b/.DS_Store
index 649816c..03ec99a 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/frontend/.gitignore b/frontend/.gitignore
index a547bf3..3b0b403 100644
--- a/frontend/.gitignore
+++ b/frontend/.gitignore
@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
+
+.env
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index add3140..65aa9e0 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -13,6 +13,7 @@
"@mui/icons-material": "^6.3.0",
"@mui/material": "^6.3.0",
"axios": "^1.7.9",
+ "dotenv": "^16.4.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
@@ -4451,6 +4452,18 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 8292390..44f8e0e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,6 +15,7 @@
"@mui/icons-material": "^6.3.0",
"@mui/material": "^6.3.0",
"axios": "^1.7.9",
+ "dotenv": "^16.4.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
diff --git a/frontend/src/components/CriticalAlert.jsx b/frontend/src/components/CriticalAlert.jsx
index 82586fe..f3aa7b6 100644
--- a/frontend/src/components/CriticalAlert.jsx
+++ b/frontend/src/components/CriticalAlert.jsx
@@ -1,54 +1,53 @@
import React, { useEffect, useState } from "react";
+import { useLocation } from "react-router-dom";
import "./CriticalAlert.css";
const CriticalAlert = () => {
+ const location = useLocation();
const [isActive, setIsActive] = useState(false);
const [opacity, setOpacity] = useState(1);
- const [showAlert, setShowAlert] = useState(false);
useEffect(() => {
- const alertStatus = localStorage.getItem("criticalAlertActive");
- const startTime = localStorage.getItem("criticalAlertStartTime");
+ const updateOpacity = () => {
+ const alertStatus = localStorage.getItem("criticalAlertActive");
+ const startTime = localStorage.getItem("criticalAlertStartTime");
+ const totalDuration = parseInt(localStorage.getItem("totalDuration") || "15", 10);
- if (alertStatus === "yes" && startTime) {
- setIsActive(true);
- setShowAlert(true); // Affichage initial de l'alerte
-
- // Disparition automatique de l'alerte après 2 secondes
- setTimeout(() => {
- setShowAlert(false);
- }, 1000);
-
- // Calcul du temps écoulé
- const startTimestamp = parseInt(startTime, 10);
- const now = Date.now();
- const elapsedDays = Math.floor((now - startTimestamp) / (1000 * 60 * 60 * 24));
-
- if (elapsedDays >= 15) {
- setOpacity(0); // Site totalement transparent après 15 jours
- } else {
- setOpacity(1 - elapsedDays / 15);
+ if (location.pathname === "/backtime") {
+ setOpacity(1);
+ return;
}
- }
- }, []);
+
+ if (alertStatus === "yes" && startTime) {
+ setIsActive(true);
+
+ const startTimestamp = parseInt(startTime, 10);
+ const now = Date.now();
+ const elapsedTime = now - startTimestamp;
+ const totalTime = totalDuration * 24 * 60 * 60 * 1000;
+ const remainingTime = totalTime - elapsedTime;
+
+ if (remainingTime <= 0) {
+ setOpacity(0);
+ } else {
+ const newOpacity = 1 - elapsedTime / totalTime;
+ setOpacity(newOpacity);
+ }
+ }
+ };
+
+ updateOpacity();
+ const interval = setInterval(updateOpacity, 10000);
+
+ return () => clearInterval(interval);
+ }, [location]);
return (
- <>
- {showAlert && (
-
-
-
⚠️ Alerte Mise a jours ⚠️
-
Contacter votre webmaster - défaut Mise à jours du système
-
-
- )}
-
- {isActive && (
-
- )}
- >
+ isActive && location.pathname !== "/backtime" && (
+
+ )
);
};
diff --git a/frontend/src/components/Pages/Backtime.css b/frontend/src/components/Pages/Backtime.css
index 83a2b54..0b3b355 100644
--- a/frontend/src/components/Pages/Backtime.css
+++ b/frontend/src/components/Pages/Backtime.css
@@ -1,22 +1,101 @@
+/* 🔥 Design moderne pour la page Backtime */
.backtime-container {
- text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ background: linear-gradient(135deg, #1e1e2f, #3b3b58);
+ color: white;
+ font-family: "Poppins", sans-serif;
padding: 20px;
}
-.switch-container {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 15px;
- margin-top: 20px;
+h2 {
+ font-size: 2rem;
+ margin-bottom: 20px;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: #f9f9f9;
}
-/* Style du switch */
+button {
+ padding: 10px 20px;
+ font-size: 1rem;
+ font-weight: bold;
+ text-transform: uppercase;
+ border: none;
+ cursor: pointer;
+ transition: all 0.3s ease-in-out;
+ border-radius: 5px;
+}
+
+.logout-button {
+ background: #e74c3c;
+ color: white;
+}
+
+.logout-button:hover {
+ background: #c0392b;
+}
+
+.countdown {
+ margin-top: 20px;
+ padding: 15px;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 10px;
+ box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.countdown h3 {
+ margin-bottom: 10px;
+ color: #f1c40f;
+}
+
+.time-setting {
+ margin-top: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.time-setting label {
+ font-size: 1.2rem;
+ margin-bottom: 5px;
+}
+
+.time-setting input {
+ width: 60px;
+ padding: 5px;
+ font-size: 1.2rem;
+ text-align: center;
+ border: none;
+ border-radius: 5px;
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+}
+
+.switch-container {
+ margin-top: 20px;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+}
+
+.switch-container span {
+ font-size: 1rem;
+ font-weight: bold;
+}
+
+/* 🎚️ Switch */
.switch {
position: relative;
display: inline-block;
- width: 50px;
- height: 25px;
+ width: 60px;
+ height: 30px;
}
.switch input {
@@ -34,42 +113,89 @@
bottom: 0;
background-color: #ccc;
transition: 0.4s;
- border-radius: 25px;
+ border-radius: 30px;
}
.slider:before {
position: absolute;
content: "";
- height: 18px;
- width: 18px;
+ height: 22px;
+ width: 22px;
left: 4px;
- bottom: 3px;
+ bottom: 4px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .slider {
- background-color: #4CAF50;
+ background-color: #2ecc71;
}
input:checked + .slider:before {
- transform: translateX(25px);
+ transform: translateX(26px);
}
-/* Voyant lumineux */
-.status-indicator {
- padding: 8px 15px;
- border-radius: 5px;
+/* 🔐 Modernisation du formulaire de connexion */
+.login-container {
+ background: rgba(0, 0, 0, 0.8); /* Fond semi-transparent pour un effet élégant */
+ padding: 30px;
+ border-radius: 12px;
+ text-align: center;
+ box-shadow: 0px 10px 25px rgba(0, 0, 0, 0.3); /* Ombre plus profonde pour effet moderne */
+ backdrop-filter: blur(10px); /* Effet flou pour un look premium */
+ width: 320px;
+ transition: all 0.3s ease-in-out;
+}
+
+.login-container:hover {
+ transform: scale(1.02); /* Effet léger d’agrandissement au survol */
+}
+
+.login-container h2 {
+ font-size: 1.8rem;
+ margin-bottom: 15px;
+ color: #f1c40f; /* Couleur dorée pour mettre en avant le titre */
font-weight: bold;
}
-.active {
- background-color: green;
- color: white;
+.login-container input,
+.login-button {
+ width: 100%; /* Assure que les deux éléments prennent la même largeur */
+ max-width: 280px; /* Définit une largeur max pour éviter qu'ils deviennent trop grands */
+ display: block; /* Évite les décalages */
+ margin: 0 auto; /* Centre les éléments */
}
-.inactive {
- background-color: red;
- color: white;
+.login-container input {
+ padding: 12px;
+ font-size: 1rem;
+ border: none;
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ text-align: center;
+ outline: none;
+}
+
+.login-container input::placeholder {
+ color: #ddd;
+ font-style: italic;
+}
+
+.login-button {
+ background: linear-gradient(135deg, #3498db, #2980b9);
+ color: white;
+ padding: 12px;
+ border-radius: 8px;
+ font-size: 1rem;
+ font-weight: bold;
+ cursor: pointer;
+ text-transform: uppercase;
+ margin-top: 10px; /* Ajoute un petit espace entre le champ et le bouton */
+}
+
+.login-button:hover {
+ background: linear-gradient(135deg, #2980b9, #1c6ea4);
+ transform: scale(1.05);
}
\ No newline at end of file
diff --git a/frontend/src/components/Pages/Backtime.jsx b/frontend/src/components/Pages/Backtime.jsx
index 69b9d05..d5db733 100644
--- a/frontend/src/components/Pages/Backtime.jsx
+++ b/frontend/src/components/Pages/Backtime.jsx
@@ -1,60 +1,145 @@
import React, { useState, useEffect } from "react";
-import { Helmet } from "react-helmet"; // Permet de gérer les balises
-import "./Backtime.css";
-
-const PASSWORD_HASH = "240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9";
+import { Helmet } from "react-helmet";
+import SwitchControl from "../SwitchControl"; // ✅ Import du SwitchControl
+import "./Backtime.css";
const Backtime = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [inputPassword, setInputPassword] = useState("");
+ const [timeRemaining, setTimeRemaining] = useState("Chargement...");
+ const [passwordHash, setPasswordHash] = useState("");
+ const [totalDuration, setTotalDuration] = useState(() => {
+ return parseInt(localStorage.getItem("totalDuration"), 10) || 15; // ✅ Utiliser la durée stockée
+ });
useEffect(() => {
- const storedAuth = localStorage.getItem("backtimeAuth");
- if (storedAuth === "true") {
- setIsAuthenticated(true);
+ try {
+ const envPasswordHash = import.meta.env.VITE_PASSWORD_HASH || "";
+ setPasswordHash(envPasswordHash);
+ console.log("✅ DEBUG : Mot de passe attendu depuis .env :", envPasswordHash);
+
+ const storedAuth = localStorage.getItem("backtimeAuth");
+ if (storedAuth === "true") {
+ setIsAuthenticated(true);
+ }
+
+ updateTimeRemaining(); // ✅ Mise à jour immédiate du chrono
+ const interval = setInterval(updateTimeRemaining, 1000);
+ return () => clearInterval(interval);
+ } catch (error) {
+ console.error("❌ Erreur dans useEffect :", error);
}
}, []);
- const hashPassword = async (password) => {
- const encoder = new TextEncoder();
- const data = encoder.encode(password);
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
- return Array.from(new Uint8Array(hashBuffer))
- .map((b) => b.toString(16).padStart(2, "0"))
- .join("");
- };
+ useEffect(() => {
+ localStorage.setItem("totalDuration", totalDuration);
+ adjustStartTime();
+ updateTimeRemaining(); // ✅ Mise à jour immédiate dès modification de la durée
+ }, [totalDuration]);
- const handleLogin = async () => {
- const hashedInput = await hashPassword(inputPassword);
- if (hashedInput === PASSWORD_HASH) {
- localStorage.setItem("backtimeAuth", "true");
- setIsAuthenticated(true);
+ useEffect(() => {
+ updateTimeRemaining(); // ✅ Exécute `updateTimeRemaining()` immédiatement après modification
+ }, [totalDuration]);
+
+ const adjustStartTime = () => {
+ const existingStartTime = localStorage.getItem("criticalAlertStartTime");
+
+ if (!existingStartTime) {
+ console.log("⏳ Initialisation de `criticalAlertStartTime`.");
+ localStorage.setItem("criticalAlertStartTime", Date.now().toString());
} else {
- alert("Mot de passe incorrect !");
+ const startTimestamp = parseInt(existingStartTime, 10);
+ const now = Date.now();
+ let elapsedTime = now - startTimestamp;
+
+ if (elapsedTime < 0) elapsedTime = 0;
+
+ const newTotalTime = totalDuration * 24 * 60 * 60 * 1000;
+ const adjustedStartTime = now - elapsedTime;
+
+ console.log(`🔄 Correction ` +
+ `Ancien Start Time = ${startTimestamp}, ` +
+ `Nouveau Start Time = ${adjustedStartTime}, ` +
+ `Temps Écoulé = ${elapsedTime}`);
+
+ localStorage.setItem("criticalAlertStartTime", adjustedStartTime.toString());
}
};
- const handleLogout = () => {
- localStorage.removeItem("backtimeAuth");
- setIsAuthenticated(false);
+ const updateTimeRemaining = () => {
+ try {
+ const startTime = localStorage.getItem("criticalAlertStartTime");
+
+ if (!startTime) {
+ setTimeRemaining("Aucune alerte activée");
+ return;
+ }
+
+ const startTimestamp = parseInt(startTime, 10);
+ const now = Date.now();
+ let elapsedTime = now - startTimestamp;
+
+ if (elapsedTime < 0) elapsedTime = 0;
+
+ const totalTime = totalDuration * 24 * 60 * 60 * 1000;
+ const remainingTime = totalTime - elapsedTime;
+
+ console.log(`🕒 DEBUG : Temps écoulé = ${elapsedTime}, Temps total = ${totalTime}, Temps restant = ${remainingTime}`);
+
+ if (remainingTime <= 0) {
+ setTimeRemaining("Le site est complètement blanc !");
+ } else {
+ const days = Math.floor(remainingTime / (1000 * 60 * 60 * 24));
+ const hours = Math.floor((remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60));
+ const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);
+ setTimeRemaining(`${days}j ${hours}h ${minutes}m ${seconds}s`);
+ }
+ } catch (error) {
+ console.error("❌ Erreur dans updateTimeRemaining :", error);
+ }
+ };
+
+ const handleDurationChange = (e) => {
+ const newDuration = parseInt(e.target.value, 10);
+ if (!isNaN(newDuration) && newDuration > 0) {
+ console.log("🔄 Mise à jour de la durée à :", newDuration);
+ setTotalDuration(newDuration);
+ }
};
return (
- {/* 🚫 Interdit l'indexation par Google */}
- Page introuvable
+
+ Gestion du module d'alerte
+
{isAuthenticated ? (
<>
Gestion du module d'alerte critique
-
);
};
-const SwitchControl = () => {
- const [isActive, setIsActive] = useState(false);
-
- useEffect(() => {
- const alertStatus = localStorage.getItem("criticalAlertActive");
- setIsActive(alertStatus === "yes");
- }, []);
-
- const toggleAlert = () => {
- if (!isActive) {
- localStorage.setItem("criticalAlertActive", "yes");
- localStorage.setItem("criticalAlertStartTime", Date.now().toString());
- } else {
- localStorage.removeItem("criticalAlertActive");
- localStorage.removeItem("criticalAlertStartTime");
- }
- setIsActive(!isActive);
- window.location.reload();
- };
-
- return (
-
-
-
- {isActive ? "Module Actif" : "Module Inactif"}
-
-
- );
-};
-
export default Backtime;
\ No newline at end of file
diff --git a/frontend/src/components/SwitchControl.css b/frontend/src/components/SwitchControl.css
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/components/SwitchControl.jsx b/frontend/src/components/SwitchControl.jsx
new file mode 100644
index 0000000..03e20b7
--- /dev/null
+++ b/frontend/src/components/SwitchControl.jsx
@@ -0,0 +1,37 @@
+import React, { useState, useEffect } from "react";
+import "./SwitchControl.css";
+
+const SwitchControl = () => {
+ const [isActive, setIsActive] = useState(false);
+
+ useEffect(() => {
+ const alertStatus = localStorage.getItem("criticalAlertActive");
+ setIsActive(alertStatus === "yes");
+ }, []);
+
+ const toggleAlert = () => {
+ if (!isActive) {
+ localStorage.setItem("criticalAlertActive", "yes");
+ localStorage.setItem("criticalAlertStartTime", Date.now().toString());
+ } else {
+ localStorage.removeItem("criticalAlertActive");
+ localStorage.removeItem("criticalAlertStartTime");
+ }
+ setIsActive(!isActive);
+ window.location.reload();
+ };
+
+ return (
+
+
+
+ {isActive ? "Module Actif" : "Module Inactif"}
+
+
+ );
+};
+
+export default SwitchControl;
\ No newline at end of file