3 Commits

Author SHA1 Message Date
Gilles Soulier 8d4dc0e853 fix(deploy): arrêt du service avant remplacement du binaire
Évite l'erreur "Fichier texte occupé" lors d'une mise à jour à chaud.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 20:32:17 +02:00
Gilles Soulier 311bdbc66d chore: version agent 0.1.2 2026-05-22 20:28:30 +02:00
Gilles Soulier 0df716b8b0 feat: version agent remontée au serveur et affichée dans la popup
- payload.rs : champ version (env!("CARGO_PKG_VERSION"))
- models.go  : Version dans AgentMetrics et Agent
- db.go      : colonne version dans agents + migration ALTER TABLE
- popups.js  : badge version dans la section INFORMATIONS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 20:27:26 +02:00
8 changed files with 30 additions and 11 deletions
+1 -1
View File
@@ -248,7 +248,7 @@ dependencies = [
[[package]] [[package]]
name = "nanometrics-agent" name = "nanometrics-agent"
version = "0.1.0" version = "0.1.2"
dependencies = [ dependencies = [
"libc", "libc",
"rumqttc", "rumqttc",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nanometrics-agent" name = "nanometrics-agent"
version = "0.1.0" version = "0.1.2"
edition = "2021" edition = "2021"
[lib] [lib]
+1
View File
@@ -68,6 +68,7 @@ fn main() {
hostname: hostname.clone(), hostname: hostname.clone(),
ip: ip.clone(), ip: ip.clone(),
status: "online".to_string(), status: "online".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
..Default::default() ..Default::default()
}; };
+1
View File
@@ -5,6 +5,7 @@ pub struct AgentMetrics {
pub hostname: String, pub hostname: String,
pub ip: String, pub ip: String,
pub status: String, pub status: String,
pub version: String,
pub cpu_percent: Option<f32>, pub cpu_percent: Option<f32>,
pub memory_used: Option<u64>, pub memory_used: Option<u64>,
pub memory_free: Option<u64>, pub memory_free: Option<u64>,
+3
View File
@@ -119,6 +119,9 @@ const Popups = (() => {
<div class="meta-grid"> <div class="meta-grid">
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">${esc(agent.hostname)}</div></div> <div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">${esc(agent.hostname)}</div></div>
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">${esc(agent.ip) || '—'}</div></div> <div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">${esc(agent.ip) || '—'}</div></div>
<div class="meta"><div class="meta-lbl">VERSION AGENT</div><div class="meta-val" style="display:flex;align-items:center;gap:6px">
${agent.version ? `<span style="font-family:var(--font-mono);background:var(--bg-1);border:1px solid var(--border-2);border-radius:5px;padding:1px 7px;font-size:11px;color:var(--accent)">v${esc(agent.version)}</span>` : '<span style="color:var(--ink-4)">—</span>'}
</div></div>
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div style="display:flex;gap:5px;margin-top:4px">${protos || '—'}</div></div> <div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div style="display:flex;gap:5px;margin-top:4px">${protos || '—'}</div></div>
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">${new Date(agent.last_seen * 1000).toLocaleTimeString('fr-FR')}</div></div> <div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">${new Date(agent.last_seen * 1000).toLocaleTimeString('fr-FR')}</div></div>
</div> </div>
+7
View File
@@ -92,6 +92,13 @@ ok "Serveur : $SERVER_IP:$SERVER_PORT | MQTT broker : $MQTT_HOST"
# ── 5. Installer le binaire ──────────────────────────────────────────────────── # ── 5. Installer le binaire ────────────────────────────────────────────────────
echo "" echo ""
echo "[1/5] Installation du binaire dans /usr/local/bin/" echo "[1/5] Installation du binaire dans /usr/local/bin/"
# Arrêter le service si en cours (le binaire ne peut pas être écrasé à chaud)
if systemctl is-active --quiet nanometrics-agent 2>/dev/null; then
warn "Service en cours — arrêt temporaire..."
systemctl stop nanometrics-agent
fi
cp "$TMP_BIN" "$INSTALL_BIN" cp "$TMP_BIN" "$INSTALL_BIN"
chmod 755 "$INSTALL_BIN" chmod 755 "$INSTALL_BIN"
ok "Binaire installé" ok "Binaire installé"
+14 -9
View File
@@ -20,7 +20,7 @@ const schema = `
CREATE TABLE IF NOT EXISTS agents ( CREATE TABLE IF NOT EXISTS agents (
id TEXT PRIMARY KEY, hostname TEXT NOT NULL, id TEXT PRIMARY KEY, hostname TEXT NOT NULL,
ip TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'offline', ip TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'offline',
last_seen INTEGER NOT NULL DEFAULT 0 last_seen INTEGER NOT NULL DEFAULT 0, version TEXT NOT NULL DEFAULT ''
); );
CREATE TABLE IF NOT EXISTS metrics ( CREATE TABLE IF NOT EXISTS metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id TEXT NOT NULL, ts INTEGER NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id TEXT NOT NULL, ts INTEGER NOT NULL,
@@ -57,8 +57,12 @@ func Open(path string) (*DB, error) {
} }
func (d *DB) migrate() error { func (d *DB) migrate() error {
_, err := d.conn.Exec(schema) if _, err := d.conn.Exec(schema); err != nil {
return err return err
}
// Migrations additives pour les colonnes ajoutées après la création initiale
_, _ = d.conn.Exec(`ALTER TABLE agents ADD COLUMN version TEXT NOT NULL DEFAULT ''`)
return nil
} }
func (d *DB) Close() { _ = d.conn.Close() } func (d *DB) Close() { _ = d.conn.Close() }
@@ -66,11 +70,12 @@ func (d *DB) Close() { _ = d.conn.Close() }
func (d *DB) UpsertAgent(m *models.AgentMetrics) error { func (d *DB) UpsertAgent(m *models.AgentMetrics) error {
ts := time.Now().Unix() ts := time.Now().Unix()
_, err := d.conn.Exec(` _, err := d.conn.Exec(`
INSERT INTO agents (id, hostname, ip, status, last_seen) INSERT INTO agents (id, hostname, ip, status, last_seen, version)
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET ON CONFLICT(id) DO UPDATE SET
ip=excluded.ip, status=excluded.status, last_seen=excluded.last_seen`, ip=excluded.ip, status=excluded.status, last_seen=excluded.last_seen,
m.Hostname, m.Hostname, m.IP, m.Status, ts) version=CASE WHEN excluded.version != '' THEN excluded.version ELSE version END`,
m.Hostname, m.Hostname, m.IP, m.Status, ts, m.Version)
return err return err
} }
@@ -104,7 +109,7 @@ func (d *DB) InsertMetrics(m *models.AgentMetrics) error {
} }
func (d *DB) GetAgents() ([]models.Agent, error) { func (d *DB) GetAgents() ([]models.Agent, error) {
rows, err := d.conn.Query(`SELECT id, hostname, ip, status, last_seen FROM agents`) rows, err := d.conn.Query(`SELECT id, hostname, ip, status, last_seen, version FROM agents`)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -112,7 +117,7 @@ func (d *DB) GetAgents() ([]models.Agent, error) {
var agents []models.Agent var agents []models.Agent
for rows.Next() { for rows.Next() {
var a models.Agent var a models.Agent
if err := rows.Scan(&a.ID, &a.Hostname, &a.IP, &a.Status, &a.LastSeen); err != nil { if err := rows.Scan(&a.ID, &a.Hostname, &a.IP, &a.Status, &a.LastSeen, &a.Version); err != nil {
return nil, err return nil, err
} }
agents = append(agents, a) agents = append(agents, a)
+2
View File
@@ -4,6 +4,7 @@ type AgentMetrics struct {
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
IP string `json:"ip"` IP string `json:"ip"`
Status string `json:"status"` Status string `json:"status"`
Version string `json:"version"`
CPUPercent *float64 `json:"cpu_percent"` CPUPercent *float64 `json:"cpu_percent"`
MemoryUsed *int64 `json:"memory_used"` MemoryUsed *int64 `json:"memory_used"`
MemoryFree *int64 `json:"memory_free"` MemoryFree *int64 `json:"memory_free"`
@@ -32,6 +33,7 @@ type Agent struct {
IP string `json:"ip"` IP string `json:"ip"`
Status string `json:"status"` Status string `json:"status"`
LastSeen int64 `json:"last_seen"` LastSeen int64 `json:"last_seen"`
Version string `json:"version,omitempty"`
LastMetrics *AgentMetrics `json:"last_metrics,omitempty"` LastMetrics *AgentMetrics `json:"last_metrics,omitempty"`
} }