dashboard: filter /dashboard/stream by ?kinds= allow-list (#408)
This commit is contained in:
parent
f540584553
commit
d143abb63d
2 changed files with 193 additions and 1 deletions
|
|
@ -798,14 +798,49 @@ async fn dashboard_history(State(state): State<AppState>) -> Response {
|
|||
}
|
||||
}
|
||||
|
||||
/// `/dashboard/stream` query string. Today's only field is `kinds`
|
||||
/// (#408): a comma-separated allow-list of event-`kind` strings.
|
||||
/// Empty / absent ⇒ no filter (current behaviour, all variants
|
||||
/// forwarded). Set ⇒ only the named kinds reach the subscriber,
|
||||
/// non-matches are skipped before the JSON serialise cost.
|
||||
///
|
||||
/// Useful for narrow pages (e.g. `flow.js` only cares about `sent`
|
||||
/// / `delivered` / `container_state_changed` / `container_removed`)
|
||||
/// that want to drop the dispatch overhead on every unrelated mutation.
|
||||
#[derive(Deserialize, Default)]
|
||||
struct DashboardStreamQuery {
|
||||
/// Comma-separated event kinds to forward. Each token is
|
||||
/// trimmed; unknown kinds are silently ignored on lookup
|
||||
/// (subscriber sees nothing instead of an error).
|
||||
kinds: Option<String>,
|
||||
}
|
||||
|
||||
async fn dashboard_stream(
|
||||
State(state): State<AppState>,
|
||||
axum::extract::Query(q): axum::extract::Query<DashboardStreamQuery>,
|
||||
) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
|
||||
let rx = state.coord.dashboard_subscribe();
|
||||
let stream = BroadcastStream::new(rx).filter_map(|res| {
|
||||
// Pre-parse the allow-list once at subscription time, so the
|
||||
// per-event hot path is just a `HashSet::contains` on a
|
||||
// `&'static str` — no string churn per frame.
|
||||
let kind_filter: Option<std::collections::HashSet<String>> = q.kinds.and_then(|raw| {
|
||||
let set: std::collections::HashSet<String> = raw
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(str::to_owned)
|
||||
.collect();
|
||||
if set.is_empty() { None } else { Some(set) }
|
||||
});
|
||||
let stream = BroadcastStream::new(rx).filter_map(move |res| {
|
||||
// Drop lagged frames. Browsers reconnect; the seq dedupe on
|
||||
// reconnect skips any frame already reflected in the snapshot.
|
||||
let event = res.ok()?;
|
||||
if let Some(filter) = kind_filter.as_ref()
|
||||
&& !filter.contains(event.kind_tag())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let json = serde_json::to_string(&event).ok()?;
|
||||
Some(Ok(Event::default().data(json)))
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue