aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/dashboard
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/dashboard')
-rw-r--r--src/api/dashboard/mod.rs54
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.