dashboard: optional matrix GUI static mount at /matrix (#607 v0)

This commit is contained in:
damocles 2026-05-29 21:40:05 +02:00
commit 360f2af15f
3 changed files with 69 additions and 0 deletions

View file

@ -51,6 +51,17 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
);
}
tracing::info!(static_dir = %static_dir.display(), "dashboard static dir resolved");
// Optional matrix-GUI static dist (#607). When `HIVE_MATRIX_GUI_DIR`
// is set and the dir exists, mount it at `/matrix/` as a sibling
// ServeDir before the SPA fallback. Pre-#15 (nginx front) this
// is the simplest way to put fluffychat-web on the same origin as
// the dashboard so the operator has matrix chat one tab away.
let matrix_gui_dir: Option<PathBuf> = std::env::var_os("HIVE_MATRIX_GUI_DIR")
.map(PathBuf::from)
.filter(|p| p.is_dir());
if let Some(dir) = &matrix_gui_dir {
tracing::info!(matrix_gui_dir = %dir.display(), "matrix GUI mount enabled at /matrix");
}
let app = Router::new()
.route("/api/state", get(api_state))
.route("/approve/{id}", post(post_approve))
@ -92,6 +103,14 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
// /static/dashboard.css → dist/static/dashboard.css, etc.).
.fallback_service(ServeDir::new(&static_dir))
.with_state(AppState { coord });
// Sibling static mount for the optional matrix GUI dist. Layered
// before the SPA fallback so `/matrix/...` resolves there instead
// of falling through to the dashboard.
let app = if let Some(dir) = matrix_gui_dir {
app.nest_service("/matrix", ServeDir::new(dir))
} else {
app
};
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = bind_with_retry(addr).await?;
tracing::info!(%port, "dashboard listening");
@ -223,6 +242,11 @@ struct StateSnapshot {
/// links each container's config + each approval's commit into the
/// forge's `agent-configs` repos.
forge_present: bool,
/// Whether the matrix GUI static dist is mounted at `/matrix/`
/// (`HIVE_MATRIX_GUI_DIR` set + dir exists). Drives the dashboard's
/// `M4TR1X →` tab-strip entry so it doesn't flash a dead link when
/// the operator hasn't opted in (#607).
matrix_gui_enabled: bool,
}
/// `OpQuestion` + computed `question_refs` / `answer_refs`. Built
@ -415,6 +439,9 @@ async fn api_state(headers: HeaderMap, State(state): State<AppState>) -> axum::J
port_conflicts,
rebuild_queue: state.coord.rebuild_queue.snapshot(),
forge_present: crate::forge::is_present().await,
matrix_gui_enabled: std::env::var_os("HIVE_MATRIX_GUI_DIR")
.map(PathBuf::from)
.is_some_and(|p| p.is_dir()),
})
}