diff options
Diffstat (limited to 'src/api/dashboard/mod.rs')
| -rw-r--r-- | src/api/dashboard/mod.rs | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/src/api/dashboard/mod.rs b/src/api/dashboard/mod.rs index b89955a..d5f0ed8 100644 --- a/src/api/dashboard/mod.rs +++ b/src/api/dashboard/mod.rs @@ -537,9 +537,63 @@ pub(crate) fn router(state: AppState) -> Router<AppState> { .route("/admin/dashboard/refresh", post(refresh)) .route("/admin/dashboard/links", post(set_links)) .route("/admin/dashboard/tree", get(writer_tree)) + .route("/admin/dashboard/links/own", get(own_links_now)) + .route("/admin/dashboard/links/own/events", get(own_links_events)) .layer(Extension(Hub::new(state, closing))) } +/// How often a stream of this node's own links looks whether they changed +/// (`own_links_events`). +const OWN_LINKS_EVERY: Duration = Duration::from_secs(2); + +/// This node's own links, as it measures them: its ID, its epoch, the links +/// (`links_json`), what a member's report on the map holds. +async fn own_report(state: &AppState) -> (Vec<Link>, JsonValue) { + let own = links::own_links(state).await; + let report = json!({ + "node": state.local_id(), + "epoch": crate::document::replica::EpochRecord::from(state.local_epoch()), + "links": links_json(&own), + }); + (own, report) +} + +/// `GET /admin/dashboard/links/own`: this node's own links (`own_report`), for a page +/// of this node or, through CORS, another member's (`api::cors`). +async fn own_links_now(Extension(hub): Extension<Arc<Hub>>) -> axum::Json<JsonValue> { + axum::Json(own_report(&hub.state).await.1) +} + +/// `GET /admin/dashboard/links/own/events`: server-sent events, `links` with this +/// node's own links at once, then whenever they changed (`links::changed`, looked at +/// every `OWN_LINKS_EVERY`); `offline` when the node stops, which ends it. +async fn own_links_events(Extension(hub): Extension<Arc<Hub>>) -> impl IntoResponse { + let stream = async_stream::stream! { + let (mut last, report) = own_report(&hub.state).await; + yield Ok::<_, Infallible>(Event::default().event("links").data(report.to_string())); + let mut every = tokio::time::interval(OWN_LINKS_EVERY); + every.tick().await; + loop { + tokio::select! { + _ = every.tick() => {} + () = hub.closed() => { + yield Ok(offline_event()); + break; + } + } + let (own, report) = own_report(&hub.state).await; + if links::changed(&last, &own) { + last = own; + yield Ok(Event::default().event("links").data(report.to_string())); + } + } + }; + ( + [(super::UNBUFFERED.0, super::UNBUFFERED.1)], + Sse::new(stream).keep_alive(KeepAlive::default()), + ) +} + /// The page as built into this binary. const PAGE: &str = include_str!("dashboard.html"); /// Where the page says which version of it it is. |
