From a2060a17139d3dd18d3ef0d785578e01edb8fd12 Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Fri, 22 May 2026 22:46:52 +0200 Subject: [PATCH] feat(v0.1.4): SMART tile icon, IP locale robuste, copier HTTP, nettoyage UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agent: détection IP via server_ip en priorité (fallback 8.8.8.8) — résout 0.0.0.0 sur LAN sans internet - Agent: détection auto des disques /sys/block (sd*, nvme*) + fix continue dans la boucle smartctl - Agent: SupplementaryGroups=disk dans le service systemd pour accès smartctl - Dashboard: icône SMART (shield-check/triangle-exclamation) dans la ligne disque de la tuile - Dashboard: bouton Copier compatible HTTP (fallback execCommand si clipboard API indisponible) - Dashboard: suppression du texte redondant dans la section INSTALLATION AGENT Co-Authored-By: Claude Sonnet 4.6 --- agent/Cargo.lock | 2 +- agent/Cargo.toml | 2 +- agent/src/main.rs | 18 ++++++++++++------ agent/src/metrics/smart.rs | 18 ++++++++++++++---- dashboard/js/grid.js | 8 +++++++- dashboard/js/popups.js | 26 +++++++++++++++++++++++--- deploy/nanometrics-agent.service | 1 + 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 28cec37..8f666ea 100644 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -248,7 +248,7 @@ dependencies = [ [[package]] name = "nanometrics-agent" -version = "0.1.2" +version = "0.1.4" dependencies = [ "libc", "rumqttc", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 80729fa..03f8da7 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nanometrics-agent" -version = "0.1.3" +version = "0.1.4" edition = "2021" [lib] diff --git a/agent/src/main.rs b/agent/src/main.rs index e816da7..c6218db 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -10,12 +10,18 @@ extern "C" fn handle_signal(_: libc::c_int) { RUNNING.store(false, Ordering::Relaxed); } -fn get_local_ip() -> String { +fn get_local_ip(server_ip: &str) -> String { use std::net::UdpSocket; - if let Ok(s) = UdpSocket::bind("0.0.0.0:0") { - if s.connect("8.8.8.8:80").is_ok() { - if let Ok(addr) = s.local_addr() { - return addr.ip().to_string(); + // Try server IP first (always reachable), then internet fallback + for target in &[format!("{}:80", server_ip), "8.8.8.8:80".to_string()] { + if let Ok(s) = UdpSocket::bind("0.0.0.0:0") { + if s.connect(target.as_str()).is_ok() { + if let Ok(addr) = s.local_addr() { + let ip = addr.ip().to_string(); + if ip != "0.0.0.0" { + return ip; + } + } } } } @@ -37,7 +43,7 @@ fn main() { .expect("Impossible de charger config.toml"); let hostname = System::host_name().unwrap_or_else(|| "unknown".to_string()); - let ip = get_local_ip(); + let ip = get_local_ip(&cfg.server.ip); let mut sys = System::new(); let mut networks = Networks::new_with_refreshed_list(); diff --git a/agent/src/metrics/smart.rs b/agent/src/metrics/smart.rs index e6e7fad..a29420e 100644 --- a/agent/src/metrics/smart.rs +++ b/agent/src/metrics/smart.rs @@ -84,11 +84,21 @@ pub fn collect() -> Option { if !is_available() { return None; } - for dev in &["/dev/sda", "/dev/nvme0"] { - let output = std::process::Command::new("smartctl") + // Détecter les disques réels depuis /sys/block (sd*, nvme*) + let mut devs: Vec = std::fs::read_dir("/sys/block") + .into_iter() + .flatten() + .flatten() + .map(|e| e.file_name().into_string().unwrap_or_default()) + .filter(|n| n.starts_with("sd") || n.starts_with("nvme")) + .map(|n| format!("/dev/{}", n)) + .collect(); + devs.sort(); + + for dev in &devs { + let Ok(output) = std::process::Command::new("smartctl") .args(["-j", dev]) - .output() - .ok()?; + .output() else { continue }; let json = String::from_utf8_lossy(&output.stdout); if let Ok(metrics) = parse_json(&json) { return Some(metrics); diff --git a/dashboard/js/grid.js b/dashboard/js/grid.js index 1ff7ba6..7ccb64b 100644 --- a/dashboard/js/grid.js +++ b/dashboard/js/grid.js @@ -55,6 +55,12 @@ const Grid = (() => { uptimeStr = d > 0 ? `${d}j ${h}h` : `${h}h`; } + const smartIco = !offline && metrics?.smart != null + ? (metrics.smart.passed + ? `` + : ``) + : ''; + const iconContent = ` @@ -87,7 +93,7 @@ const Grid = (() => {
- ${offline ? '—' : (metrics?.hdd_used && metrics?.hdd_total ? fmt(metrics.hdd_used) + '/' + fmt(metrics.hdd_total) : '—')} + ${offline ? '—' : (metrics?.hdd_used && metrics?.hdd_total ? fmt(metrics.hdd_used) + '/' + fmt(metrics.hdd_total) : '—')}${smartIco}
diff --git a/dashboard/js/popups.js b/dashboard/js/popups.js index 1b5f42f..51d86f8 100644 --- a/dashboard/js/popups.js +++ b/dashboard/js/popups.js @@ -331,7 +331,6 @@ const Popups = (() => {
INSTALLATION AGENT
-
+ onclick="Popups._copyInstallCmd(this)">Copier
`; @@ -450,10 +449,31 @@ const Popups = (() => { } } + function _copyInstallCmd(btn) { + const text = document.getElementById('s-install-cmd').value; + const done = () => { btn.textContent = '✓ Copié'; setTimeout(() => btn.textContent = 'Copier', 1500); }; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(done).catch(() => _copyFallback(text, done)); + } else { + _copyFallback(text, done); + } + } + + function _copyFallback(text, cb) { + const ta = document.createElement('textarea'); + ta.value = text; + ta.style.cssText = 'position:fixed;opacity:0;top:0;left:0'; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + try { document.execCommand('copy'); cb(); } catch (_) {} + document.body.removeChild(ta); + } + return { showDetail, hideDetail, showAgentCfg, sendAgentConfig, toggleCbox, showSrvCfg, hideSrvCfg, saveSrvCfg, confirmDeleteAgent, doDeleteAgent, - showSmart, + showSmart, _copyInstallCmd, }; })(); diff --git a/deploy/nanometrics-agent.service b/deploy/nanometrics-agent.service index 454db56..f5f47da 100644 --- a/deploy/nanometrics-agent.service +++ b/deploy/nanometrics-agent.service @@ -10,6 +10,7 @@ Restart=on-failure RestartSec=5 DynamicUser=yes +SupplementaryGroups=disk ConfigurationDirectory=nanometrics ConfigurationDirectoryMode=0755