uptime-kuma-dashboard/src/ui/dashboard/model.rs

156 lines
4.2 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::*;
use std::borrow::Cow;
pub const BORDER_LINES_VIEW: usize = 3;
#[derive(Debug, Clone, PartialEq)]
pub enum MonitorStatus {
Up,
Down,
Unknown,
}
#[derive(Debug, Clone)]
pub struct MonitorViewState {
pub name: Cow<'static, str>,
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,
total_length: usize,
}
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),
total_length: 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 total_length: usize = groups
.iter()
.map(|g| g.monitors.len() + BORDER_LINES_VIEW)
.sum();
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(total_length.saturating_sub(1)),
total_length,
}
}
pub fn get_total_lenght(&self) -> usize {
self.total_length
}
pub fn get_all_monitors(&self) -> Vec<&MonitorViewState> {
self.groups.iter().flat_map(|g| g.monitors.iter()).collect()
}
pub fn show_vertical_scrollbar(&self, available_height: u16) -> bool {
self.total_length as u16 > available_height
}
}
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: Cow::Owned(monitor.name),
status,
response_time,
uptime_24h,
status_history,
}
})
.collect_into_vec(&mut monitors);
monitors.sort_by_key(|m| m.name.clone());
monitors
}