327 lines
12 KiB
Rust
327 lines
12 KiB
Rust
use std::error::Error;
|
|
|
|
use async_std::task;
|
|
use futures::stream::TryStreamExt;
|
|
use golgi::{messages::SsbMessageValue, Sbot};
|
|
use maud::{html, Markup, PreEscaped};
|
|
use peach_lib::sbot::{SbotConfig, SbotStatus};
|
|
|
|
use crate::{error::PeachWebError, templates};
|
|
|
|
// HELPER FUNCTIONS
|
|
|
|
pub async fn init_sbot_with_config(
|
|
sbot_config: &Option<SbotConfig>,
|
|
) -> Result<Sbot, PeachWebError> {
|
|
// initialise sbot connection with ip:port and shscap from config file
|
|
let sbot_client = match sbot_config {
|
|
// TODO: panics if we pass `Some(conf.shscap)` as second arg
|
|
Some(conf) => {
|
|
let ip_port = conf.lis.clone();
|
|
Sbot::init(Some(ip_port), None).await?
|
|
}
|
|
None => Sbot::init(None, None).await?,
|
|
};
|
|
|
|
Ok(sbot_client)
|
|
}
|
|
|
|
fn latest_sequence_number() -> Result<u64, Box<dyn Error>> {
|
|
// retrieve latest go-sbot configuration parameters
|
|
let sbot_config = SbotConfig::read().ok();
|
|
|
|
task::block_on(async {
|
|
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
|
|
|
|
// retrieve the local id
|
|
let id = sbot_client.whoami().await?;
|
|
|
|
let history_stream = sbot_client.create_history_stream(id).await?;
|
|
let mut msgs: Vec<SsbMessageValue> = history_stream.try_collect().await?;
|
|
|
|
// reverse the list of messages so we can easily reference the latest one
|
|
msgs.reverse();
|
|
|
|
// return the sequence number of the latest msg
|
|
Ok(msgs[0].sequence)
|
|
})
|
|
}
|
|
|
|
fn downtime_element(downtime: &Option<String>) -> Markup {
|
|
match downtime {
|
|
Some(time) => {
|
|
html! {
|
|
label class="label-small font-gray" for="sbotDowntime" title="go-sbot downtime" style="margin-top: 0.5rem;" { "DOWNTIME" }
|
|
p id="sbotDowntime" class="card-text" title="Downtime" { (time) }
|
|
}
|
|
}
|
|
_ => html! { (PreEscaped("")) },
|
|
}
|
|
}
|
|
|
|
fn uptime_element(uptime: &Option<String>) -> Markup {
|
|
match uptime {
|
|
Some(time) => {
|
|
html! {
|
|
label class="label-small font-gray" for="sbotUptime" title="go-sbot uptime" style="margin-top: 0.5rem;" { "UPTIME" }
|
|
p id="sbotUptime" class="card-text" title="Uptime" { (time) }
|
|
}
|
|
}
|
|
_ => html! { (PreEscaped("")) },
|
|
}
|
|
}
|
|
|
|
fn run_on_startup_element(boot_state: &Option<String>) -> Markup {
|
|
match boot_state {
|
|
Some(state) if state == "enabled" => {
|
|
html! {
|
|
p id="runOnStartup" class="card-text" title="Enabled" { "Enabled" }
|
|
}
|
|
}
|
|
_ => {
|
|
html! {
|
|
p id="runOnStartup" class="card-text" title="Disabled" { "Disabled" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn database_element(state: &str) -> Markup {
|
|
// retrieve the sequence number of the latest message in the sbot database
|
|
let sequence_num = latest_sequence_number();
|
|
|
|
if state == "active" && sequence_num.is_ok() {
|
|
let number = sequence_num.unwrap();
|
|
html! {
|
|
label class="card-text" style="margin-right: 5px;" { (number) }
|
|
label class="label-small font-gray" { "MESSAGES IN LOCAL DATABASE" }
|
|
}
|
|
} else {
|
|
html! { label class="label-small font-gray" { "DATABASE UNAVAILABLE" } }
|
|
}
|
|
}
|
|
|
|
fn memory_element(memory: Option<u32>) -> Markup {
|
|
let (memory, img_class, medium_label_class, small_label_class) = match memory {
|
|
Some(mem) => {
|
|
// convert memory to mb representation
|
|
let memory_rounded = mem / 1024 / 1024;
|
|
(
|
|
memory_rounded.to_string(),
|
|
"icon icon-active",
|
|
"label-medium font-normal",
|
|
"label-small font-normal",
|
|
)
|
|
}
|
|
_ => (
|
|
0.to_string(),
|
|
"icon icon-inactive",
|
|
"label-medium font-gray",
|
|
"label-small font-gray",
|
|
),
|
|
};
|
|
|
|
html! {
|
|
div class="stack" {
|
|
img class=(img_class) title="Memory" src="/icons/ram.png";
|
|
div class="flex-grid" style="padding-top: 0.5rem;" {
|
|
label class=(medium_label_class) style="padding-right: 3px;" title="Memory usage of the go-sbot process in MB" { (memory) }
|
|
label class=(small_label_class) { "MB" }
|
|
}
|
|
label class=(small_label_class) { "MEMORY" }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn hops_element() -> Markup {
|
|
// retrieve go-sbot systemd process status
|
|
let hops = match SbotConfig::read() {
|
|
Ok(conf) => conf.hops,
|
|
_ => 0,
|
|
};
|
|
|
|
html! {
|
|
div class="stack" {
|
|
img class="icon icon-active" title="Hops" src="/icons/orbits.png";
|
|
div class="flex-grid" style="padding-top: 0.5rem;" {
|
|
label class="label-medium font-normal" style="padding-right: 3px;" title="Replication hops" {
|
|
(hops)
|
|
}
|
|
}
|
|
label class="label-small font-gray" { "HOPS" }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn blobs_element(blobstore: Option<u64>) -> Markup {
|
|
let blobstore_size = match blobstore {
|
|
// convert blobstore size to mb representation
|
|
Some(blobs) => blobs / 1024 / 1024,
|
|
None => 0,
|
|
};
|
|
|
|
html! {
|
|
div class="stack" {
|
|
img class="icon icon-active" title="Blobs" src="/icons/image-file.png";
|
|
div class="flex-grid" style="padding-top: 0.5rem;" {
|
|
label class="label-medium font-normal" style="padding-right: 3px;" title="Blobstore size in MB" { (blobstore_size) }
|
|
label class="label-small font-normal" { "MB" }
|
|
}
|
|
label class="label-small font-gray" { "BLOBSTORE" }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Read the state of the go-sbot process and define status-related
|
|
/// elements accordingly.
|
|
fn render_status_elements<'a>() -> (
|
|
String,
|
|
&'a str,
|
|
&'a str,
|
|
Markup,
|
|
Markup,
|
|
Markup,
|
|
Markup,
|
|
Markup,
|
|
) {
|
|
// retrieve go-sbot systemd process status
|
|
let sbot_status = SbotStatus::read();
|
|
|
|
// conditionally render the following elements:
|
|
// state, capsule border class, sbot icon class, uptime or downtime element,
|
|
// run on startup element and database (sequence number) element
|
|
if let Ok(status) = sbot_status {
|
|
match status.state {
|
|
Some(state) if state == "active" => (
|
|
"ACTIVE".to_string(),
|
|
"capsule capsule-container border-success",
|
|
"center icon icon-active",
|
|
uptime_element(&status.uptime),
|
|
run_on_startup_element(&status.boot_state),
|
|
database_element("active"),
|
|
memory_element(status.memory),
|
|
blobs_element(status.blobstore),
|
|
),
|
|
Some(state) if state == "inactive" => (
|
|
"INACTIVE".to_string(),
|
|
"capsule capsule-container border-warning",
|
|
"center icon icon-inactive",
|
|
downtime_element(&status.downtime),
|
|
run_on_startup_element(&status.boot_state),
|
|
database_element("inactive"),
|
|
memory_element(None),
|
|
blobs_element(status.blobstore),
|
|
),
|
|
// state is neither active nor inactive (might be failed)
|
|
Some(state) => (
|
|
state.to_string(),
|
|
"capsule capsule-container border-danger",
|
|
"center icon icon-inactive",
|
|
downtime_element(&None),
|
|
run_on_startup_element(&status.boot_state),
|
|
database_element("failed"),
|
|
memory_element(None),
|
|
blobs_element(status.blobstore),
|
|
),
|
|
None => (
|
|
"UNAVAILABLE".to_string(),
|
|
"capsule capsule-container border-danger",
|
|
"center icon icon-inactive",
|
|
downtime_element(&None),
|
|
run_on_startup_element(&status.boot_state),
|
|
database_element("unavailable"),
|
|
memory_element(None),
|
|
blobs_element(status.blobstore),
|
|
),
|
|
}
|
|
// show an error state if the attempt to read the go-sbot process
|
|
// status fails
|
|
} else {
|
|
(
|
|
"PROCESS QUERY FAILED".to_string(),
|
|
"capsule capsule-container border-danger",
|
|
"center icon icon-inactive",
|
|
downtime_element(&None),
|
|
run_on_startup_element(&None),
|
|
database_element("error"),
|
|
memory_element(None),
|
|
blobs_element(None),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Scuttlebutt status template builder.
|
|
pub fn build_template() -> PreEscaped<String> {
|
|
let (
|
|
sbot_state,
|
|
capsule_class,
|
|
sbot_icon_class,
|
|
uptime_downtime_element,
|
|
run_on_startup_element,
|
|
database_element,
|
|
memory_element,
|
|
blobs_element,
|
|
) = render_status_elements();
|
|
|
|
let hops_element = hops_element();
|
|
|
|
let status_template = html! {
|
|
(PreEscaped("<!-- SCUTTLEBUTT STATUS -->"))
|
|
div class="card center" {
|
|
(PreEscaped("<!-- SBOT INFO BOX -->"))
|
|
div class=(capsule_class) {
|
|
(PreEscaped("<!-- SBOT STATUS GRID -->"))
|
|
div class="two-grid" title="go-sbot process state" {
|
|
(PreEscaped("<!-- top-right config icon -->"))
|
|
a class="link two-grid-top-right" href="/settings/scuttlebutt" title="Configure Scuttlebutt settings" {
|
|
img id="configureNetworking" class="icon-small icon-active" src="/icons/cog.svg" alt="Configure";
|
|
}
|
|
(PreEscaped("<!-- left column -->"))
|
|
(PreEscaped("<!-- go-sbot state icon with label -->"))
|
|
div class="grid-column-1" {
|
|
img id="sbotStateIcon" class=(sbot_icon_class) src="/icons/hermies.svg" alt="Hermies";
|
|
label id="sbotStateLabel" for="sbotStateIcon" class="center label-small font-gray" style="margin-top: 0.5rem;" title="Sbot state" {
|
|
(sbot_state)
|
|
}
|
|
}
|
|
(PreEscaped("<!-- right column -->"))
|
|
(PreEscaped("<!-- go-sbot version and uptime / downtime with labels -->"))
|
|
div class="grid-column-2" {
|
|
label class="label-small font-gray" for="sbotVersion" title="go-sbot version" {
|
|
"VERSION"
|
|
}
|
|
p id="sbotVersion" class="card-text" title="Version" {
|
|
"1.1.0-alpha"
|
|
}
|
|
(uptime_downtime_element)
|
|
label class="label-small font-gray" for="sbotBootState" title="go-sbot boot state" style="margin-top: 0.5rem;" { "RUN ON STARTUP" }
|
|
(run_on_startup_element)
|
|
}
|
|
}
|
|
hr style="color: var(--light-gray);";
|
|
div id="middleSection" style="margin-top: 1rem;" {
|
|
div id="sbotInfo" class="center" style="display: flex; justify-content: space-between; width: 90%;" {
|
|
div class="center" style="display: flex; align-items: last baseline;" {
|
|
(database_element)
|
|
}
|
|
}
|
|
}
|
|
hr style="color: var(--light-gray);";
|
|
(PreEscaped("<!-- THREE-ACROSS STACK -->"))
|
|
div class="three-grid card-container" style="margin-top: 1rem;" {
|
|
(hops_element)
|
|
(blobs_element)
|
|
(memory_element)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// wrap the nav bars around the settings menu template content
|
|
// parameters are template, title and back url
|
|
let body = templates::nav::build_template(status_template, "Settings", Some("/"));
|
|
|
|
// render the base template with the provided body
|
|
templates::base::build_template(body)
|
|
}
|