add --listen-open flag for web LAN binding

This commit is contained in:
markmental 2026-02-11 14:17:18 -05:00
commit 1762fa250f
4 changed files with 43 additions and 5 deletions

View file

@ -96,3 +96,13 @@
- Reused Rust simulation engine and instance lifecycle for backend execution. - 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. - 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. - 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.

View file

@ -81,9 +81,16 @@ Then open:
http://127.0.0.1:9009 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: Notes:
- The web frontend (`index.html` + `data.js`) now uses Rust backend APIs. - The web frontend (`index.html` + `data.js`) now uses Rust backend APIs.
- Simulation logic runs server-side in Rust (shared with CLI/TUI engine). - 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) ### Quick match (headless)

View file

@ -32,6 +32,9 @@ struct Cli {
#[arg(long, global = true)] #[arg(long, global = true)]
web: bool, web: bool,
#[arg(long, global = true)]
listen_open: bool,
#[command(subcommand)] #[command(subcommand)]
command: Option<Commands>, command: Option<Commands>,
} }
@ -76,6 +79,13 @@ fn main() -> io::Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
let base_seed = cli.seed.unwrap_or_else(|| Rng::from_time().next_u64()); 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.web {
if cli.command.is_some() { if cli.command.is_some() {
return Err(io::Error::new( return Err(io::Error::new(
@ -83,7 +93,7 @@ fn main() -> io::Result<()> {
"--web cannot be combined with subcommands", "--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 { match cli.command {

View file

@ -457,17 +457,28 @@ async fn api_export_csv(path: web::Path<usize>, state: web::Data<SharedState>) -
.body(csv) .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 { let shared = SharedState {
inner: Arc::new(Mutex::new(WebState::new(base_seed, speed))), inner: Arc::new(Mutex::new(WebState::new(base_seed, speed))),
}; };
let ticker = shared.clone(); 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 {
"<machine-ip>"
} else {
"127.0.0.1"
};
println!( println!(
"Starting SoccerCloud web UI at http://127.0.0.1:{WEB_PORT} (seed={base_seed}, speed={})", "Starting SoccerCloud web UI at http://{display_host}:{WEB_PORT} (bound {bind_host}:{WEB_PORT}, seed={base_seed}, speed={})",
speed.label() speed.label(),
); );
if listen_open {
println!(
"Open listen enabled: accessible from other machines on your network at http://<machine-ip>:{WEB_PORT}"
);
}
actix_web::rt::System::new().block_on(async move { actix_web::rt::System::new().block_on(async move {
actix_web::rt::spawn(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() .run()
.await .await
}) })