feat(dashboard): métriques chargées immédiatement au rechargement de page

GET /api/agents inclut désormais last_metrics (dernière ligne de la table
metrics) pour chaque agent. grid.js l'utilise lors du refresh initial, ce
qui peuple les tuiles sans attendre le prochain message WebSocket.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gilles Soulier
2026-05-22 22:00:33 +02:00
parent f69c22039b
commit d8f395cb53
3 changed files with 51 additions and 1 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ const Grid = (() => {
function refresh(agents) {
agents.forEach(a => {
if (!_agents.has(a.id)) {
_agents.set(a.id, { agent: a, metrics: null });
_agents.set(a.id, { agent: a, metrics: a.last_metrics || null });
} else {
_agents.get(a.id).agent = a;
}
+47
View File
@@ -128,6 +128,53 @@ func (d *DB) GetAgents() ([]models.Agent, error) {
return agents, nil
}
func (d *DB) GetLastMetrics(agentID string) (*models.AgentMetrics, error) {
var cpu, temperature *float64
var memUsed, memFree, memTotal, hddUsed, hddFree, hddTotal *int64
var uptime, netRX, netTX *int64
var smartPassed, smartTemp, smartRealloc, smartHours, smartWear *int64
err := d.conn.QueryRow(`
SELECT cpu_percent, memory_used, memory_free, memory_total,
hdd_used, hdd_free, hdd_total,
uptime, network_rx, network_tx, temperature,
smart_passed, smart_temp, smart_realloc, smart_hours, smart_wear
FROM metrics WHERE agent_id = ? ORDER BY ts DESC LIMIT 1`, agentID).
Scan(&cpu, &memUsed, &memFree, &memTotal,
&hddUsed, &hddFree, &hddTotal,
&uptime, &netRX, &netTX, &temperature,
&smartPassed, &smartTemp, &smartRealloc, &smartHours, &smartWear)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
m := &models.AgentMetrics{
CPUPercent: cpu,
MemoryUsed: memUsed,
MemoryFree: memFree,
MemoryTotal: memTotal,
HDDUsed: hddUsed,
HDDFree: hddFree,
HDDTotal: hddTotal,
Uptime: uptime,
NetworkRX: netRX,
NetworkTX: netTX,
Temperature: temperature,
}
if smartPassed != nil {
m.Smart = &models.SmartMetrics{
Passed: *smartPassed == 1,
Temperature: smartTemp,
ReallocatedSectors: smartRealloc,
PowerOnHours: smartHours,
WearLevel: smartWear,
}
}
return m, nil
}
func (d *DB) GetMetricsHistory(agentID string, from, to int64) ([]map[string]interface{}, error) {
rows, err := d.conn.Query(`
SELECT ts, cpu_percent, memory_used, memory_total, hdd_used, hdd_total
+3
View File
@@ -15,6 +15,9 @@ func AgentsHandler(database *db.DB) http.HandlerFunc {
http.Error(w, err.Error(), 500)
return
}
for i := range agents {
agents[i].LastMetrics, _ = database.GetLastMetrics(agents[i].ID)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(agents)
}