use anyhow::{Result, Context};
use colored::Colorize;
use tabled::{Table, Tabled, settings::Style};
use serde::{Serialize, Deserialize};
use std::time::Duration;
use crate::config::Config;
use crate::commands::HealthCommands;
use crate::utils::{print_success, print_error, print_info};
#[derive(Tabled, Serialize, Deserialize)]
struct HealthRow {
#[tabled(rename = "Component")]
component: String,
#[tabled(rename = "Status")]
status: String,
#[tabled(rename = "Response Time")]
response_time: String,
#[tabled(rename = "Details")]
details: String,
}
pub async fn handle_command(command: HealthCommands, config: &Config) -> Result<()> {
match command {
HealthCommands::Check { detailed } => {
check_system_health(detailed, config).await?
}
HealthCommands::Service { name } => {
check_service_health(&name, config).await?
}
HealthCommands::Monitor { interval, services } => {
monitor_health(interval, services, config).await?
}
}
Ok(())
}
async fn check_system_health(detailed: bool, config: &Config) -> Result<()> {
print_info("Checking system health...");
let components = vec![
HealthRow {
component: "Consul".to_string(),
status: "healthy".green().to_string(),
response_time: "5ms".to_string(),
details: "Version 1.17.0, 3 nodes".to_string(),
},
HealthRow {
component: "Gateway".to_string(),
status: "healthy".green().to_string(),
response_time: "2ms".to_string(),
details: "Uptime: 7d 14h 23m".to_string(),
},
HealthRow {
component: "User Service".to_string(),
status: "healthy".green().to_string(),
response_time: "12ms".to_string(),
details: "3/3 instances healthy".to_string(),
},
HealthRow {
component: "Order Service".to_string(),
status: "degraded".yellow().to_string(),
response_time: "145ms".to_string(),
details: "2/3 instances healthy".to_string(),
},
HealthRow {
component: "Database".to_string(),
status: "healthy".green().to_string(),
response_time: "8ms".to_string(),
details: "Primary: OK, Replica lag: 0.2s".to_string(),
},
];
println!("\n{}", "System Health Status".bold());
println!("{}", "=".repeat(80));
let table = Table::new(&components)
.with(Style::modern())
.to_string();
println!("{}", table);
let unhealthy_count = components.iter()
.filter(|c| c.status.contains("unhealthy"))
.count();
let degraded_count = components.iter()
.filter(|c| c.status.contains("degraded"))
.count();
println!("\n{}", "Overall Status".bold());
println!("{}", "-".repeat(40));
if unhealthy_count > 0 {
println!("{}: {} ({})",
"Status".bold(),
"UNHEALTHY".red().bold(),
format!("{} components down", unhealthy_count)
);
} else if degraded_count > 0 {
println!("{}: {} ({})",
"Status".bold(),
"DEGRADED".yellow().bold(),
format!("{} components degraded", degraded_count)
);
} else {
println!("{}: {}",
"Status".bold(),
"HEALTHY".green().bold()
);
}
if detailed {
println!("\n{}", "Detailed Information".bold());
println!("{}", "-".repeat(40));
println!("Resource Usage:");
println!(" CPU Usage: {}%", "23".green());
println!(" Memory Usage: {}%", "67".yellow());
println!(" Disk Usage: {}%", "45".green());
println!("\nRecent Issues:");
println!(" - Order Service instance 3 failed health check (5 minutes ago)");
println!(" - Database replica lag spike detected (2 hours ago)");
}
Ok(())
}
async fn check_service_health(service_name: &str, config: &Config) -> Result<()> {
print_info(&format!("Checking health for service: {}", service_name));
println!("\n{}", format!("Health Status for '{}'", service_name).bold());
println!("{}", "=".repeat(50));
println!("{}: {}", "Status".bold(), "healthy".green().bold());
println!("{}: {}", "Instances".bold(), "3/3 healthy");
println!("{}: {}", "Average Response Time".bold(), "45ms");
println!("{}: {}", "Success Rate".bold(), "99.8%");
println!("{}: {}", "Uptime".bold(), "14d 7h 23m");
println!("\n{}", "Instance Details".bold());
println!("{}", "-".repeat(50));
let instances = vec![
("instance-1", "172.16.0.10:8080", "healthy", "12ms"),
("instance-2", "172.16.0.11:8080", "healthy", "15ms"),
("instance-3", "172.16.0.12:8080", "healthy", "18ms"),
];
for (id, address, status, response_time) in instances {
let status_colored = if status == "healthy" {
status.green().to_string()
} else {
status.red().to_string()
};
println!(" {} @ {} - {} ({})", id, address, status_colored, response_time);
}
println!("\n{}", "Recent Health Checks".bold());
println!("{}", "-".repeat(50));
println!(" {} - {} (12ms)",
chrono::Local::now().format("%H:%M:%S"),
"passed".green()
);
println!(" {} - {} (14ms)",
(chrono::Local::now() - chrono::Duration::seconds(30)).format("%H:%M:%S"),
"passed".green()
);
println!(" {} - {} (13ms)",
(chrono::Local::now() - chrono::Duration::seconds(60)).format("%H:%M:%S"),
"passed".green()
);
Ok(())
}
async fn monitor_health(
interval: u64,
services: Vec<String>,
config: &Config,
) -> Result<()> {
use tokio::time;
use std::io::{self, Write};
let services_to_monitor = if services.is_empty() {
vec![
"gateway".to_string(),
"user-service".to_string(),
"order-service".to_string(),
]
} else {
services
};
print_info(&format!(
"Monitoring health for: {} (updating every {}s)",
services_to_monitor.join(", "),
interval
));
println!("Press Ctrl+C to stop monitoring\n");
let mut interval_timer = time::interval(Duration::from_secs(interval));
loop {
print!("\x1B[2J\x1B[1;1H");
io::stdout().flush()?;
println!("{}", "Health Monitor".bold());
println!("{}: {}", "Time".bold(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S"));
println!("{}", "=".repeat(80));
for service in &services_to_monitor {
let (status, response_time, instances) = match service.as_str() {
"gateway" => ("healthy", "2ms", "1/1"),
"user-service" => ("healthy", "15ms", "3/3"),
"order-service" => ("degraded", "145ms", "2/3"),
_ => ("unknown", "N/A", "0/0"),
};
let status_colored = match status {
"healthy" => status.green().bold(),
"degraded" => status.yellow().bold(),
"unhealthy" => status.red().bold(),
_ => status.dimmed(),
};
println!("{:.<30} {} ({}, {})",
format!("{} ", service),
status_colored,
response_time,
instances
);
}
println!("\n{}", format!("Next update in {} seconds...", interval).dimmed());
interval_timer.tick().await;
}
}