Update version to 0.1.20 and fix email notification aggregation
All checks were successful
Build and Release / build-and-release (push) Successful in 1m11s

- Fix email notification aggregation to send batched notifications instead of individual emails
- Fix startup data collection to properly process initial status without triggering change notifications
- Maintain event-driven transmission while preserving aggregated notification batching
- Update version from 0.1.19 to 0.1.20 across all components
This commit is contained in:
Christoffer Martinsson 2025-10-28 10:48:29 +01:00
parent 91f037aa3e
commit a2519b2814
6 changed files with 31 additions and 48 deletions

View File

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

View File

@ -125,7 +125,7 @@ impl Agent {
info!("Force collected and cached {} metrics", metrics.len()); info!("Force collected and cached {} metrics", metrics.len());
// Process metrics through status manager (don't trigger transmission on startup) // Process metrics through status manager (collect status data at startup)
let _status_changed = self.process_metrics(&metrics).await; let _status_changed = self.process_metrics(&metrics).await;
Ok(()) Ok(())

View File

@ -158,62 +158,45 @@ impl HostStatusManager {
/// Process a metric - updates status and triggers immediate notifications if status changed /// Process a metric - updates status and queues for aggregated notifications if status changed
pub async fn process_metric(&mut self, metric: &Metric, notification_manager: &mut crate::notifications::NotificationManager) -> bool { pub async fn process_metric(&mut self, metric: &Metric, _notification_manager: &mut crate::notifications::NotificationManager) -> bool {
let old_status = self.service_statuses.get(&metric.name).copied().unwrap_or(Status::Unknown); let old_status = self.service_statuses.get(&metric.name).copied();
let new_status = metric.status; let new_status = metric.status;
// Update status // Update status
self.update_service_status(metric.name.clone(), new_status); self.update_service_status(metric.name.clone(), new_status);
// Check if status actually changed // Check if status actually changed (ignore first-time status setting)
if old_status != new_status { if let Some(old_status) = old_status {
debug!("Status change detected for {}: {:?} -> {:?}", metric.name, old_status, new_status); if old_status != new_status {
debug!("Status change detected for {}: {:?} -> {:?}", metric.name, old_status, new_status);
// Process notification immediately on status change
if let Err(e) = self.process_status_change(&metric.name, old_status, new_status, notification_manager).await { // Queue change for aggregated notification (not immediate)
error!("Failed to process status change notification: {}", e); self.queue_status_change(&metric.name, old_status, new_status);
return true; // Status changed - caller should trigger immediate transmission
} }
} else {
return true; // Status changed - caller should trigger immediate transmission debug!("Initial status set for {}: {:?}", metric.name, new_status);
} }
false // No status change false // No status change (or first-time status)
} }
/// Process immediate status change notification /// Queue status change for aggregated notification
async fn process_status_change(&mut self, metric_name: &str, old_status: Status, new_status: Status, notification_manager: &mut crate::notifications::NotificationManager) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { fn queue_status_change(&mut self, metric_name: &str, old_status: Status, new_status: Status) {
if !self.config.enabled { // Add to pending changes for aggregated notification
return Ok(()); let entry = self.pending_changes.entry(metric_name.to_string()).or_insert((old_status, old_status, 0));
} entry.1 = new_status; // Update final status
entry.2 += 1; // Increment change count
// Create immediate notification for this specific status change
let status_summary = StatusChangeSummary {
service_name: metric_name.to_string(),
initial_status: old_status,
final_status: new_status,
change_count: 1,
};
let aggregated = AggregatedStatusChanges {
start_time: Instant::now(),
end_time: Instant::now(),
service_summaries: vec![status_summary],
host_status_initial: old_status,
host_status_final: new_status,
requires_notification: true,
};
// Send immediate notification using existing method
if let Err(e) = self.send_aggregated_email(&aggregated, notification_manager).await {
error!("Failed to send immediate notification: {}", e);
return Err(e);
}
info!("Sent immediate notification for {} status change: {:?} -> {:?}", metric_name, old_status, new_status); // Set batch start time if this is the first change
Ok(()) if self.batch_start_time.is_none() {
self.batch_start_time = Some(Instant::now());
}
} }
/// Process pending notifications - legacy method, now rarely used /// Process pending notifications - legacy method, now rarely used
pub async fn process_pending_notifications(&mut self, notification_manager: &mut crate::notifications::NotificationManager) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { pub async fn process_pending_notifications(&mut self, notification_manager: &mut crate::notifications::NotificationManager) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if !self.config.enabled || self.pending_changes.is_empty() { if !self.config.enabled || self.pending_changes.is_empty() {

View File

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

View File

@ -14,7 +14,7 @@ use app::Dashboard;
/// Get hardcoded version /// Get hardcoded version
fn get_version() -> &'static str { fn get_version() -> &'static str {
"v0.1.19" "v0.1.20"
} }
/// Check if running inside tmux session /// Check if running inside tmux session

View File

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