Files
WebNationsGlory_ServeurBuil…/frontend/public/js/app.js
y.campiontrebouta@innotexnas.ovh abb51904d7 initial commit
2026-02-04 19:04:46 +01:00

890 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Configuration API
const API_URL = 'http://localhost:3000/api';
// State
let currentUser = null;
let isAuthenticated = false;
// Initialisation
document.addEventListener('DOMContentLoaded', () => {
checkAuth();
});
// Vérifier l'authentification
async function checkAuth() {
try {
const response = await fetch(`${API_URL}/auth/check`, {
credentials: 'include'
});
const data = await response.json();
if (data.authenticated) {
currentUser = data.user;
isAuthenticated = true;
showDashboard();
} else {
showLoginPage();
}
} catch (e) {
console.error('Erreur vérification auth:', e);
showLoginPage();
}
}
// Afficher la page de connexion
function showLoginPage() {
document.getElementById('app').innerHTML = `
<div class="login-container">
<div class="login-box">
<h1>🎮 NationsGlory Admin</h1>
<div class="message" id="loginMessage"></div>
<div id="loginForm">
<h2 style="text-align: center; margin-bottom: 20px; color: var(--primary); font-size: 18px;">Connexion</h2>
<div class="form-group">
<label>Nom d'utilisateur</label>
<input type="text" id="username" placeholder="admin">
</div>
<div class="form-group">
<label>Mot de passe</label>
<input type="password" id="password" placeholder="">
</div>
<button class="btn-primary" onclick="handleLogin()">Se connecter</button>
</div>
<div id="registerForm" style="display: none;">
<h2 style="text-align: center; margin-bottom: 20px; color: var(--primary); font-size: 18px;">Créer le compte Admin</h2>
<div class="form-group">
<label>Nom d'utilisateur</label>
<input type="text" id="regUsername" placeholder="admin">
</div>
<div class="form-group">
<label>Mot de passe</label>
<input type="password" id="regPassword" placeholder="">
</div>
<div class="form-group">
<label>Pseudo Minecraft (doit être OP)</label>
<input type="text" id="mcUsername" placeholder="VotreNomMC">
</div>
<button class="btn-primary" onclick="handleRegister()">Créer le compte</button>
</div>
</div>
</div>
`;
// Vérifier s'il y a déjà un admin
checkIfAdminExists();
}
// Vérifier si un admin existe
async function checkIfAdminExists() {
try {
const response = await fetch(`${API_URL}/auth/check`, {
credentials: 'include'
});
// Si pas connecté, afficher le formulaire d'enregistrement si c'est la première fois
setTimeout(() => {
const regForm = document.getElementById('registerForm');
const loginForm = document.getElementById('loginForm');
if (regForm && loginForm) {
// Pour la première connexion, montrer le formulaire d'enregistrement
regForm.style.display = 'block';
loginForm.style.display = 'none';
}
}, 100);
} catch (e) {
console.error('Erreur:', e);
}
}
// Connexion
async function handleLogin() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
if (!username || !password) {
showMessage('Veuillez remplir tous les champs', 'error', 'loginMessage');
return;
}
try {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (response.ok) {
currentUser = data.user;
isAuthenticated = true;
showDashboard();
} else {
showMessage(data.error || 'Erreur de connexion', 'error', 'loginMessage');
}
} catch (e) {
console.error('Erreur connexion:', e);
showMessage('Erreur serveur', 'error', 'loginMessage');
}
}
// Enregistrement
async function handleRegister() {
const username = document.getElementById('regUsername').value;
const password = document.getElementById('regPassword').value;
const mcUsername = document.getElementById('mcUsername').value;
if (!username || !password || !mcUsername) {
showMessage('Veuillez remplir tous les champs', 'error', 'loginMessage');
return;
}
try {
const response = await fetch(`${API_URL}/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username, password, mcUsername })
});
const data = await response.json();
if (response.ok) {
showMessage('Compte créé! Connectez-vous maintenant.', 'success', 'loginMessage');
setTimeout(() => {
document.getElementById('registerForm').style.display = 'none';
document.getElementById('loginForm').style.display = 'block';
document.getElementById('username').value = username;
document.getElementById('password').value = '';
}, 1000);
} else {
showMessage(data.error || 'Erreur d\'enregistrement', 'error', 'loginMessage');
}
} catch (e) {
console.error('Erreur enregistrement:', e);
showMessage('Erreur serveur', 'error', 'loginMessage');
}
}
// Afficher le dashboard
function showDashboard() {
document.getElementById('app').innerHTML = `
<div class="container">
<div class="header">
<div>
<h1>🎮 NationsGlory Admin Panel</h1>
</div>
<div class="user-info">
<p>👤 <strong>${currentUser.username}</strong> (${currentUser.mcUsername})</p>
<button class="btn-danger" onclick="handleLogout()">Déconnexion</button>
</div>
</div>
<div class="message" id="dashboardMessage"></div>
<div class="nav-tabs">
<button class="active" onclick="switchTab('dashboard')">📊 Dashboard</button>
<button onclick="switchTab('console')">⌨️ Console RCON</button>
<button onclick="switchTab('logs')">📜 Logs</button>
<button onclick="switchTab('players')">👥 Joueurs</button>
<button onclick="switchTab('whitelist')">✅ Whitelist</button>
<button onclick="switchTab('backups')">💾 Backups</button>
<button onclick="switchTab('settings')">⚙️ Paramètres</button>
</div>
<!-- Dashboard -->
<div id="dashboard" class="content-section active">
${getDashboardHTML()}
</div>
<!-- Console RCON -->
<div id="console" class="content-section">
${getConsoleHTML()}
</div>
<!-- Logs -->
<div id="logs" class="content-section">
${getLogsHTML()}
</div>
<!-- Joueurs -->
<div id="players" class="content-section">
${getPlayersHTML()}
</div>
<!-- Whitelist -->
<div id="whitelist" class="content-section">
${getWhitelistHTML()}
</div>
<!-- Backups -->
<div id="backups" class="content-section">
${getBackupsHTML()}
</div>
<!-- Paramètres -->
<div id="settings" class="content-section">
${getSettingsHTML()}
</div>
</div>
`;
// Charger les données
loadDashboardData();
}
// Changer d'onglet
function switchTab(tabName) {
// Masquer tous les onglets
document.querySelectorAll('.content-section').forEach(el => {
el.classList.remove('active');
});
// Afficher l'onglet sélectionné
document.getElementById(tabName).classList.add('active');
// Mettre à jour les boutons
document.querySelectorAll('.nav-tabs button').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
// Charger les données spécifiques
if (tabName === 'console') loadConsoleData();
if (tabName === 'logs') loadLogsData();
if (tabName === 'players') loadPlayersData();
if (tabName === 'whitelist') loadWhitelistData();
if (tabName === 'backups') loadBackupsData();
if (tabName === 'settings') loadSettingsData();
}
// ========== DASHBOARD ==========
function getDashboardHTML() {
return `
<div class="section-card">
<h2>🎮 Contrôle du Serveur</h2>
<div class="btn-group">
<button class="btn-success" onclick="restartServer()">🔄 Redémarrer</button>
<button class="btn-primary" onclick="saveServer()">💾 Sauvegarder</button>
<button class="btn-warning" onclick="openChangeRconPasswordModal()">🔐 Changer RCON</button>
</div>
</div>
<div class="grid">
<div class="grid-card">
<h3>État Serveur</h3>
<p class="value"><span class="status online"></span>En ligne</p>
</div>
<div class="grid-card">
<h3>Joueurs Connectés</h3>
<p class="value" id="playerCount">--</p>
</div>
<div class="grid-card">
<h3>Version Forge</h3>
<p class="value" id="forgeVersion">1.6.4</p>
</div>
</div>
<div class="section-card">
<h2>📊 Informations Rapides</h2>
<table>
<tbody id="quickInfoTable">
<tr><td>Chargement...</td><td></td></tr>
</tbody>
</table>
</div>
`;
}
async function loadDashboardData() {
try {
const response = await fetch(`${API_URL}/server`, {
credentials: 'include'
});
const data = await response.json();
if (data.properties) {
const table = document.getElementById('quickInfoTable');
table.innerHTML = `
<tr><td><strong>Nom Serveur</strong></td><td>${data.properties['motd'] || 'NationsGlory'}</td></tr>
<tr><td><strong>Mode Jeu</strong></td><td>${data.properties['gamemode'] || 'Survival'}</td></tr>
<tr><td><strong>Difficulté</strong></td><td>${data.properties['difficulty'] || '2'}</td></tr>
<tr><td><strong>PvP</strong></td><td>${data.properties['pvp'] === 'true' ? 'Activé' : 'Désactivé'}</td></tr>
<tr><td><strong>Port</strong></td><td>${data.properties['server-port'] || '25565'}</td></tr>
<tr><td><strong>Max Joueurs</strong></td><td>${data.properties['max-players'] || '20'}</td></tr>
`;
}
} catch (e) {
console.error('Erreur chargement dashboard:', e);
}
}
// ========== CONSOLE RCON ==========
function getConsoleHTML() {
return `
<div class="section-card">
<h2>⌨️ Console RCON</h2>
<p style="margin-bottom: 15px; color: #666;">Exécutez des commandes directement sur le serveur</p>
<div class="console-output" id="consoleOutput"></div>
<div class="console-input">
<input type="text" id="commandInput" placeholder="Entrez une commande..." onkeypress="if(event.key==='Enter') sendRconCommand();">
<button class="btn-primary" onclick="sendRconCommand()">Envoyer</button>
</div>
<h3 style="margin-top: 30px; margin-bottom: 15px;">Historique</h3>
<div id="historyContainer" style="max-height: 300px; overflow-y: auto; background: #f8f9fa; border-radius: 5px; padding: 15px;"></div>
</div>
`;
}
async function loadConsoleData() {
try {
const response = await fetch(`${API_URL}/rcon/history`, {
credentials: 'include'
});
const history = await response.json();
const container = document.getElementById('historyContainer');
container.innerHTML = history.map(h => `
<div style="margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #ddd; font-size: 12px;">
<strong style="color: var(--primary);">${h.command}</strong><br>
<span style="color: #666;">${new Date(h.timestamp).toLocaleTimeString()}</span>
</div>
`).join('') || '<p style="color: #666;">Aucun historique</p>';
} catch (e) {
console.error('Erreur historique:', e);
}
}
async function sendRconCommand() {
const commandInput = document.getElementById('commandInput');
const command = commandInput.value.trim();
if (!command) return;
const output = document.getElementById('consoleOutput');
output.innerHTML += `<div>$ ${command}</div>`;
commandInput.value = '';
try {
const response = await fetch(`${API_URL}/rcon/command`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ command })
});
const data = await response.json();
if (response.ok) {
output.innerHTML += `<div>${data.response}</div>`;
showMessage('Commande exécutée', 'success', 'dashboardMessage');
} else {
output.innerHTML += `<div class="error">Erreur: ${data.error}</div>`;
showMessage(data.error, 'error', 'dashboardMessage');
}
output.scrollTop = output.scrollHeight;
} catch (e) {
console.error('Erreur envoi commande:', e);
output.innerHTML += `<div class="error">Erreur serveur</div>`;
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
// ========== LOGS ==========
function getLogsHTML() {
return `
<div class="section-card">
<h2>📜 Logs</h2>
<div class="btn-group">
<button class="btn-primary" onclick="reloadLogs()">🔄 Rafraîchir</button>
<button class="btn-secondary" onclick="openSearchLogsModal()">🔍 Chercher</button>
</div>
<div class="logs-container" id="logsContainer"></div>
</div>
`;
}
async function loadLogsData() {
try {
const response = await fetch(`${API_URL}/logs?lines=200`, {
credentials: 'include'
});
const data = await response.json();
const container = document.getElementById('logsContainer');
container.innerHTML = data.logs.map((line, idx) => {
let className = '';
if (line.includes('ERROR')) className = 'error';
else if (line.includes('WARN')) className = 'warning';
return `<div class="log-line ${className}">${escapeHtml(line)}</div>`;
}).join('');
container.scrollTop = container.scrollHeight;
} catch (e) {
console.error('Erreur chargement logs:', e);
document.getElementById('logsContainer').innerHTML = '<div class="error">Erreur chargement</div>';
}
}
async function reloadLogs() {
showMessage('Rechargement...', 'success', 'dashboardMessage');
await loadLogsData();
showMessage('Logs rechargés', 'success', 'dashboardMessage');
}
function openSearchLogsModal() {
const query = prompt('Chercher dans les logs:');
if (query) searchLogs(query);
}
async function searchLogs(query) {
try {
const response = await fetch(`${API_URL}/logs/search?query=${encodeURIComponent(query)}`, {
credentials: 'include'
});
const data = await response.json();
const container = document.getElementById('logsContainer');
container.innerHTML = data.results.map((r, idx) => `
<div class="log-line" style="background: ${idx % 2 === 0 ? '#f8f9fa' : 'white'}; padding: 5px;">
<strong style="color: var(--accent);">[${r.index}]</strong> ${escapeHtml(r.line)}
</div>
`).join('');
} catch (e) {
console.error('Erreur recherche:', e);
}
}
// ========== JOUEURS ==========
function getPlayersHTML() {
return `
<div class="section-card">
<h2>👥 Joueurs Connectés</h2>
<p style="margin-bottom: 15px; color: #666;">Liste des joueurs qui se sont connectés</p>
<table>
<thead>
<tr>
<th>Nom</th>
<th>UUID</th>
<th>Dernière Connexion</th>
</tr>
</thead>
<tbody id="playersTable">
<tr><td colspan="3" style="text-align: center;">Chargement...</td></tr>
</tbody>
</table>
</div>
`;
}
async function loadPlayersData() {
try {
const response = await fetch(`${API_URL}/players`, {
credentials: 'include'
});
const data = await response.json();
const table = document.getElementById('playersTable');
if (data.players.length > 0) {
table.innerHTML = data.players.map(p => `
<tr>
<td>${p.name}</td>
<td><code style="font-size: 11px;">${p.uuid}</code></td>
<td>${new Date(p.lastPlayed).toLocaleString()}</td>
</tr>
`).join('');
} else {
table.innerHTML = '<tr><td colspan="3" style="text-align: center;">Aucun joueur</td></tr>';
}
} catch (e) {
console.error('Erreur joueurs:', e);
document.getElementById('playersTable').innerHTML = '<tr><td colspan="3" style="text-align: center; color: red;">Erreur</td></tr>';
}
}
// ========== WHITELIST ==========
function getWhitelistHTML() {
return `
<div class="section-card">
<h2>✅ Whitelist</h2>
<div class="btn-group">
<button class="btn-primary" onclick="openAddWhitelistModal()"> Ajouter</button>
<button class="btn-secondary" onclick="reloadWhitelist()">🔄 Rafraîchir</button>
</div>
<table>
<thead>
<tr>
<th>Joueur</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="whitelistTable">
<tr><td colspan="2" style="text-align: center;">Chargement...</td></tr>
</tbody>
</table>
</div>
`;
}
async function loadWhitelistData() {
try {
const response = await fetch(`${API_URL}/whitelist`, {
credentials: 'include'
});
const data = await response.json();
const table = document.getElementById('whitelistTable');
if (data.whitelist && data.whitelist.length > 0) {
const players = data.whitelist.map(w => typeof w === 'string' ? w : w.name);
table.innerHTML = players.map(p => `
<tr>
<td>${p}</td>
<td>
<button class="btn-danger" style="padding: 5px 10px; font-size: 12px;" onclick="removeFromWhitelist('${p}')">❌ Retirer</button>
</td>
</tr>
`).join('');
} else {
table.innerHTML = '<tr><td colspan="2" style="text-align: center;">Whitelist vide</td></tr>';
}
} catch (e) {
console.error('Erreur whitelist:', e);
document.getElementById('whitelistTable').innerHTML = '<tr><td colspan="2" style="text-align: center; color: red;">Erreur</td></tr>';
}
}
function openAddWhitelistModal() {
const username = prompt('Nom du joueur à ajouter:');
if (username) addToWhitelist(username);
}
async function addToWhitelist(username) {
try {
const response = await fetch(`${API_URL}/whitelist/add`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username })
});
const data = await response.json();
if (response.ok) {
showMessage(`${username} ajouté à la whitelist`, 'success', 'dashboardMessage');
loadWhitelistData();
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
async function removeFromWhitelist(username) {
if (!confirm(`Confirmer la suppression de ${username}?`)) return;
try {
const response = await fetch(`${API_URL}/whitelist/remove`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username })
});
const data = await response.json();
if (response.ok) {
showMessage(`${username} retiré de la whitelist`, 'success', 'dashboardMessage');
loadWhitelistData();
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
async function reloadWhitelist() {
showMessage('Rechargement...', 'success', 'dashboardMessage');
await loadWhitelistData();
showMessage('Whitelist rechargée', 'success', 'dashboardMessage');
}
// ========== BACKUPS ==========
function getBackupsHTML() {
return `
<div class="section-card">
<h2>💾 Backups</h2>
<div class="btn-group">
<button class="btn-success" onclick="createBackup()"> Créer Backup</button>
<button class="btn-secondary" onclick="reloadBackups()">🔄 Rafraîchir</button>
</div>
<table>
<thead>
<tr>
<th>Nom</th>
<th>Taille</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="backupsTable">
<tr><td colspan="4" style="text-align: center;">Chargement...</td></tr>
</tbody>
</table>
</div>
`;
}
async function loadBackupsData() {
try {
const response = await fetch(`${API_URL}/backup`, {
credentials: 'include'
});
const data = await response.json();
const table = document.getElementById('backupsTable');
if (Array.isArray(data) && data.length > 0) {
table.innerHTML = data.map(b => `
<tr>
<td>${b.name}</td>
<td>${b.size}</td>
<td>${new Date(b.created).toLocaleString()}</td>
<td>
<button class="btn-danger" style="padding: 5px 10px; font-size: 12px;" onclick="deleteBackup('${b.name}')">❌ Supprimer</button>
</td>
</tr>
`).join('');
} else {
table.innerHTML = '<tr><td colspan="4" style="text-align: center;">Aucun backup</td></tr>';
}
} catch (e) {
console.error('Erreur backups:', e);
document.getElementById('backupsTable').innerHTML = '<tr><td colspan="4" style="text-align: center; color: red;">Erreur</td></tr>';
}
}
async function createBackup() {
if (!confirm('Créer un nouveau backup? (cela peut prendre du temps)')) return;
showMessage('Création du backup en cours...', 'success', 'dashboardMessage');
try {
const response = await fetch(`${API_URL}/backup/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({})
});
const data = await response.json();
if (response.ok) {
showMessage(data.message, 'success', 'dashboardMessage');
setTimeout(() => loadBackupsData(), 2000);
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
async function deleteBackup(filename) {
if (!confirm(`Confirmer la suppression du backup ${filename}?`)) return;
try {
const response = await fetch(`${API_URL}/backup/delete/${filename}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({})
});
const data = await response.json();
if (response.ok) {
showMessage('Backup supprimé', 'success', 'dashboardMessage');
loadBackupsData();
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
async function reloadBackups() {
showMessage('Rechargement...', 'success', 'dashboardMessage');
await loadBackupsData();
showMessage('Backups rechargés', 'success', 'dashboardMessage');
}
// ========== PARAMÈTRES ==========
function getSettingsHTML() {
return `
<div class="section-card">
<h2>⚙️ Paramètres du Serveur</h2>
<table id="settingsTable">
<thead>
<tr>
<th>Paramètre</th>
<th>Valeur</th>
</tr>
</thead>
<tbody>
<tr><td colspan="2" style="text-align: center;">Chargement...</td></tr>
</tbody>
</table>
</div>
`;
}
async function loadSettingsData() {
try {
const response = await fetch(`${API_URL}/server`, {
credentials: 'include'
});
const data = await response.json();
const table = document.getElementById('settingsTable');
const tbody = table.querySelector('tbody');
if (data.properties) {
const keys = Object.keys(data.properties).sort();
tbody.innerHTML = keys.map(k => `
<tr>
<td><strong>${k}</strong></td>
<td>${escapeHtml(data.properties[k])}</td>
</tr>
`).join('');
}
} catch (e) {
console.error('Erreur paramètres:', e);
}
}
// ========== ACTIONS ==========
async function restartServer() {
if (!confirm('⚠️ Redémarrer le serveur? Les joueurs seront déconnectés.')) return;
showMessage('Redémarrage du serveur...', 'success', 'dashboardMessage');
try {
const response = await fetch(`${API_URL}/rcon/restart`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({})
});
const data = await response.json();
if (response.ok) {
showMessage(data.message, 'success', 'dashboardMessage');
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
async function saveServer() {
showMessage('Sauvegarde du serveur...', 'success', 'dashboardMessage');
try {
const response = await fetch(`${API_URL}/rcon/save`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({})
});
const data = await response.json();
if (response.ok) {
showMessage(data.message, 'success', 'dashboardMessage');
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
function openChangeRconPasswordModal() {
const oldPassword = prompt('Ancien mot de passe RCON:');
if (!oldPassword) return;
const newPassword = prompt('Nouveau mot de passe RCON:');
if (!newPassword) return;
changeRconPassword(oldPassword, newPassword);
}
async function changeRconPassword(oldPassword, newPassword) {
try {
const response = await fetch(`${API_URL}/auth/change-rcon-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ oldPassword, newPassword })
});
const data = await response.json();
if (response.ok) {
showMessage(data.message, 'success', 'dashboardMessage');
} else {
showMessage(data.error, 'error', 'dashboardMessage');
}
} catch (e) {
console.error('Erreur:', e);
showMessage('Erreur serveur', 'error', 'dashboardMessage');
}
}
// ========== UTILITAIRES ==========
function showMessage(text, type, elementId) {
const element = document.getElementById(elementId);
if (element) {
element.textContent = text;
element.className = `message show ${type}`;
setTimeout(() => {
element.classList.remove('show');
}, 4000);
}
}
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// Déconnexion
async function handleLogout() {
try {
await fetch(`${API_URL}/auth/logout`, {
method: 'POST',
credentials: 'include'
});
checkAuth();
} catch (e) {
console.error('Erreur:', e);
}
}