use std::sync::Arc; use serde_json::json; use tokio::{sync::RwLock, time}; use tracing::{error, info}; use tracing_subscriber::EnvFilter; mod api; mod backend; mod collectors; mod config; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env().add_directive("info".parse()?)) .init(); let config_path = std::env::args().nth(1).unwrap_or_else(|| "config.yaml".into()); let cfg = config::Config::load(&config_path)?; info!("Agent ID : {} | hostname : {}", cfg.agent.id, cfg.agent.hostname); let client = backend::BackendClient::new(&cfg); // Enregistrement initial if let Err(e) = client.register(&cfg).await { error!("Enregistrement backend : {e}"); } // Collecte hardware au démarrage let hardware = collectors::static_info::collect(); info!("Hardware : {} — {} cœurs — {} Mo RAM", hardware.cpu_model, hardware.cpu_cores, hardware.ram_total_mb); if let Err(e) = client.push_static(&cfg.agent.id, &hardware).await { error!("Push hardware : {e}"); } // Événement boot if let Err(e) = backend::push_event(&client, &cfg.agent.id, "boot", json!({ "hostname": cfg.agent.hostname })).await { error!("Push événement boot : {e}"); } // État partagé avec l'API locale let shared = Arc::new(RwLock::new(api::AgentState { hardware: Some(hardware), ..Default::default() })); // API locale (tâche de fond) let api_state = shared.clone(); let api_listen = cfg.api.listen.clone(); tokio::spawn(async move { api::serve(api_listen, api_state).await }); // Collecteur temps réel let mut rt_collector = collectors::realtime::RealtimeCollector::new(); let realtime_interval = time::Duration::from_millis(cfg.intervals.realtime_ms); let medium_interval = time::Duration::from_secs(cfg.intervals.medium_s); // Première collecte medium au démarrage let medium = collectors::medium::collect(); shared.write().await.medium = medium.clone(); if let Err(e) = client.push_medium(&cfg.agent.id, &medium).await { error!("Push medium initial : {e}"); } // Minuteries let mut rt_ticker = time::interval(realtime_interval); let mut med_ticker = time::interval(medium_interval); med_ticker.tick().await; // consomme le premier tick immédiat loop { tokio::select! { _ = rt_ticker.tick() => { let metrics = rt_collector.collect(cfg.intervals.realtime_ms); shared.write().await.realtime = metrics.clone(); if let Err(e) = client.push_realtime(&cfg.agent.id, &metrics).await { error!("Push temps réel : {e}"); } } _ = med_ticker.tick() => { info!("Collecte medium (disques, températures)…"); let metrics = collectors::medium::collect(); shared.write().await.medium = metrics.clone(); if let Err(e) = client.push_medium(&cfg.agent.id, &metrics).await { error!("Push medium : {e}"); } } } } }