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
18pub async fn start_admin_server(
19    addr: SocketAddr,
20    http_server_addr: Option<SocketAddr>,
21    ws_server_addr: Option<SocketAddr>,
22    grpc_server_addr: Option<SocketAddr>,
23    graphql_server_addr: Option<SocketAddr>,
24    api_enabled: bool,
25    prometheus_url: String,
26) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
27    let app = create_admin_router(
28        http_server_addr,
29        ws_server_addr,
30        grpc_server_addr,
31        graphql_server_addr,
32        api_enabled,
33        addr.port(),
34        prometheus_url,
35    );
36
37    tracing::info!("Starting MockForge Admin UI on {}", addr);
38
39    let listener = tokio::net::TcpListener::bind(addr).await.map_err(|e| {
40        format!(
41            "Failed to bind Admin UI server to port {}: {}\n\
42             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 {}",
43            addr.port(), e, addr.port(), addr.port()
44        )
45    })?;
46
47    axum::serve(listener, app).await?;
48
49    Ok(())
50}
51
52/// Get React UI HTML content
53pub fn get_admin_html() -> &'static str {
54    include_str!("../ui/dist/index.html")
55}
56
57/// Get React UI CSS content
58pub fn get_admin_css() -> &'static str {
59    include_str!("../ui/dist/assets/index.css")
60}
61
62/// Get React UI JavaScript content
63pub fn get_admin_js() -> &'static str {
64    include_str!("../ui/dist/assets/index.js")
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_get_admin_html() {
73        let html = get_admin_html();
74        assert!(!html.is_empty());
75        assert!(html.contains("<!DOCTYPE html>") || html.contains("<html"));
76    }
77
78    #[test]
79    fn test_get_admin_css() {
80        let css = get_admin_css();
81        assert!(!css.is_empty());
82    }
83
84    #[test]
85    fn test_get_admin_js() {
86        let js = get_admin_js();
87        assert!(!js.is_empty());
88    }
89}