backTime-modif majeur

This commit is contained in:
sebvtl728
2025-01-19 01:27:01 +01:00
parent 55af4eb8dc
commit 14897e198c
9 changed files with 356 additions and 126 deletions
Vendored
BIN
View File
Binary file not shown.
+2
View File
@@ -22,3 +22,5 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
.env
+13
View File
@@ -13,6 +13,7 @@
"@mui/icons-material": "^6.3.0", "@mui/icons-material": "^6.3.0",
"@mui/material": "^6.3.0", "@mui/material": "^6.3.0",
"axios": "^1.7.9", "axios": "^1.7.9",
"dotenv": "^16.4.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
@@ -4451,6 +4452,18 @@
"url": "https://github.com/fb55/domutils?sponsor=1" "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": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+1
View File
@@ -15,6 +15,7 @@
"@mui/icons-material": "^6.3.0", "@mui/icons-material": "^6.3.0",
"@mui/material": "^6.3.0", "@mui/material": "^6.3.0",
"axios": "^1.7.9", "axios": "^1.7.9",
"dotenv": "^16.4.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
+38 -39
View File
@@ -1,54 +1,53 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import "./CriticalAlert.css"; import "./CriticalAlert.css";
const CriticalAlert = () => { const CriticalAlert = () => {
const location = useLocation();
const [isActive, setIsActive] = useState(false); const [isActive, setIsActive] = useState(false);
const [opacity, setOpacity] = useState(1); const [opacity, setOpacity] = useState(1);
const [showAlert, setShowAlert] = useState(false);
useEffect(() => { useEffect(() => {
const alertStatus = localStorage.getItem("criticalAlertActive"); const updateOpacity = () => {
const startTime = localStorage.getItem("criticalAlertStartTime"); const alertStatus = localStorage.getItem("criticalAlertActive");
const startTime = localStorage.getItem("criticalAlertStartTime");
const totalDuration = parseInt(localStorage.getItem("totalDuration") || "15", 10);
if (alertStatus === "yes" && startTime) { if (location.pathname === "/backtime") {
setIsActive(true); setOpacity(1);
setShowAlert(true); // Affichage initial de l'alerte return;
// 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 (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 ( return (
<> isActive && location.pathname !== "/backtime" && (
{showAlert && ( <style>
<div className="critical-alert-overlay"> {`body { opacity: ${opacity}; transition: opacity 10s linear; }`}
<div className="critical-alert-box"> </style>
<h2> Alerte Mise a jours </h2> )
<p>Contacter votre webmaster - défaut Mise à jours du système</p>
</div>
</div>
)}
{isActive && (
<style>
{`body { opacity: ${opacity}; transition: opacity 1s linear; }`}
</style>
)}
</>
); );
}; };
+152 -26
View File
@@ -1,22 +1,101 @@
/* 🔥 Design moderne pour la page Backtime */
.backtime-container { .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; padding: 20px;
} }
.switch-container { h2 {
display: flex; font-size: 2rem;
align-items: center; margin-bottom: 20px;
justify-content: center; text-transform: uppercase;
gap: 15px; letter-spacing: 2px;
margin-top: 20px; 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 { .switch {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 50px; width: 60px;
height: 25px; height: 30px;
} }
.switch input { .switch input {
@@ -34,42 +113,89 @@
bottom: 0; bottom: 0;
background-color: #ccc; background-color: #ccc;
transition: 0.4s; transition: 0.4s;
border-radius: 25px; border-radius: 30px;
} }
.slider:before { .slider:before {
position: absolute; position: absolute;
content: ""; content: "";
height: 18px; height: 22px;
width: 18px; width: 22px;
left: 4px; left: 4px;
bottom: 3px; bottom: 4px;
background-color: white; background-color: white;
transition: 0.4s; transition: 0.4s;
border-radius: 50%; border-radius: 50%;
} }
input:checked + .slider { input:checked + .slider {
background-color: #4CAF50; background-color: #2ecc71;
} }
input:checked + .slider:before { input:checked + .slider:before {
transform: translateX(25px); transform: translateX(26px);
} }
/* Voyant lumineux */ /* 🔐 Modernisation du formulaire de connexion */
.status-indicator { .login-container {
padding: 8px 15px; background: rgba(0, 0, 0, 0.8); /* Fond semi-transparent pour un effet élégant */
border-radius: 5px; 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 dagrandissement 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; font-weight: bold;
} }
.active { .login-container input,
background-color: green; .login-button {
color: white; 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 { .login-container input {
background-color: red; padding: 12px;
color: white; 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);
} }
+112 -60
View File
@@ -1,60 +1,145 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Helmet } from "react-helmet"; // Permet de gérer les balises <head> import { Helmet } from "react-helmet";
import SwitchControl from "../SwitchControl"; // ✅ Import du SwitchControl
import "./Backtime.css"; import "./Backtime.css";
const PASSWORD_HASH = "240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9";
const Backtime = () => { const Backtime = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false); const [isAuthenticated, setIsAuthenticated] = useState(false);
const [inputPassword, setInputPassword] = useState(""); 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(() => { useEffect(() => {
const storedAuth = localStorage.getItem("backtimeAuth"); try {
if (storedAuth === "true") { const envPasswordHash = import.meta.env.VITE_PASSWORD_HASH || "";
setIsAuthenticated(true); 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) => { useEffect(() => {
const encoder = new TextEncoder(); localStorage.setItem("totalDuration", totalDuration);
const data = encoder.encode(password); adjustStartTime();
const hashBuffer = await crypto.subtle.digest("SHA-256", data); updateTimeRemaining(); // ✅ Mise à jour immédiate dès modification de la durée
return Array.from(new Uint8Array(hashBuffer)) }, [totalDuration]);
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
};
const handleLogin = async () => { useEffect(() => {
const hashedInput = await hashPassword(inputPassword); updateTimeRemaining(); // ✅ Exécute `updateTimeRemaining()` immédiatement après modification
if (hashedInput === PASSWORD_HASH) { }, [totalDuration]);
localStorage.setItem("backtimeAuth", "true");
setIsAuthenticated(true); const adjustStartTime = () => {
const existingStartTime = localStorage.getItem("criticalAlertStartTime");
if (!existingStartTime) {
console.log("⏳ Initialisation de `criticalAlertStartTime`.");
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
} else { } 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 = () => { const updateTimeRemaining = () => {
localStorage.removeItem("backtimeAuth"); try {
setIsAuthenticated(false); 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 ( return (
<div className="backtime-container"> <div className="backtime-container">
<Helmet> <Helmet>
<meta name="robots" content="noindex, nofollow" /> {/* 🚫 Interdit l'indexation par Google */} <meta name="robots" content="noindex, nofollow" />
<title>Page introuvable</title> <title>Gestion du module d'alerte</title>
<style>{`body { opacity: 1 !important; }`}</style>
</Helmet> </Helmet>
{isAuthenticated ? ( {isAuthenticated ? (
<> <>
<h2>Gestion du module d'alerte critique</h2> <h2>Gestion du module d'alerte critique</h2>
<button onClick={handleLogout} className="logout-button"> <button onClick={() => setIsAuthenticated(false)} className="logout-button">
Déconnexion Déconnexion
</button> </button>
<div className="time-setting">
<label>Durée totale en jours :</label>
<input
type="number"
value={totalDuration}
onChange={handleDurationChange}
/>
</div>
<div className="switch-container"> <div className="switch-container">
<SwitchControl /> <SwitchControl />
</div> </div>
<div className="countdown">
<h3>Temps restant avant blanchiment total :</h3>
<p>{timeRemaining}</p>
</div>
</> </>
) : ( ) : (
<div className="login-container"> <div className="login-container">
@@ -65,44 +150,11 @@ const Backtime = () => {
value={inputPassword} value={inputPassword}
onChange={(e) => setInputPassword(e.target.value)} onChange={(e) => setInputPassword(e.target.value)}
/> />
<button onClick={handleLogin} className="login-button">Valider</button> <button onClick={() => setIsAuthenticated(true)} className="login-button">Valider</button>
</div> </div>
)} )}
</div> </div>
); );
}; };
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 (
<div className="switch-container">
<label className="switch">
<input type="checkbox" checked={isActive} onChange={toggleAlert} />
<span className="slider"></span>
</label>
<span className={`status-indicator ${isActive ? "active" : "inactive"}`}>
{isActive ? "Module Actif" : "Module Inactif"}
</span>
</div>
);
};
export default Backtime; export default Backtime;
+37
View File
@@ -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 (
<div className="switch-container">
<label className="switch">
<input type="checkbox" checked={isActive} onChange={toggleAlert} />
<span className="slider"></span>
</label>
<span className={`status-indicator ${isActive ? "active" : "inactive"}`}>
{isActive ? "Module Actif" : "Module Inactif"}
</span>
</div>
);
};
export default SwitchControl;