Admin: onglet Infos systeme (reseau, RAM, disque, temp, camera)

- Endpoint /api/systeme/info : IPs par interface, hostname, uptime,
  disque, RAM, temperature CPU, version Python, statut camera
- Onglet Infos dans le backoffice avec affichage visuel (barres, couleurs)
- Bouton Actualiser pour rafraichir les donnees en temps reel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 20:55:34 +02:00
parent cf41f66367
commit deca890b34
4 changed files with 266 additions and 0 deletions

View File

@@ -780,6 +780,82 @@ html, body {
display: flex;
}
/* Infos systeme */
.infos-systeme-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.infos-systeme-contenu {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.infos-section {
background: rgba(255,255,255,0.05);
border-radius: 0.8rem;
padding: 1rem 1.2rem;
}
.infos-section h4 {
margin: 0 0 0.8rem 0;
font-size: 1rem;
opacity: 0.7;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.infos-ligne {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.35rem 0;
border-bottom: 1px solid rgba(255,255,255,0.07);
font-size: 1.05rem;
}
.infos-ligne:last-child {
border-bottom: none;
}
.infos-ligne span {
opacity: 0.65;
}
.infos-ip {
font-family: monospace;
font-size: 1.1rem;
letter-spacing: 0.03em;
}
.infos-barre-fond {
margin-top: 0.6rem;
background: rgba(255,255,255,0.1);
border-radius: 4px;
height: 6px;
overflow: hidden;
}
.infos-barre {
height: 100%;
border-radius: 4px;
transition: width 0.4s ease;
}
.infos-chargement, .infos-erreur {
opacity: 0.5;
text-align: center;
padding: 2rem;
}
.btn-petit {
font-size: 0.9rem;
padding: 0.4rem 1rem;
}
/* Toggles */
.toggle-groupe {
display: flex;

View File

@@ -331,6 +331,7 @@
<button class="onglet" data-onglet="personnalisation">Personnalisation</button>
<button class="onglet" data-onglet="evenement">Evenement</button>
<button class="onglet" data-onglet="fonctions">Fonctions</button>
<button class="onglet" data-onglet="infos" onclick="chargerInfosSysteme()">Infos</button>
</div>
<!-- Panneau Materiel (Camera + Imprimante) -->
@@ -567,6 +568,17 @@
<button class="btn-danger" onclick="confirmerRedemarrage()">Redemarrer le systeme</button>
<button class="btn-danger" onclick="confirmerExtinction()">Eteindre le systeme</button>
</div>
<!-- Panneau Infos systeme -->
<div class="admin-panneau" id="panneau-infos">
<div class="infos-systeme-header">
<h3>Informations systeme</h3>
<button class="btn-action btn-petit" onclick="chargerInfosSysteme()">&#8635; Actualiser</button>
</div>
<div id="infos-systeme-contenu" class="infos-systeme-contenu">
<p class="infos-chargement">Chargement...</p>
</div>
</div>
</div>
</section>

View File

@@ -685,6 +685,81 @@ async function sauvegarderBooth() {
chargerBoothInfo();
}
// === INFOS SYSTEME ===
async function chargerInfosSysteme() {
const contenu = document.getElementById('infos-systeme-contenu');
contenu.innerHTML = '<p class="infos-chargement">Chargement...</p>';
try {
const info = await apiGet('/api/systeme/info');
let html = '';
// Reseau
html += '<div class="infos-section">';
html += '<h4>&#127760; Reseau</h4>';
html += `<div class="infos-ligne"><span>Hostname</span><strong>${info.hostname}</strong></div>`;
if (info.interfaces && info.interfaces.length > 0) {
info.interfaces.forEach(iface => {
iface.ips.forEach(ip => {
html += `<div class="infos-ligne"><span>${iface.nom}</span><strong class="infos-ip">${ip}</strong></div>`;
});
});
} else {
html += '<div class="infos-ligne"><span>Aucune interface</span><strong>—</strong></div>';
}
html += '</div>';
// Systeme
html += '<div class="infos-section">';
html += '<h4>&#128187; Systeme</h4>';
if (info.uptime) html += `<div class="infos-ligne"><span>Uptime</span><strong>${info.uptime}</strong></div>`;
if (info.temperature_cpu !== null && info.temperature_cpu !== undefined) {
const couleur = info.temperature_cpu > 70 ? '#e53935' : info.temperature_cpu > 55 ? '#fb8c00' : '#43a047';
html += `<div class="infos-ligne"><span>Temperature CPU</span><strong style="color:${couleur}">${info.temperature_cpu} °C</strong></div>`;
}
if (info.python) html += `<div class="infos-ligne"><span>Python</span><strong>${info.python}</strong></div>`;
html += '</div>';
// Stockage
if (info.disque && info.disque.total) {
const pct = Math.round((info.disque.utilise / info.disque.total) * 100);
const couleurDisk = pct > 85 ? '#e53935' : pct > 65 ? '#fb8c00' : '#43a047';
html += '<div class="infos-section">';
html += '<h4>&#128190; Stockage</h4>';
html += `<div class="infos-ligne"><span>Total</span><strong>${info.disque.total} Go</strong></div>`;
html += `<div class="infos-ligne"><span>Utilise</span><strong style="color:${couleurDisk}">${info.disque.utilise} Go (${pct}%)</strong></div>`;
html += `<div class="infos-ligne"><span>Libre</span><strong>${info.disque.libre} Go</strong></div>`;
html += '<div class="infos-barre-fond"><div class="infos-barre" style="width:' + pct + '%;background:' + couleurDisk + '"></div></div>';
html += '</div>';
}
// RAM
if (info.ram && info.ram.total) {
const pctRam = Math.round((info.ram.utilise / info.ram.total) * 100);
const couleurRam = pctRam > 85 ? '#e53935' : pctRam > 65 ? '#fb8c00' : '#43a047';
html += '<div class="infos-section">';
html += '<h4>&#129728; Memoire RAM</h4>';
html += `<div class="infos-ligne"><span>Total</span><strong>${info.ram.total} Go</strong></div>`;
html += `<div class="infos-ligne"><span>Utilise</span><strong style="color:${couleurRam}">${info.ram.utilise} Go (${pctRam}%)</strong></div>`;
html += `<div class="infos-ligne"><span>Libre</span><strong>${info.ram.libre} Go</strong></div>`;
html += '<div class="infos-barre-fond"><div class="infos-barre" style="width:' + pctRam + '%;background:' + couleurRam + '"></div></div>';
html += '</div>';
}
// Camera
html += '<div class="infos-section">';
html += '<h4>&#128247; Camera</h4>';
const camOk = info.camera_connectee;
html += `<div class="infos-ligne"><span>Mode</span><strong>${info.camera_mode}</strong></div>`;
html += `<div class="infos-ligne"><span>Statut</span><strong style="color:${camOk ? '#43a047' : '#e53935'}">${camOk ? 'Connectee' : 'Deconnectee'}</strong></div>`;
html += '</div>';
contenu.innerHTML = html;
} catch (e) {
contenu.innerHTML = '<p class="infos-erreur">Impossible de charger les informations systeme.</p>';
}
}
async function regenererBoothPassword() {
const booth = config.booth || {};
const url = getValue('admin-booth-url').replace(/\/$/, '');