feat: Implémentation RCON, gestion historique et corrections Docker
- Fix protocole RCON (Int32LE, Map-based response handling) - Ajout historique des commandes RCON avec persistance - Correction chemins Docker (SERVER_DIR, RCON_HOST, volumes) - Fix récupération données joueurs (world/players) - Amélioration UX login/register - Nettoyage logs de debug
This commit is contained in:
@@ -50,6 +50,9 @@ function showLoginPage() {
|
||||
<input type="password" id="password" placeholder="">
|
||||
</div>
|
||||
<button class="btn-primary" onclick="handleLogin()">Se connecter</button>
|
||||
<div style="text-align: center; margin-top: 15px;">
|
||||
<small>Pas encore de compte? <a href="#" onclick="toggleToRegister(event)" style="color: var(--primary); text-decoration: underline; cursor: pointer;">Créer un compte</a></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="registerForm" style="display: none;">
|
||||
@@ -67,34 +70,21 @@ function showLoginPage() {
|
||||
<input type="text" id="mcUsername" placeholder="VotreNomMC">
|
||||
</div>
|
||||
<button class="btn-primary" onclick="handleRegister()">Créer le compte</button>
|
||||
<div style="text-align: center; margin-top: 15px;">
|
||||
<small>Vous avez déjà un compte? <a href="#" onclick="toggleToLogin(event)" style="color: var(--primary); text-decoration: underline; cursor: pointer;">Se connecter</a></small>
|
||||
</div>
|
||||
</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);
|
||||
// Par défaut, montrer le formulaire de connexion
|
||||
const regForm = document.getElementById('registerForm');
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
|
||||
if (regForm && loginForm) {
|
||||
regForm.style.display = 'none';
|
||||
loginForm.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +159,39 @@ async function handleRegister() {
|
||||
}
|
||||
}
|
||||
|
||||
// Basculer vers le formulaire de connexion
|
||||
function toggleToLogin(event) {
|
||||
event.preventDefault();
|
||||
const regForm = document.getElementById('registerForm');
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
|
||||
if (regForm && loginForm) {
|
||||
regForm.style.display = 'none';
|
||||
loginForm.style.display = 'block';
|
||||
document.getElementById('loginMessage').innerHTML = '';
|
||||
// Effacer les champs
|
||||
document.getElementById('username').value = '';
|
||||
document.getElementById('password').value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Basculer vers le formulaire d'enregistrement
|
||||
function toggleToRegister(event) {
|
||||
event.preventDefault();
|
||||
const regForm = document.getElementById('registerForm');
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
|
||||
if (regForm && loginForm) {
|
||||
loginForm.style.display = 'none';
|
||||
regForm.style.display = 'block';
|
||||
document.getElementById('loginMessage').innerHTML = '';
|
||||
// Effacer les champs
|
||||
document.getElementById('regUsername').value = '';
|
||||
document.getElementById('regPassword').value = '';
|
||||
document.getElementById('mcUsername').value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher le dashboard
|
||||
function showDashboard() {
|
||||
document.getElementById('app').innerHTML = `
|
||||
@@ -334,28 +357,82 @@ function getConsoleHTML() {
|
||||
<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>
|
||||
<h3 style="margin-top: 30px; margin-bottom: 15px;">📜 Historique des Commandes</h3>
|
||||
<div class="btn-group" style="margin-bottom: 15px;">
|
||||
<input type="text" id="historySearch" placeholder="Chercher dans l'historique..." style="padding: 8px 12px; border-radius: 4px; border: 1px solid #ddd; flex: 1;" onchange="loadConsoleData()">
|
||||
<button class="btn-secondary" onclick="loadConsoleData()">🔄 Rafraîchir</button>
|
||||
<button class="btn-danger" onclick="clearRconHistory()">🗑️ Vider</button>
|
||||
</div>
|
||||
<div id="historyContainer" style="max-height: 400px; overflow-y: auto; background: #f8f9fa; border-radius: 5px; padding: 15px; border: 1px solid #ddd;"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async function loadConsoleData() {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/rcon/history`, {
|
||||
const search = document.getElementById('historySearch')?.value || '';
|
||||
const url = new URL(`${API_URL}/rcon/history`);
|
||||
url.searchParams.set('limit', '50');
|
||||
if (search) url.searchParams.set('search', search);
|
||||
|
||||
const response = await fetch(url, {
|
||||
credentials: 'include'
|
||||
});
|
||||
const history = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = 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>';
|
||||
|
||||
if (data.history && data.history.length > 0) {
|
||||
container.innerHTML = data.history.map(h => {
|
||||
const timestamp = new Date(h.timestamp).toLocaleString('fr-FR');
|
||||
const statusIcon = h.success ? '✓' : '✗';
|
||||
const statusColor = h.success ? '#28a745' : '#dc3545';
|
||||
return `
|
||||
<div style="margin-bottom: 12px; padding: 10px; background: white; border-left: 3px solid ${statusColor}; border-radius: 3px; font-size: 13px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start;">
|
||||
<div style="flex: 1;">
|
||||
<strong style="color: var(--primary); word-break: break-all;">${h.command}</strong><br>
|
||||
<span style="color: #666; font-size: 11px;">${timestamp}</span>
|
||||
</div>
|
||||
<span style="color: ${statusColor}; font-weight: bold; margin-left: 10px;">${statusIcon}</span>
|
||||
</div>
|
||||
${h.response ? `<div style="color: #333; margin-top: 5px; padding-top: 5px; border-top: 1px solid #eee; font-size: 12px; max-height: 80px; overflow-y: auto; word-break: break-word;">${h.response}</div>` : ''}
|
||||
${h.error ? `<div style="color: #dc3545; margin-top: 5px; padding-top: 5px; border-top: 1px solid #eee; font-size: 12px;">Erreur: ${h.error}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
} else {
|
||||
container.innerHTML = '<p style="color: #666; text-align: center; padding: 20px;">Aucun historique</p>';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur historique:', e);
|
||||
const container = document.getElementById('historyContainer');
|
||||
container.innerHTML = `<p style="color: #dc3545;">Erreur lors du chargement: ${e.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function clearRconHistory() {
|
||||
if (!confirm('Êtes-vous sûr de vouloir supprimer tout l\'historique?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/rcon/history`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showMessage('Historique supprimé', 'success', 'dashboardMessage');
|
||||
loadConsoleData();
|
||||
} else {
|
||||
showMessage('Erreur lors de la suppression', 'error', 'dashboardMessage');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur suppression historique:', e);
|
||||
showMessage('Erreur serveur', 'error', 'dashboardMessage');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +443,7 @@ async function sendRconCommand() {
|
||||
if (!command) return;
|
||||
|
||||
const output = document.getElementById('consoleOutput');
|
||||
output.innerHTML += `<div>$ ${command}</div>`;
|
||||
output.innerHTML += `<div style="color: var(--primary); margin-bottom: 5px;">$ ${command}</div>`;
|
||||
commandInput.value = '';
|
||||
|
||||
try {
|
||||
@@ -380,17 +457,18 @@ async function sendRconCommand() {
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
output.innerHTML += `<div>${data.response}</div>`;
|
||||
showMessage('Commande exécutée', 'success', 'dashboardMessage');
|
||||
output.innerHTML += `<div style="color: #333; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee;">${data.response || '(pas de réponse)'}</div>`;
|
||||
showMessage('Commande exécutée ✓', 'success', 'dashboardMessage');
|
||||
loadConsoleData(); // Rafraîchir l'historique
|
||||
} else {
|
||||
output.innerHTML += `<div class="error">Erreur: ${data.error}</div>`;
|
||||
output.innerHTML += `<div style="color: #dc3545; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee;">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>`;
|
||||
output.innerHTML += `<div style="color: #dc3545; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee;">Erreur serveur: ${e.message}</div>`;
|
||||
showMessage('Erreur serveur', 'error', 'dashboardMessage');
|
||||
}
|
||||
}
|
||||
@@ -488,10 +566,15 @@ async function loadPlayersData() {
|
||||
const response = await fetch(`${API_URL}/players`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const table = document.getElementById('playersTable');
|
||||
if (data.players.length > 0) {
|
||||
if (data.players && data.players.length > 0) {
|
||||
table.innerHTML = data.players.map(p => `
|
||||
<tr>
|
||||
<td>${p.name}</td>
|
||||
@@ -504,7 +587,7 @@ async function loadPlayersData() {
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur joueurs:', e);
|
||||
document.getElementById('playersTable').innerHTML = '<tr><td colspan="3" style="text-align: center; color: red;">Erreur</td></tr>';
|
||||
document.getElementById('playersTable').innerHTML = `<tr><td colspan="3" style="text-align: center; color: red;">Erreur: ${e.message}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user