From 1762fa250f1e2588c05f85afd4dd6df7f923de3c Mon Sep 17 00:00:00 2001 From: markmental Date: Wed, 11 Feb 2026 14:17:18 -0500 Subject: [PATCH] add --listen-open flag for web LAN binding --- DEVLOG.md | 10 ++++++++++ README.md | 7 +++++++ src/main.rs | 12 +++++++++++- src/web.rs | 19 +++++++++++++++---- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/DEVLOG.md b/DEVLOG.md index f01ed97..2cb27c4 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -96,3 +96,13 @@ - Reused Rust simulation engine and instance lifecycle for backend execution. - Rebuilt `index.html` and `data.js` as a modern web dashboard UI backed by Rust APIs. - Removed legacy client-side simulation engine from the browser path. + +## 2026-02-11 - Open listen option for web mode + +### Scope completed +- Added `--listen-open` CLI argument for web mode. +- Enforced `--listen-open` usage only when `--web` is present. +- Updated web server bind address behavior: + - default: `127.0.0.1:9009` + - open listen: `0.0.0.0:9009` +- Updated startup logs and README usage examples for LAN-accessible mode. diff --git a/README.md b/README.md index ca9a6e1..3cb2e22 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,16 @@ Then open: http://127.0.0.1:9009 ``` +Open web mode on all network interfaces (`0.0.0.0:9009`) so other machines can access it: + +```bash +cargo run -- --web --listen-open +``` + Notes: - The web frontend (`index.html` + `data.js`) now uses Rust backend APIs. - Simulation logic runs server-side in Rust (shared with CLI/TUI engine). +- `--listen-open` is only valid with `--web` and should be used on trusted networks. ### Quick match (headless) diff --git a/src/main.rs b/src/main.rs index 486278c..2ecef92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,9 @@ struct Cli { #[arg(long, global = true)] web: bool, + #[arg(long, global = true)] + listen_open: bool, + #[command(subcommand)] command: Option, } @@ -76,6 +79,13 @@ fn main() -> io::Result<()> { let cli = Cli::parse(); let base_seed = cli.seed.unwrap_or_else(|| Rng::from_time().next_u64()); + if cli.listen_open && !cli.web { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "--listen-open can only be used with --web", + )); + } + if cli.web { if cli.command.is_some() { return Err(io::Error::new( @@ -83,7 +93,7 @@ fn main() -> io::Result<()> { "--web cannot be combined with subcommands", )); } - return run_web_server(base_seed, cli.speed); + return run_web_server(base_seed, cli.speed, cli.listen_open); } match cli.command { diff --git a/src/web.rs b/src/web.rs index 05fac25..cbf75ce 100644 --- a/src/web.rs +++ b/src/web.rs @@ -457,17 +457,28 @@ async fn api_export_csv(path: web::Path, state: web::Data) - .body(csv) } -pub fn run_web_server(base_seed: u64, speed: Speed) -> io::Result<()> { +pub fn run_web_server(base_seed: u64, speed: Speed, listen_open: bool) -> io::Result<()> { let shared = SharedState { inner: Arc::new(Mutex::new(WebState::new(base_seed, speed))), }; let ticker = shared.clone(); + let bind_host = if listen_open { "0.0.0.0" } else { "127.0.0.1" }; + let display_host = if listen_open { + "" + } else { + "127.0.0.1" + }; println!( - "Starting SoccerCloud web UI at http://127.0.0.1:{WEB_PORT} (seed={base_seed}, speed={})", - speed.label() + "Starting SoccerCloud web UI at http://{display_host}:{WEB_PORT} (bound {bind_host}:{WEB_PORT}, seed={base_seed}, speed={})", + speed.label(), ); + if listen_open { + println!( + "Open listen enabled: accessible from other machines on your network at http://:{WEB_PORT}" + ); + } actix_web::rt::System::new().block_on(async move { actix_web::rt::spawn(async move { @@ -507,7 +518,7 @@ pub fn run_web_server(base_seed: u64, speed: Speed) -> io::Result<()> { ), ) }) - .bind(("127.0.0.1", WEB_PORT))? + .bind((bind_host, WEB_PORT))? .run() .await })