2026-06-24 09:30:29 +00:00
|
|
|
pub mod datastream_source;
|
2026-02-13 14:42:10 +00:00
|
|
|
pub mod history;
|
2026-06-23 15:42:28 +00:00
|
|
|
mod html;
|
2026-06-24 09:30:29 +00:00
|
|
|
mod layer;
|
2026-02-24 09:12:28 +00:00
|
|
|
pub mod plugin;
|
2026-02-09 09:04:57 +00:00
|
|
|
mod server;
|
2026-06-23 15:42:28 +00:00
|
|
|
pub mod telemetry;
|
2026-02-13 14:42:10 +00:00
|
|
|
pub mod topology;
|
2026-06-23 15:42:28 +00:00
|
|
|
pub mod warnings;
|
2026-06-24 09:30:29 +00:00
|
|
|
pub use crate::layer::{DashboardEvent, EventStore};
|
2026-06-05 07:25:43 +00:00
|
|
|
|
|
|
|
|
/// The canonical Distribution page (the SWIM connection-graph view). Owned by
|
|
|
|
|
/// the dashboard crate so every front-end that serves it — a live node's
|
|
|
|
|
/// `DistributionPlugin` and the datastream dashboard — renders the exact same
|
2026-06-09 09:29:07 +00:00
|
|
|
/// page and chrome, fed by the distribution-page JSON the datastream consumer
|
|
|
|
|
/// reconstructs (see [`crate::datastream_source`]).
|
2026-06-05 07:25:43 +00:00
|
|
|
pub const DISTRIBUTION_PAGE_HTML: &str = include_str!("distribution_page.html");
|
|
|
|
|
|
2026-02-09 09:04:57 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
refactor: remove dead code, consolidate files (#52)
Remove the unused TCP transport and dead CLI/simulation scaffolding and collapse scattered single-purpose modules into consolidated files across the dashboard, process, and simulation crates.
- crates/transport: drop the TCP transport (src/tcp.rs and its tcp feature), leaving only ed25519 identity and encoding utilities behind the iroh transport
- crates/dashboard: collapse actor_detail_html/actors_html/dashboard_html/topology_html into a single html.rs, drop command/parse.rs, and inline the trace types into lib.rs
- crates/process: fold driver, pipeline_types, queue, subscriber, waker, and local pipes/signal/wait into local/mod.rs, pipeline.rs, and a unified types.rs, consolidating the public re-exports
- crates/simulation: remove the ci subdirectory (local_sim, sim) and dead node/config/trace modules, and flatten the distribution subdirectory into top-level files
- crates/datastore: remove the unused store_cli, cli, and in-memory storage, and deduplicate crypto.rs across datastore and distribution (about 190 lines of shared code removed)
- crates/distribution: drop dead codec code and trim the messages module
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-25 14:33:04 +00:00
|
|
|
use swactor::stats::RuntimeStats;
|
|
|
|
|
|
2026-02-13 14:42:10 +00:00
|
|
|
use crate::history::{DashboardHistory, HistoryConfig};
|
2026-06-24 09:30:29 +00:00
|
|
|
use crate::layer::now_ms;
|
2026-02-24 09:12:28 +00:00
|
|
|
use crate::plugin::PluginRegistry;
|
refactor: remove dead code, consolidate files (#52)
Remove the unused TCP transport and dead CLI/simulation scaffolding and collapse scattered single-purpose modules into consolidated files across the dashboard, process, and simulation crates.
- crates/transport: drop the TCP transport (src/tcp.rs and its tcp feature), leaving only ed25519 identity and encoding utilities behind the iroh transport
- crates/dashboard: collapse actor_detail_html/actors_html/dashboard_html/topology_html into a single html.rs, drop command/parse.rs, and inline the trace types into lib.rs
- crates/process: fold driver, pipeline_types, queue, subscriber, waker, and local pipes/signal/wait into local/mod.rs, pipeline.rs, and a unified types.rs, consolidating the public re-exports
- crates/simulation: remove the ci subdirectory (local_sim, sim) and dead node/config/trace modules, and flatten the distribution subdirectory into top-level files
- crates/datastore: remove the unused store_cli, cli, and in-memory storage, and deduplicate crypto.rs across datastore and distribution (about 190 lines of shared code removed)
- crates/distribution: drop dead codec code and trim the messages module
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-25 14:33:04 +00:00
|
|
|
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Configuration for the datastream-backed dashboard.
|
2026-02-09 09:04:57 +00:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct DashboardConfig {
|
|
|
|
|
pub port: u16,
|
|
|
|
|
pub event_capacity: usize,
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Enable full activity-log retention inside the event store.
|
2026-02-09 09:04:57 +00:00
|
|
|
pub record: bool,
|
2026-02-11 15:23:26 +00:00
|
|
|
/// Maximum events retained in the recording log. Only used when `record = true`.
|
|
|
|
|
pub record_event_capacity: usize,
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for DashboardConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
port: 9090,
|
|
|
|
|
event_capacity: 10_000,
|
|
|
|
|
record: false,
|
2026-02-11 15:23:26 +00:00
|
|
|
record_event_capacity: 100_000,
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Handle to a running datastream-backed dashboard.
|
2026-02-09 09:04:57 +00:00
|
|
|
pub struct DashboardHandle {
|
|
|
|
|
store: Arc<EventStore>,
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Externally pushed stats from the datastream fold (latest wins).
|
2026-06-05 07:25:43 +00:00
|
|
|
pushed_stats: Arc<Mutex<Option<RuntimeStats>>>,
|
2026-02-09 09:04:57 +00:00
|
|
|
shutdown: Arc<AtomicBool>,
|
2026-02-19 14:39:33 +00:00
|
|
|
shutdown_notify: Arc<tokio::sync::Notify>,
|
2026-02-13 14:42:10 +00:00
|
|
|
history: Arc<DashboardHistory>,
|
2026-02-19 14:39:33 +00:00
|
|
|
port: u16,
|
2026-02-24 09:12:28 +00:00
|
|
|
plugin_registry: Arc<PluginRegistry>,
|
2026-02-19 14:39:33 +00:00
|
|
|
standalone_rt: Mutex<Option<tokio::runtime::Runtime>>,
|
2026-06-06 17:53:25 +00:00
|
|
|
/// Optional override for the `/` landing page (e.g. a host serving a fleet
|
2026-06-24 09:30:29 +00:00
|
|
|
/// board instead of the single-node actor dashboard).
|
2026-05-29 08:55:10 +00:00
|
|
|
landing_html: Mutex<Option<Arc<str>>>,
|
2026-06-06 17:53:25 +00:00
|
|
|
/// Optional extra axum router merged into the live server, for hosts that
|
|
|
|
|
/// add disjoint routes of their own.
|
2026-05-29 08:55:10 +00:00
|
|
|
extra_router: Mutex<Option<axum::Router>>,
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DashboardHandle {
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Push a stats snapshot from the datastream fold (latest wins).
|
2026-06-05 07:25:43 +00:00
|
|
|
pub fn set_stats(&self, stats: RuntimeStats) {
|
|
|
|
|
*self.pushed_stats.lock().unwrap() = Some(stats);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Push a dashboard activity line into the SSE event stream.
|
|
|
|
|
pub fn push_activity(&self, is_warn: bool, message: impl Into<String>) {
|
|
|
|
|
self.store.push(DashboardEvent {
|
|
|
|
|
seq: 0,
|
|
|
|
|
timestamp_ms: now_ms(),
|
|
|
|
|
level: if is_warn { "WARN" } else { "INFO" }.to_string(),
|
|
|
|
|
message: message.into(),
|
|
|
|
|
worker_id: None,
|
|
|
|
|
actor_addr: None,
|
|
|
|
|
fields: serde_json::Map::new(),
|
|
|
|
|
});
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
/// Register a plugin with the dashboard.
|
|
|
|
|
pub fn register_plugin(&self, plugin: Arc<dyn plugin::DashboardPlugin>) {
|
|
|
|
|
self.plugin_registry.register(plugin);
|
2026-02-19 14:39:33 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-29 08:55:10 +00:00
|
|
|
/// Override the `/` landing page with custom HTML. Used when the dashboard
|
2026-06-24 09:30:29 +00:00
|
|
|
/// shows a fleet board rather than the single-node actor dashboard.
|
2026-05-29 08:55:10 +00:00
|
|
|
pub fn set_landing_html(&self, html: impl Into<Arc<str>>) {
|
|
|
|
|
*self.landing_html.lock().unwrap() = Some(html.into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Merge an extra axum router into the live HTTP server. The routes must be
|
2026-06-24 09:30:29 +00:00
|
|
|
/// disjoint from the dashboard's own routes. Must be called before
|
|
|
|
|
/// `start_http`/`start_http_standalone`.
|
2026-05-29 08:55:10 +00:00
|
|
|
pub fn set_extra_router(&self, router: axum::Router) {
|
|
|
|
|
*self.extra_router.lock().unwrap() = Some(router);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Access the time-series history store.
|
2026-02-13 14:42:10 +00:00
|
|
|
pub fn history(&self) -> &Arc<DashboardHistory> {
|
|
|
|
|
&self.history
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 09:04:57 +00:00
|
|
|
/// Signal the dashboard to shut down (SSE clients receive "done").
|
|
|
|
|
pub fn shutdown(&self) {
|
|
|
|
|
self.shutdown.store(true, Ordering::Release);
|
2026-02-19 14:39:33 +00:00
|
|
|
self.shutdown_notify.notify_waiters();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start the HTTP server on the provided tokio handle.
|
|
|
|
|
/// Use this when a tokio runtime already exists (e.g. IrohDriver's runtime).
|
|
|
|
|
pub fn start_http(&self, handle: tokio::runtime::Handle) {
|
|
|
|
|
let state = self.build_app_state();
|
2026-05-29 08:55:10 +00:00
|
|
|
let extra = self.extra_router.lock().unwrap().take();
|
2026-02-19 14:39:33 +00:00
|
|
|
let port = self.port;
|
|
|
|
|
handle.spawn(async move {
|
2026-05-29 08:55:10 +00:00
|
|
|
server::run_server(state, port, extra).await;
|
2026-02-19 14:39:33 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start the HTTP server on a standalone tokio runtime (1 worker thread).
|
refactor: remove dead code, consolidate files (#52)
Remove the unused TCP transport and dead CLI/simulation scaffolding and collapse scattered single-purpose modules into consolidated files across the dashboard, process, and simulation crates.
- crates/transport: drop the TCP transport (src/tcp.rs and its tcp feature), leaving only ed25519 identity and encoding utilities behind the iroh transport
- crates/dashboard: collapse actor_detail_html/actors_html/dashboard_html/topology_html into a single html.rs, drop command/parse.rs, and inline the trace types into lib.rs
- crates/process: fold driver, pipeline_types, queue, subscriber, waker, and local pipes/signal/wait into local/mod.rs, pipeline.rs, and a unified types.rs, consolidating the public re-exports
- crates/simulation: remove the ci subdirectory (local_sim, sim) and dead node/config/trace modules, and flatten the distribution subdirectory into top-level files
- crates/datastore: remove the unused store_cli, cli, and in-memory storage, and deduplicate crypto.rs across datastore and distribution (about 190 lines of shared code removed)
- crates/distribution: drop dead codec code and trim the messages module
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-25 14:33:04 +00:00
|
|
|
/// Use this when no external tokio runtime is available (e.g. non-async transport).
|
2026-02-19 14:39:33 +00:00
|
|
|
pub fn start_http_standalone(&self) {
|
|
|
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
|
|
|
.worker_threads(1)
|
|
|
|
|
.enable_all()
|
|
|
|
|
.build()
|
|
|
|
|
.expect("failed to create tokio runtime for dashboard HTTP");
|
|
|
|
|
let handle = rt.handle().clone();
|
|
|
|
|
*self.standalone_rt.lock().unwrap() = Some(rt);
|
|
|
|
|
self.start_http(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_app_state(&self) -> server::AppState {
|
|
|
|
|
server::AppState {
|
|
|
|
|
store: Arc::clone(&self.store),
|
2026-06-05 07:25:43 +00:00
|
|
|
pushed_stats: Arc::clone(&self.pushed_stats),
|
2026-02-19 14:39:33 +00:00
|
|
|
shutdown: Arc::clone(&self.shutdown),
|
|
|
|
|
shutdown_notify: Arc::clone(&self.shutdown_notify),
|
|
|
|
|
history: Arc::clone(&self.history),
|
2026-02-24 09:12:28 +00:00
|
|
|
plugins: Arc::clone(&self.plugin_registry),
|
2026-05-29 08:55:10 +00:00
|
|
|
landing: self.landing_html.lock().unwrap().clone(),
|
2026-02-19 14:39:33 +00:00
|
|
|
}
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-19 14:39:33 +00:00
|
|
|
/// Start a dashboard and return a handle.
|
2026-02-09 09:04:57 +00:00
|
|
|
///
|
2026-02-19 14:39:33 +00:00
|
|
|
/// The dashboard state is created immediately but the HTTP server is NOT started.
|
|
|
|
|
/// Call `start_http()` or `start_http_standalone()` to begin serving.
|
2026-06-24 09:30:29 +00:00
|
|
|
/// Push datastream-folded stats with `set_stats()`; HTTP/SSE reads only those
|
|
|
|
|
/// snapshots and registered plugins.
|
2026-02-09 09:04:57 +00:00
|
|
|
pub fn start_dashboard(config: DashboardConfig) -> DashboardHandle {
|
2026-02-11 15:23:26 +00:00
|
|
|
let store = Arc::new(EventStore::new(
|
|
|
|
|
config.event_capacity,
|
|
|
|
|
config.record,
|
|
|
|
|
config.record_event_capacity,
|
|
|
|
|
));
|
2026-06-05 07:25:43 +00:00
|
|
|
let pushed_stats: Arc<Mutex<Option<RuntimeStats>>> = Arc::new(Mutex::new(None));
|
2026-02-09 09:04:57 +00:00
|
|
|
let shutdown = Arc::new(AtomicBool::new(false));
|
2026-02-19 14:39:33 +00:00
|
|
|
let shutdown_notify = Arc::new(tokio::sync::Notify::new());
|
2026-02-13 14:42:10 +00:00
|
|
|
let history = Arc::new(DashboardHistory::new(HistoryConfig::default()));
|
2026-02-09 09:04:57 +00:00
|
|
|
|
2026-02-19 14:39:33 +00:00
|
|
|
let port = config.port;
|
2026-02-24 09:12:28 +00:00
|
|
|
let plugin_registry = Arc::new(PluginRegistry::new());
|
2026-02-09 09:04:57 +00:00
|
|
|
|
|
|
|
|
DashboardHandle {
|
|
|
|
|
store,
|
2026-06-05 07:25:43 +00:00
|
|
|
pushed_stats,
|
2026-02-09 09:04:57 +00:00
|
|
|
shutdown,
|
2026-02-19 14:39:33 +00:00
|
|
|
shutdown_notify,
|
2026-02-13 14:42:10 +00:00
|
|
|
history,
|
2026-02-19 14:39:33 +00:00
|
|
|
port,
|
2026-02-24 09:12:28 +00:00
|
|
|
plugin_registry,
|
2026-02-19 14:39:33 +00:00
|
|
|
standalone_rt: Mutex::new(None),
|
2026-05-29 08:55:10 +00:00
|
|
|
landing_html: Mutex::new(None),
|
|
|
|
|
extra_router: Mutex::new(None),
|
2026-02-09 09:04:57 +00:00
|
|
|
}
|
|
|
|
|
}
|