diff options
Diffstat (limited to 'src/api/dashboard/mod.rs')
| -rw-r--r-- | src/api/dashboard/mod.rs | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/src/api/dashboard/mod.rs b/src/api/dashboard/mod.rs index 1de21cd..99a0b83 100644 --- a/src/api/dashboard/mod.rs +++ b/src/api/dashboard/mod.rs @@ -15,7 +15,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::convert::Infallible; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, OnceLock}; use std::time::Duration; use axum::http::{HeaderValue, StatusCode, header::CONTENT_TYPE}; @@ -221,6 +221,7 @@ async fn build(state: &AppState, graph: &Graph) -> JsonValue { } json!({ "node": { "id": local, "alias": state.alias }, + "page": page().0, "stats": stats, "peers": peers, "graph": { "members": members_json, "intended": intended, "measured": measured }, @@ -312,15 +313,36 @@ fn merge_patch(old: &JsonValue, new: &JsonValue) -> Option<JsonValue> { /// `closing` turns true when the node stops (`Hub::closing`). pub(crate) fn router(state: AppState, closing: watch::Receiver<bool>) -> Router<AppState> { Router::new() - .route("/admin/dashboard", get(page)) + .route("/admin/dashboard", get(page_html)) .route("/admin/dashboard/state", get(current_state)) .route("/admin/dashboard/events", get(events)) .layer(Extension(Hub::new(state, closing))) } +/// The page as built into this binary. +const PAGE: &str = include_str!("dashboard.html"); +/// Where the page says which version of it it is. +const PAGE_VERSION_MARK: &str = "__PAGE_VERSION__"; + +/// The page's version (a hash of it), and the page saying so: a page whose version +/// differs from the snapshot's `page` was served by another build, and reloads. +fn page() -> &'static (String, String) { + static PAGE_SERVED: OnceLock<(String, String)> = OnceLock::new(); + PAGE_SERVED.get_or_init(|| { + use sha2::{Digest, Sha256}; + let version: String = Sha256::digest(PAGE.as_bytes()) + .iter() + .take(8) + .map(|byte| format!("{byte:02x}")) + .collect(); + let served = PAGE.replace(PAGE_VERSION_MARK, &version); + (version, served) + }) +} + /// `GET /admin/dashboard`: the page. -async fn page() -> Html<&'static str> { - Html(include_str!("dashboard.html")) +async fn page_html() -> Html<&'static str> { + Html(page().1.as_str()) } /// `GET /admin/dashboard/state`: the latest snapshot, for a client that wants it whole. |
