From db6e1ad6592aefce8a99db8478fac0baae09b228 Mon Sep 17 00:00:00 2001 From: tv Date: Fri, 30 Oct 2026 21:45:22 +0100 Subject: dashboard: click on live/reconnecting triggers --- src/api/dashboard/mod.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'src/api/dashboard/mod.rs') diff --git a/src/api/dashboard/mod.rs b/src/api/dashboard/mod.rs index 7679787..dd454ec 100644 --- a/src/api/dashboard/mod.rs +++ b/src/api/dashboard/mod.rs @@ -21,10 +21,10 @@ use std::time::Duration; use axum::http::{HeaderValue, StatusCode, header::CONTENT_TYPE}; use axum::response::sse::{Event, KeepAlive, Sse}; use axum::response::{Html, IntoResponse, Response}; -use axum::routing::get; +use axum::routing::{get, post}; use axum::{Extension, Router}; use serde_json::{Map, Value as JsonValue, json}; -use tokio::sync::{broadcast, watch}; +use tokio::sync::{Notify, broadcast, watch}; use tokio::time::Instant; use crate::locks::MutexExt; @@ -60,6 +60,8 @@ struct Hub { /// `{"paths": [...]}`, or `{"all": true}` if too many or not known. documents: broadcast::Sender>, control: Mutex, + /// Someone asked for it now (`refresh`): a tick at once, members asked again. + poke: Notify, /// True once the node stops: every client gets an `offline` event, and its stream /// ends (so the HTTP server needn't wait for clients to go). closing: watch::Receiver, @@ -81,6 +83,7 @@ impl Hub { latest, patches, documents, + poke: Notify::new(), control: Mutex::new(Control { running: false, wanted_until: Instant::now(), @@ -151,7 +154,13 @@ impl Hub { }))); last = Some(snapshot); } - tokio::time::sleep(TICK).await; + let poked = tokio::select! { + () = tokio::time::sleep(TICK) => false, + () = self.poke.notified() => true, + }; + if poked { + graph_due = Instant::now(); + } if let Some(changed) = document_changes(&mut changes) { let _ = self.documents.send(Arc::new(changed.to_string())); } @@ -360,6 +369,7 @@ pub(crate) fn router(state: AppState, closing: watch::Receiver) -> Router< .route("/admin/dashboard", get(page_html)) .route("/admin/dashboard/state", get(current_state)) .route("/admin/dashboard/events", get(events)) + .route("/admin/dashboard/refresh", post(refresh)) .layer(Extension(Hub::new(state, closing))) } @@ -389,6 +399,14 @@ async fn page_html() -> Html<&'static str> { Html(page().1.as_str()) } +/// `POST /admin/dashboard/refresh`: a tick now rather than at the next `TICK`, and the +/// members asked for their links again; what changed comes as events, as ever. +async fn refresh(Extension(hub): Extension>) -> StatusCode { + hub.want(); + hub.poke.notify_one(); + StatusCode::NO_CONTENT +} + /// `GET /admin/dashboard/state`: the latest snapshot, for a client that wants it whole. async fn current_state(Extension(hub): Extension>) -> Response { hub.want(); -- cgit v1.3.1