Skip to main content

crdt_dev_ui/
lib.rs

1//! # crdt-dev-ui
2//!
3//! Embedded web panel for inspecting [`crdt-kit`] databases during development.
4//!
5//! Launches a lightweight local web server (Axum) that serves a single-page
6//! dashboard for browsing namespaces, entities, events, and migration metadata.
7//!
8//! ## Quick Start
9//!
10//! ```no_run
11//! #[tokio::main]
12//! async fn main() {
13//!     crdt_dev_ui::start("app.db", 4242).await.unwrap();
14//! }
15//! ```
16
17mod api;
18
19use std::sync::Arc;
20
21use axum::{routing::get, Router};
22use crdt_store::SqliteStore;
23
24/// Shared application state for Axum handlers.
25pub(crate) struct AppState {
26    pub store: SqliteStore,
27    pub db_path: String,
28}
29
30/// Start the Dev UI web server.
31///
32/// Opens the SQLite database at `db_path` and serves the inspection panel
33/// on `http://127.0.0.1:{port}`. This function blocks until the server is
34/// shut down (Ctrl-C).
35pub async fn start(db_path: &str, port: u16) -> Result<(), Box<dyn std::error::Error>> {
36    let store = SqliteStore::open(db_path)?;
37    let state = Arc::new(AppState {
38        store,
39        db_path: db_path.to_string(),
40    });
41
42    let app = Router::new()
43        .route("/", get(api::index))
44        .route("/api/status", get(api::status))
45        .route("/api/namespaces/{ns}/entities", get(api::list_entities))
46        .route("/api/namespaces/{ns}/entities/{key}", get(api::get_entity))
47        .route(
48            "/api/namespaces/{ns}/entities/{key}/events",
49            get(api::get_events),
50        )
51        .with_state(state);
52
53    let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{port}")).await?;
54    eprintln!("  Dev UI: http://localhost:{port}");
55    axum::serve(listener, app).await?;
56    Ok(())
57}