seedelf_cli/
web_server.rs

1use colored::Colorize;
2use include_dir::{Dir, include_dir};
3use std::net::SocketAddr;
4use warp::Filter;
5
6const STATIC_DIR: Dir = include_dir!("static");
7
8/// Start a web server and inject a dynamic message into the HTML.
9///
10/// # Arguments
11/// - `message`: The dynamic message to replace in the `injected-data` script.
12pub async fn run_web_server(message: String, network_flag: bool) {
13    let addr: SocketAddr = ([127, 0, 0, 1], 44203).into();
14    println!(
15        "{} {}",
16        "\nStarting server at".bright_cyan(),
17        format!("http://{}/", addr).bright_white()
18    );
19    println!("{}", "Hit Ctrl-C To Stop Server".bright_yellow());
20
21    // Serve index.html with dynamic content
22    let html_route = warp::path::end().map(move || {
23        let html_file = STATIC_DIR
24            .get_file("index.html")
25            .expect("Failed to read HTML file");
26        let mut html = html_file
27            .contents_utf8()
28            .expect("Failed to read HTML")
29            .to_string();
30        // Replace the JSON content inside the injected-data script
31        let dynamic_json = format!(r#"{{ "message": "{}" }}"#, message);
32        html = html.replace(r#"{ "message": "ACAB000000000000" }"#, &dynamic_json);
33        if network_flag {
34            html = html.replace(
35                r#"{ "network": "FADECAFE00000000" }"#,
36                format!(r#"{{ "network": "{}" }}"#, "preprod.").as_str(),
37            );
38        } else {
39            html = html.replace(
40                r#"{ "network": "FADECAFE00000000" }"#,
41                r#"{ "network": "" }"#,
42            );
43        }
44        warp::reply::html(html)
45    });
46
47    // Serve index.js as a static file
48    let js_route = warp::path("index.js").map(|| {
49        let file = STATIC_DIR
50            .get_file("index.js")
51            .expect("JavaScript file not found");
52        warp::reply::with_header(file.contents(), "Content-Type", "application/javascript")
53    });
54
55    // Serve favicon.ico
56    let favicon_route = warp::path("favicon.ico").map(|| {
57        let file = STATIC_DIR
58            .get_file("favicon.ico")
59            .expect("Favicon not found");
60        warp::reply::with_header(file.contents(), "Content-Type", "image/x-icon")
61    });
62
63    // Serve index.css
64    let css_route = warp::path("index.css").map(|| {
65        let file = STATIC_DIR
66            .get_file("index.css")
67            .expect("CSS file not found");
68        warp::reply::with_header(file.contents(), "Content-Type", "text/css")
69    });
70
71    // Combine all routes
72    let routes = html_route.or(js_route).or(favicon_route).or(css_route);
73
74    // Run the server with graceful shutdown
75    let (_, server) = warp::serve(routes).bind_with_graceful_shutdown(addr, shutdown_signal());
76    server.await;
77
78    println!("{}", "Server has stopped.".bright_purple());
79}
80
81/// Function to handle graceful shutdown via Ctrl-C
82async fn shutdown_signal() {
83    tokio::signal::ctrl_c()
84        .await
85        .expect("Failed to install Ctrl-C handler");
86    println!("{}", "\nShutdown signal received...".red());
87}