From 98dbf3d06d4225cac425b5db8803055e30f8d6bb Mon Sep 17 00:00:00 2001 From: naps62-yolo Date: Tue, 26 May 2026 18:23:04 +0000 Subject: [PATCH] feat(maestro): add [web] section to unified config (NAP-241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebSection holds bind_addr, daemon_url, and dev_proxy_target — all optional so a partial file is still valid. Bumps the workspace deps with include_dir, mime_guess, bytes; adds `stream` to reqwest features for proxying bodies. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 106 +++++++++++++++++++++++++++++++ Cargo.toml | 5 +- crates/maestro-config/src/lib.rs | 32 ++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0665ca8..d2da7e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,6 +454,23 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -473,8 +490,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -788,6 +808,25 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1028,6 +1067,28 @@ dependencies = [ "uuid", ] +[[package]] +name = "maestro-web" +version = "0.1.0" +dependencies = [ + "axum", + "bytes", + "clap", + "dirs", + "futures-util", + "include_dir", + "maestro-config", + "mime_guess", + "reqwest", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite 0.21.0", + "tracing", + "tracing-subscriber", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1055,6 +1116,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "mio" version = "1.2.0" @@ -1365,6 +1436,7 @@ dependencies = [ "cookie", "cookie_store", "futures-core", + "futures-util", "http", "http-body", "http-body-util", @@ -1384,12 +1456,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls 0.26.4", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 1.0.7", ] @@ -1935,6 +2009,19 @@ dependencies = [ "tungstenite 0.24.0", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.23" @@ -2135,6 +2222,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -2327,6 +2420,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" diff --git a/Cargo.toml b/Cargo.toml index a167ca8..178dd90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,11 @@ maestro-config = { path = "crates/maestro-config" } maestro-protocol = { path = "crates/maestro-protocol" } nix = { version = "0.29", default-features = false, features = ["term", "signal"] } rand = "0.8" -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "cookies"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "cookies", "stream"] } rust-embed = "8" +include_dir = "0.7" +mime_guess = "2" +bytes = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" tempfile = "3" diff --git a/crates/maestro-config/src/lib.rs b/crates/maestro-config/src/lib.rs index 2efd33a..8462982 100644 --- a/crates/maestro-config/src/lib.rs +++ b/crates/maestro-config/src/lib.rs @@ -74,6 +74,8 @@ pub struct MaestroConfigFile { pub daemon: DaemonSection, #[serde(default, skip_serializing_if = "RegistrySection::is_empty")] pub registry: RegistrySection, + #[serde(default, skip_serializing_if = "WebSection::is_empty")] + pub web: WebSection, } /// Fields consumed by more than one binary (CLI + daemon, daemon + registry). @@ -166,6 +168,31 @@ impl RegistrySection { } } +/// `[web]` — `maestro-web` binary settings (NAP-241). +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct WebSection { + /// Address `maestro-web` binds. Default `127.0.0.1:8080`. Bind a + /// perimeter-facing address (LAN/WG) to share with other devices on a + /// trusted segment; do NOT bind a public address without adding auth. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bind_addr: Option, + /// HTTP base URL of the co-located daemon `maestro-web` proxies to. + /// Default `http://127.0.0.1:7070`. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub daemon_url: Option, + /// When set, requests for `/` are proxied to a Vite dev server at this + /// URL instead of being served from the embedded bundle. Use it during + /// frontend development so live reload works. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dev_proxy_target: Option, +} + +impl WebSection { + fn is_empty(&self) -> bool { + self.bind_addr.is_none() && self.daemon_url.is_none() && self.dev_proxy_target.is_none() + } +} + impl MaestroConfigFile { /// Strict load — error with [`ConfigError::MissingFile`] if absent. pub fn load(path: &Path) -> Result { @@ -288,6 +315,11 @@ mod tests { registry: RegistrySection { bind_addr: Some("0.0.0.0:8080".parse().unwrap()), }, + web: WebSection { + bind_addr: Some("127.0.0.1:8080".parse().unwrap()), + daemon_url: Some("http://127.0.0.1:7070".into()), + dev_proxy_target: Some("http://127.0.0.1:5173".into()), + }, }; cfg.save(&path).unwrap(); assert_eq!(MaestroConfigFile::load(&path).unwrap(), cfg);