Christoffer Martinsson 14aae90954 Fix storage display and improve UI formatting
- Fix duplicate storage pool issue by clearing cache on agent startup
- Change storage pool header text to normal color for better readability
- Improve services panel tree icons with proper └─ symbols for last items
- Ensure fresh metrics data on each agent restart
2025-10-22 23:02:16 +02:00

129 lines
3.9 KiB
Rust

use cm_dashboard_shared::{CacheConfig, Metric};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{info, warn, error};
/// Simple persistent cache for metrics
pub struct SimpleCache {
metrics: RwLock<HashMap<String, Metric>>,
persist_path: String,
}
impl SimpleCache {
pub fn new(config: CacheConfig) -> Self {
let cache = Self {
metrics: RwLock::new(HashMap::new()),
persist_path: config.persist_path,
};
// Clear cache file on startup to ensure fresh data
cache.clear_cache_file();
cache
}
/// Store metric in cache
pub async fn store_metric(&self, metric: Metric) {
let mut metrics = self.metrics.write().await;
metrics.insert(metric.name.clone(), metric);
}
/// Get all cached metrics
pub async fn get_all_cached_metrics(&self) -> Vec<Metric> {
let metrics = self.metrics.read().await;
metrics.values().cloned().collect()
}
/// Save cache to disk
pub async fn save_to_disk(&self) {
let metrics = self.metrics.read().await;
// Create directory if needed
if let Some(parent) = Path::new(&self.persist_path).parent() {
if let Err(e) = fs::create_dir_all(parent) {
warn!("Failed to create cache directory {}: {}", parent.display(), e);
return;
}
}
// Serialize and save
match serde_json::to_string_pretty(&*metrics) {
Ok(json) => {
if let Err(e) = fs::write(&self.persist_path, json) {
error!("Failed to save cache to {}: {}", self.persist_path, e);
}
}
Err(e) => {
error!("Failed to serialize cache: {}", e);
}
}
}
/// Load cache from disk
fn load_from_disk(&self) {
match fs::read_to_string(&self.persist_path) {
Ok(content) => {
match serde_json::from_str::<HashMap<String, Metric>>(&content) {
Ok(loaded_metrics) => {
if let Ok(mut metrics) = self.metrics.try_write() {
*metrics = loaded_metrics;
info!("Loaded {} metrics from cache", metrics.len());
}
}
Err(e) => {
warn!("Failed to parse cache file {}: {}", self.persist_path, e);
}
}
}
Err(_) => {
info!("No cache file found at {}, starting fresh", self.persist_path);
}
}
}
/// Clear cache file on startup to ensure fresh data
fn clear_cache_file(&self) {
if Path::new(&self.persist_path).exists() {
match fs::remove_file(&self.persist_path) {
Ok(_) => info!("Cleared cache file {} on startup", self.persist_path),
Err(e) => warn!("Failed to clear cache file {}: {}", self.persist_path, e),
}
}
}
}
#[derive(Clone)]
pub struct MetricCacheManager {
cache: Arc<SimpleCache>,
}
impl MetricCacheManager {
pub fn new(config: CacheConfig) -> Self {
Self {
cache: Arc::new(SimpleCache::new(config)),
}
}
pub async fn store_metric(&self, metric: Metric) {
self.cache.store_metric(metric).await;
}
pub async fn cache_metric(&self, metric: Metric) {
self.store_metric(metric).await;
}
pub async fn start_background_tasks(&self) {
// No background tasks needed for simple cache
}
pub async fn get_all_cached_metrics(&self) -> Result<Vec<Metric>, anyhow::Error> {
Ok(self.cache.get_all_cached_metrics().await)
}
pub async fn save_to_disk(&self) {
self.cache.save_to_disk().await;
}
}