mockforge_ui/
lib.rs

1//! # MockForge UI
2//!
3//! Web-based admin interface for managing mock servers.
4
5pub mod handlers;
6pub mod routes;
7// Templates module removed; static assets in `static/` are the single source of truth
8pub mod models;
9pub mod prometheus_client;
10pub mod time_travel_handlers;
11
12pub use models::{RequestLog, RouteInfo, ServerStatus, SystemInfo};
13pub use routes::create_admin_router;
14
15use std::net::SocketAddr;
16
17/// Start the admin UI server
18///
19/// # Arguments
20/// * `addr` - Address to bind the admin server to
21/// * `http_server_addr` - HTTP server address
22/// * `ws_server_addr` - WebSocket server address
23/// * `grpc_server_addr` - gRPC server address
24/// * `graphql_server_addr` - GraphQL server address
25/// * `api_enabled` - Whether API endpoints are enabled
26/// * `prometheus_url` - Prometheus metrics URL
27/// * `chaos_api_state` - Optional chaos API state for hot-reload support
28/// * `latency_injector` - Optional latency injector for hot-reload support
29/// * `mockai` - Optional MockAI instance for hot-reload support
30/// * `continuum_config` - Optional Reality Continuum configuration
31/// * `virtual_clock` - Optional virtual clock for time-based progression
32pub async fn start_admin_server(
33    addr: SocketAddr,
34    http_server_addr: Option<SocketAddr>,
35    ws_server_addr: Option<SocketAddr>,
36    grpc_server_addr: Option<SocketAddr>,
37    graphql_server_addr: Option<SocketAddr>,
38    api_enabled: bool,
39    prometheus_url: String,
40    chaos_api_state: Option<std::sync::Arc<mockforge_chaos::api::ChaosApiState>>,
41    latency_injector: Option<
42        std::sync::Arc<tokio::sync::RwLock<mockforge_core::latency::LatencyInjector>>,
43    >,
44    mockai: Option<
45        std::sync::Arc<tokio::sync::RwLock<mockforge_core::intelligent_behavior::MockAI>>,
46    >,
47    continuum_config: Option<mockforge_core::ContinuumConfig>,
48    virtual_clock: Option<std::sync::Arc<mockforge_core::VirtualClock>>,
49) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
50    let app = create_admin_router(
51        http_server_addr,
52        ws_server_addr,
53        grpc_server_addr,
54        graphql_server_addr,
55        api_enabled,
56        addr.port(),
57        prometheus_url,
58        chaos_api_state,
59        latency_injector,
60        mockai,
61        continuum_config,
62        virtual_clock,
63    );
64
65    tracing::info!("Starting MockForge Admin UI on {}", addr);
66
67    let listener = tokio::net::TcpListener::bind(addr).await.map_err(|e| {
68        format!(
69            "Failed to bind Admin UI server to port {}: {}\n\
70             Hint: The port may already be in use. Try using a different port with --admin-port or check if another process is using this port with: lsof -i :{} or netstat -tulpn | grep {}",
71            addr.port(), e, addr.port(), addr.port()
72        )
73    })?;
74
75    axum::serve(listener, app).await?;
76
77    Ok(())
78}
79
80/// Get React UI HTML content
81pub fn get_admin_html() -> &'static str {
82    include_str!("../ui/dist/index.html")
83}
84
85/// Get React UI CSS content
86pub fn get_admin_css() -> &'static str {
87    include_str!("../ui/dist/assets/index.css")
88}
89
90/// Get React UI JavaScript content
91pub fn get_admin_js() -> &'static str {
92    include_str!("../ui/dist/assets/index.js")
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_get_admin_html() {
101        let html = get_admin_html();
102        assert!(!html.is_empty());
103        assert!(html.contains("<!DOCTYPE html>") || html.contains("<html"));
104    }
105
106    #[test]
107    fn test_get_admin_css() {
108        let css = get_admin_css();
109        assert!(!css.is_empty());
110    }
111
112    #[test]
113    fn test_get_admin_js() {
114        let js = get_admin_js();
115        assert!(!js.is_empty());
116    }
117}