149 lines
4 KiB
Rust
149 lines
4 KiB
Rust
use crate::core::models::{UnifiedData, UnifiedGroupData};
|
|
use crate::data::heartbeat::model::HeartbeatEntry;
|
|
use crate::i18n::t;
|
|
use ratatui::widgets::ScrollbarState;
|
|
use rayon::prelude::*;
|
|
|
|
const BORDER_LINES_VIEW: usize = 3;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum MonitorStatus {
|
|
Up,
|
|
Down,
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MonitorViewState {
|
|
pub name: String,
|
|
pub status: MonitorStatus,
|
|
pub response_time: String,
|
|
pub uptime_24h: String,
|
|
pub status_history: Vec<MonitorStatus>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct GroupViewState {
|
|
pub name: String,
|
|
pub monitors: Vec<MonitorViewState>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DashboardViewState {
|
|
pub title: String,
|
|
pub descriptions: Option<String>,
|
|
pub groups: Vec<GroupViewState>,
|
|
pub is_loading: bool,
|
|
pub error_message: Option<String>,
|
|
pub auto_refresh_interval: u32,
|
|
pub scroll_state: ScrollbarState,
|
|
}
|
|
|
|
impl DashboardViewState {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
title: t("loading").to_string(),
|
|
descriptions: None,
|
|
groups: Vec::new(),
|
|
is_loading: true,
|
|
error_message: None,
|
|
auto_refresh_interval: 300,
|
|
scroll_state: ScrollbarState::new(0),
|
|
}
|
|
}
|
|
|
|
pub fn from_unified_data(data: UnifiedData) -> Self {
|
|
let mut groups = Vec::with_capacity(data.groups.len());
|
|
|
|
for group in data.groups {
|
|
groups.push(GroupViewState {
|
|
name: group.group_info.name.clone(),
|
|
monitors: add_monitor_view_state(group),
|
|
});
|
|
}
|
|
|
|
let content_length = groups
|
|
.iter()
|
|
.map(|g| g.monitors.len() + BORDER_LINES_VIEW)
|
|
.sum::<usize>();
|
|
|
|
Self {
|
|
title: data.title,
|
|
descriptions: data.description,
|
|
groups,
|
|
is_loading: false,
|
|
error_message: None,
|
|
auto_refresh_interval: data.auto_refresh_interval.max(30),
|
|
scroll_state: ScrollbarState::new(content_length.saturating_sub(1)),
|
|
}
|
|
}
|
|
|
|
pub fn get_all_monitors(&self) -> Vec<&MonitorViewState> {
|
|
self.groups.iter().flat_map(|g| g.monitors.iter()).collect()
|
|
}
|
|
|
|
pub fn show_vertical_scrollbar(&self, height: u16) -> bool {
|
|
height < self.get_all_monitors().len() as u16
|
|
}
|
|
}
|
|
|
|
fn get_status_history(heartbeats: &[HeartbeatEntry]) -> Vec<MonitorStatus> {
|
|
let mut history = heartbeats
|
|
.iter()
|
|
.rev()
|
|
.take(100)
|
|
.map(|h| match h.status {
|
|
0 => MonitorStatus::Down,
|
|
1 => MonitorStatus::Up,
|
|
_ => MonitorStatus::Unknown,
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
while history.len() < 100 {
|
|
history.push(MonitorStatus::Unknown);
|
|
}
|
|
|
|
history.reverse();
|
|
history
|
|
}
|
|
|
|
fn add_monitor_view_state(group: UnifiedGroupData) -> Vec<MonitorViewState> {
|
|
let mut monitors = Vec::with_capacity(group.monitors.len());
|
|
group
|
|
.monitors
|
|
.into_par_iter()
|
|
.map(|monitor| {
|
|
let status_history = get_status_history(&monitor.heartbeats);
|
|
|
|
let status = match monitor.heartbeats.last().map(|h| h.status) {
|
|
Some(1) => MonitorStatus::Up,
|
|
Some(0) => MonitorStatus::Down,
|
|
_ => MonitorStatus::Unknown,
|
|
};
|
|
|
|
let response_time = monitor
|
|
.heartbeats
|
|
.last()
|
|
.and_then(|h| h.ping)
|
|
.map(|ms| format!("{}", ms))
|
|
.unwrap_or_else(|| t("unknown").to_string() + " ");
|
|
|
|
let uptime_24h = monitor
|
|
.uptime_data
|
|
.map(|u| u.get_perc_formated())
|
|
.unwrap_or_else(|| t("unknown").to_string());
|
|
|
|
MonitorViewState {
|
|
name: monitor.name,
|
|
status,
|
|
response_time,
|
|
uptime_24h,
|
|
status_history,
|
|
}
|
|
})
|
|
.collect_into_vec(&mut monitors);
|
|
|
|
monitors.sort_by_key(|m| m.name.clone());
|
|
|
|
monitors
|
|
}
|