aboutsummaryrefslogtreecommitdiff
path: root/src/web.rs
blob: b0d2ca9670663fc504be98ac161a0e730b96b1bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use crate::{log::LOG, Check, Config, Service, Status, STATUS};
use axum::response::Html;
use chrono::SubsecRound;
use markup::{doctype, Render};
use std::{collections::BTreeMap, ops::Deref, sync::Arc};

pub async fn send_html_page(config: Arc<Config>) -> Html<String> {
    let mut out = String::new();
    let status = STATUS.read().await;
    let status = status.deref();
    let log = LOG.read().await;
    let log = log.deref();

    #[cfg(not(debug_assertions))]
    let css = include_str!("style.css");
    #[cfg(debug_assertions)]
    let css = tokio::fs::read_to_string("src/style.css").await.unwrap();

    markup::new! {
        @doctype()
        html {
            head {
                meta[charset="UTF-8"];
                meta[name="viewport", content="width=device-width, initial-scale=1.0"];
                title { "Status Page" }
                style { @css }
            }
            body {
                main {
                    h1 { @config.title }
                    @for (i, service) in config.services.iter().enumerate() {
                        @ServiceCard { i, status, service }
                    }
                    div.log {
                        h2 { "Past Events" }
                        ul {
                            @for event in log.iter() { 
                                li.{if event.status.status.is_ok() { "ok" } else { "error" }} {
                                    @let service = &config.services[event.service];
                                    b { @event.status.time.to_rfc2822() ": " }
                                    @service.title " "
                                    @if let Err(error) = &event.status.status {
                                        " failed. " @service.checks[event.check].display() " reported " @error
                                    } else { 
                                        " is working again."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    .render(&mut out)
    .unwrap();
    Html(out)
}

markup::define!(
    ServiceCard<'a>(status: &'a BTreeMap<(usize, usize), Status>, service: &'a Service, i: usize) {
        @let any_err = status.range((*i,usize::MIN)..(*i,usize::MAX)).any(|(_,v)|v.status.is_err());
        div.service.{if any_err { "error" } else { "ok" }} {
            h2 { @service.title }
            @if let Some(url) = &service.url { a[href=url] { "Link" } }
            div.checks {
                @for (j, check) in service.checks.iter().enumerate() {
                    @if let Some(status) = status.get(&(*i, j)) {
                        span.name { @check.display() }
                        @match &status.status {
                            Ok(s) => {
                                span.status.ok { "Ok" }
                                span.details.ok { @s }
                            }
                            Err(s) => {
                                span.status.error { "Error" }
                                span.details.error { @s }
                            }
                        }
                        span.time {
                            @status.time.time().round_subsecs(0).to_string()
                        }
                    }
                }
            }
        }
    }
);

impl Check {
    pub fn display(&self) -> String {
        match self {
            Check::Systemd(_) => "Service".to_string(),
            Check::Http { title, .. } => title.clone().unwrap_or("HTTP".to_string()),
            Check::Shell { title, .. } => title.to_owned(),
            Check::Pacman(_) => "Installed".to_string(),
        }
    }
}