From 9ee400041f62f61ee7cb298efd5bff425f2b47dc Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 30 Apr 2026 15:53:24 -0700 Subject: [PATCH] Pulling data off KUma status page correctly --- src/api.rs | 56 +++++++++++++++++++++++++++++++++++++++-------------- src/main.rs | 8 ++++++-- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/api.rs b/src/api.rs index 26d6474..b1f5080 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,6 +1,8 @@ use serde_json::Value; +use serde::Serialize; use reqwest::{self, Error}; +/// Target base URL to use for all KUMA API call's pub const BASE_URL: &str = "https://uptime.shockrah.xyz"; @@ -14,21 +16,30 @@ macro_rules! endpoints { ($slug:expr) => { format!("{}/api/status-page/{}", crate::api::BASE_URL, $slug) } } -#[derive(Debug)] +/// A single heartbeat within a monitor's latest status +#[derive(Debug, Serialize)] pub struct HeartBeat { status: i64, time: String, msg: String, + ping: i64, } -#[derive(Debug)] +/// A monitor which contains the recent heartbeats for that monitor +/// Heartbeat list themselves will always be populated and can be configured +/// to have a maximum size with the MAX_HEARTBEATS env variable +#[derive(Debug, Serialize)] pub struct KumaMonitor { id: i64, name: String, - heartbeats: Option> + // TODO: make sure we support MAX_HEARTBEATS somehow + heartbeats: Vec } -#[derive(Debug)] +/// The overall look of the Kuma status page +/// For now we only have pub however we can control which we are loking at +/// with the use of the `slug` field. +#[derive(Debug, Serialize)] pub struct KumaStatusPage { slug: String, title: String, @@ -37,22 +48,37 @@ pub struct KumaStatusPage { } impl KumaMonitor { - pub fn blank(val: &Value) -> Self { - Self { - id: val["id"].as_i64().unwrap_or(0), - name: val["name"].to_string(), - heartbeats: None + pub async fn new(val: &Value) -> Result { + // Populate the monitor with it's respective heartbeats at that time + let id = val["id"].as_i64().unwrap_or(0); + let name = val["name"].to_string(); + let response: Value = reqwest::get(heartbeat!("pub")).await?.json().await?; + if let Some(list) = &response["heartbeatList"][id.to_string()].as_array() { + let heartbeats = list.iter().map(|item| { + HeartBeat { + status: item["status"].as_i64().unwrap_or(-1), + time: item["time"].to_string().replace("\"", ""), + msg: item["msg"].to_string().replace("\"", ""), + ping: item["ping"].as_i64().unwrap_or(-1), + } + }).collect(); + return Ok(Self { id, name, heartbeats }) } + // TODO: wtf should a default response be? + // temporary blank response for now + Ok(Self { id, name, heartbeats: vec![] }) } } impl KumaStatusPage { - fn get_monitors(json: &Value) -> Vec { + async fn get_monitors(json: &Value) -> Vec { let mut monitors = vec![]; for group in json["publicGroupList"].as_array().unwrap_or(&vec![]) { for monitor in group["monitorList"].as_array().unwrap_or(&vec![]) { - monitors.push(KumaMonitor::blank(&monitor)); + if let Ok(mon) = KumaMonitor::new(&monitor).await { + monitors.push(mon); + } } } return monitors; @@ -62,10 +88,10 @@ impl KumaStatusPage { let endpoint = endpoints!(slug); let resp: Value = reqwest::get(&endpoint).await?.json().await?; return Ok(KumaStatusPage { - slug: resp["config"]["slug"].to_string(), - title: resp["config"]["title"].to_string(), - description: resp["config"]["description"].to_string(), - monitors: KumaStatusPage::get_monitors(&resp) + slug: resp["config"]["slug"].as_str().unwrap().into(), + title: resp["config"]["title"].as_str().unwrap().into(), + description: resp["config"]["description"].as_str().unwrap().into(), + monitors: KumaStatusPage::get_monitors(&resp).await }) } } diff --git a/src/main.rs b/src/main.rs index 5031e45..43761e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,16 @@ mod api; use reqwest::{self, Error}; use crate::api::KumaStatusPage; +use serde_json; #[tokio::main] async fn main() -> Result<(), Error> { - let page = KumaStatusPage::get("pub").await?; - println!("{page:?}"); + let public = KumaStatusPage::get("pub").await?; + match serde_json::to_string(&public) { + Ok(result) => println!("{result}"), + Err(_) => eprintln!("bruh") + }; Ok(()) }