Compare commits

..

4 Commits

Author SHA1 Message Date
9b84b70581 Bump version to v0.1.191
All checks were successful
Build and Release / build-and-release (push) Successful in 1m8s
2025-11-27 18:16:49 +01:00
92c3ee3f2a Add Docker whale icon for docker images
Docker images now display with distinctive 🐋 whale icon in blue (highlight color) instead of status icons. This provides clear visual identification that these are docker images while not implying operational status.
2025-11-27 18:16:33 +01:00
1be55f765d Bump version to v0.1.190
All checks were successful
Build and Release / build-and-release (push) Successful in 1m9s
2025-11-27 18:09:49 +01:00
2f94a4b853 Add service_type field to separate data from presentation
Changes:
- Add service_type field to SubServiceData: 'nginx_site', 'container', 'image'
- Agent sends pure data without display formatting
- Dashboard checks service_type to decide presentation
- Docker images now display without status icon (service_type='image')
- Remove unused image_size_str from docker images tuple

Clean separation: agent provides data, dashboard handles display logic.
2025-11-27 18:09:20 +01:00
7 changed files with 80 additions and 40 deletions

6
Cargo.lock generated
View File

@@ -279,7 +279,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]] [[package]]
name = "cm-dashboard" name = "cm-dashboard"
version = "0.1.188" version = "0.1.190"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -301,7 +301,7 @@ dependencies = [
[[package]] [[package]]
name = "cm-dashboard-agent" name = "cm-dashboard-agent"
version = "0.1.188" version = "0.1.190"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -324,7 +324,7 @@ dependencies = [
[[package]] [[package]]
name = "cm-dashboard-shared" name = "cm-dashboard-shared"
version = "0.1.188" version = "0.1.190"
dependencies = [ dependencies = [
"chrono", "chrono",
"serde", "serde",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard-agent" name = "cm-dashboard-agent"
version = "0.1.189" version = "0.1.191"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@@ -113,6 +113,7 @@ impl SystemdCollector {
name: site_name.clone(), name: site_name.clone(),
service_status: self.calculate_service_status(&site_name, &site_status), service_status: self.calculate_service_status(&site_name, &site_status),
metrics, metrics,
service_type: "nginx_site".to_string(),
}); });
} }
} }
@@ -128,12 +129,13 @@ impl SystemdCollector {
name: container_name.clone(), name: container_name.clone(),
service_status: self.calculate_service_status(&container_name, &container_status), service_status: self.calculate_service_status(&container_name, &container_status),
metrics, metrics,
service_type: "container".to_string(),
}); });
} }
// Add Docker images // Add Docker images
let docker_images = self.get_docker_images(); let docker_images = self.get_docker_images();
for (image_name, image_status, image_size_str, image_size_mb) in docker_images { for (image_name, image_status, image_size_mb) in docker_images {
let mut metrics = Vec::new(); let mut metrics = Vec::new();
metrics.push(SubServiceMetric { metrics.push(SubServiceMetric {
label: "size".to_string(), label: "size".to_string(),
@@ -142,9 +144,10 @@ impl SystemdCollector {
}); });
sub_services.push(SubServiceData { sub_services.push(SubServiceData {
name: format!("{} ({})", image_name, image_size_str), name: image_name.to_string(),
service_status: self.calculate_service_status(&image_name, &image_status), service_status: self.calculate_service_status(&image_name, &image_status),
metrics, metrics,
service_type: "image".to_string(),
}); });
} }
} }
@@ -824,7 +827,7 @@ impl SystemdCollector {
} }
/// Get docker images as sub-services /// Get docker images as sub-services
fn get_docker_images(&self) -> Vec<(String, String, String, f32)> { fn get_docker_images(&self) -> Vec<(String, String, f32)> {
let mut images = Vec::new(); let mut images = Vec::new();
// Check if docker is available (cm-agent user is in docker group) with 3 second timeout // Check if docker is available (cm-agent user is in docker group) with 3 second timeout
let output = Command::new("timeout") let output = Command::new("timeout")
@@ -865,9 +868,8 @@ impl SystemdCollector {
let size_mb = self.parse_docker_size(size_str); let size_mb = self.parse_docker_size(size_str);
images.push(( images.push((
format!("I {}", image_name), image_name.to_string(),
"inactive".to_string(), // Images are informational - use inactive for neutral display "inactive".to_string(), // Images are informational - use inactive for neutral display
size_str.to_string(),
size_mb size_mb
)); ));
} }

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard" name = "cm-dashboard"
version = "0.1.189" version = "0.1.191"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@@ -32,6 +32,7 @@ struct ServiceInfo {
disk_gb: Option<f32>, disk_gb: Option<f32>,
metrics: Vec<(String, f32, Option<String>)>, // (label, value, unit) metrics: Vec<(String, f32, Option<String>)>, // (label, value, unit)
widget_status: Status, widget_status: Status,
service_type: String, // "nginx_site", "container", "image", or empty for parent services
} }
impl ServicesWidget { impl ServicesWidget {
@@ -179,6 +180,35 @@ impl ServicesWidget {
}; };
let tree_symbol = if is_last { "└─" } else { "├─" }; let tree_symbol = if is_last { "└─" } else { "├─" };
// Docker images use docker whale icon
if info.service_type == "image" {
vec![
// Indentation and tree prefix
ratatui::text::Span::styled(
format!(" {} ", tree_symbol),
Typography::tree(),
),
// Docker whale icon
ratatui::text::Span::styled(
"🐋 ".to_string(),
Style::default().fg(Theme::highlight()).bg(Theme::background()),
),
// Service name
ratatui::text::Span::styled(
format!("{:<18} ", short_name),
Style::default()
.fg(Theme::secondary_text())
.bg(Theme::background()),
),
// Status/metrics text
ratatui::text::Span::styled(
status_str,
Style::default()
.fg(Theme::secondary_text())
.bg(Theme::background()),
),
]
} else {
vec![ vec![
// Indentation and tree prefix // Indentation and tree prefix
ratatui::text::Span::styled( ratatui::text::Span::styled(
@@ -206,6 +236,7 @@ impl ServicesWidget {
), ),
] ]
} }
}
/// Move selection up /// Move selection up
pub fn select_previous(&mut self) { pub fn select_previous(&mut self) {
@@ -282,6 +313,7 @@ impl Widget for ServicesWidget {
disk_gb: Some(service.disk_gb), disk_gb: Some(service.disk_gb),
metrics: Vec::new(), // Parent services don't have custom metrics metrics: Vec::new(), // Parent services don't have custom metrics
widget_status: service.service_status, widget_status: service.service_status,
service_type: String::new(), // Parent services have no type
}; };
self.parent_services.insert(service.name.clone(), parent_info); self.parent_services.insert(service.name.clone(), parent_info);
@@ -299,6 +331,7 @@ impl Widget for ServicesWidget {
disk_gb: None, // Not used for sub-services disk_gb: None, // Not used for sub-services
metrics, metrics,
widget_status: sub_service.service_status, widget_status: sub_service.service_status,
service_type: sub_service.service_type.clone(),
}; };
sub_list.push((sub_service.name.clone(), sub_info)); sub_list.push((sub_service.name.clone(), sub_info));
} }
@@ -342,6 +375,7 @@ impl ServicesWidget {
disk_gb: None, disk_gb: None,
metrics: Vec::new(), metrics: Vec::new(),
widget_status: Status::Unknown, widget_status: Status::Unknown,
service_type: String::new(),
}); });
if metric.name.ends_with("_status") { if metric.name.ends_with("_status") {
@@ -377,6 +411,7 @@ impl ServicesWidget {
disk_gb: None, disk_gb: None,
metrics: Vec::new(), metrics: Vec::new(),
widget_status: Status::Unknown, widget_status: Status::Unknown,
service_type: String::new(), // Unknown type in legacy path
}, },
)); ));
&mut sub_service_list.last_mut().unwrap().1 &mut sub_service_list.last_mut().unwrap().1

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard-shared" name = "cm-dashboard-shared"
version = "0.1.189" version = "0.1.191"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@@ -149,6 +149,9 @@ pub struct SubServiceData {
pub name: String, pub name: String,
pub service_status: Status, pub service_status: Status,
pub metrics: Vec<SubServiceMetric>, pub metrics: Vec<SubServiceMetric>,
/// Type of sub-service: "nginx_site", "container", "image"
#[serde(default)]
pub service_type: String,
} }
/// Individual metric for a sub-service /// Individual metric for a sub-service