diff --git a/server/src/dashboard.html b/server/src/dashboard.html
new file mode 100644
index 0000000..54a580d
--- /dev/null
+++ b/server/src/dashboard.html
@@ -0,0 +1,73 @@
+
+
+
+ Brain Monitor
+
+
+
+
+
+
+
+
+
+
diff --git a/server/src/main.rs b/server/src/main.rs
index 1e26993..75edd27 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -139,6 +139,32 @@ fn run_server(state: Arc) -> Result<(), Box>
let app = Router::new()
.route("/sse", get(sse_handler))
.route("/messages", post(message_handler))
+ .route("/health", get(health_handler))
+ .route("/dashboard", get(|| async move {
+ axum::response::Html(include_str!("dashboard.html"))
+ }))
+ .route("/api/stats", get({
+ let state_clone = app_state.handler.state.clone();
+ move || async move {
+ let (entities, relations) = {
+ let graph = state_clone.get_full_graph();
+ (graph.entities.len(), graph.relations.len())
+ };
+ let tasks = state_clone.tasks.read().len();
+ let snippets = state_clone.snippets.read().len();
+ let tech_debts = state_clone.tech_debts.read().len();
+ let adrs = state_clone.adrs.read().len();
+
+ axum::Json(serde_json::json!({
+ "entities": entities,
+ "relations": relations,
+ "tasks": tasks,
+ "snippets": snippets,
+ "tech_debts": tech_debts,
+ "adrs": adrs
+ }))
+ }
+ }))
.with_state(app_state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
@@ -162,6 +188,10 @@ async fn sse_handler(
Sse::new(stream).keep_alive(axum::response::sse::KeepAlive::new())
}
+async fn health_handler() -> &'static str {
+ "OK"
+}
+
#[derive(serde::Deserialize)]
struct SessionQuery {
#[serde(rename = "sessionId")]