New crate crates/maestro-web/: axum server that * embeds the web/dist SPA bundle via include_dir! (graceful placeholder when pnpm build hasn't run yet — keeps fresh checkouts compiling) * reverse-proxies /api/* HTTP to a co-located daemon (default http://127.0.0.1:7070) via reqwest, stripping hop-by-hop headers * reverse-proxies /api/attach/:id and /api/events WebSockets via tokio_tungstenite::connect_async + a tokio task pumping both directions * reads bind_addr / daemon_url / dev_proxy_target from the new [web] config section; env-var overrides MAESTRO_WEB_BIND_ADDR / _DAEMON_URL / _DEV_PROXY_TARGET Integration tests spin up a mock daemon + maestro-web pair on random ports and verify HTTP forward, SPA fallback, WS echo, and 502 on unreachable daemon. Adds a top-level Justfile (`just web` = pnpm install + pnpm build + cargo build), crates/maestro-web/README.md (config + trust model + dev workflow), and a docs/INSTALL.md section on optional web UI install. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
84 lines
2.4 KiB
Markdown
84 lines
2.4 KiB
Markdown
# maestro-web
|
|
|
|
Standalone HTTP server that hosts the Maestro browser UI and reverse-proxies
|
|
to a co-located `maestrod`.
|
|
|
|
```
|
|
browser ──HTTP──▶ maestro-web (:8080) ──HTTP/WS──▶ maestrod (:7070)
|
|
│
|
|
└── / serves the embedded SPA (web/dist)
|
|
/api/... proxied unmodified
|
|
/api/attach/:id, /api/events proxied as WebSocket
|
|
```
|
|
|
|
## Trust model
|
|
|
|
`maestro-web` does not authenticate anyone. Whoever can reach the bind address
|
|
can use it. Pick the bind accordingly:
|
|
|
|
| Bind | Reach | Use when |
|
|
| --------------------- | ---------------------------------- | --------------------------- |
|
|
| `127.0.0.1:8080` | Local users on this host | Default. Same as tmux. |
|
|
| `<wg-ip>:8080` | Devices on your WireGuard mesh | Phone/tablet over VPN. |
|
|
| `<lan-ip>:8080` | Devices on your trusted LAN | Home / lab networks. |
|
|
| Public IP | Don't | — add real auth first. |
|
|
|
|
## Config
|
|
|
|
The binary reads `~/.config/maestro/config.toml`. The `[web]` section:
|
|
|
|
```toml
|
|
[web]
|
|
bind_addr = "127.0.0.1:8080" # default
|
|
daemon_url = "http://127.0.0.1:7070" # default
|
|
# dev_proxy_target = "http://127.0.0.1:5173" # only when developing the frontend
|
|
```
|
|
|
|
Env-var overrides (all optional):
|
|
|
|
- `MAESTRO_WEB_BIND_ADDR`
|
|
- `MAESTRO_WEB_DAEMON_URL`
|
|
- `MAESTRO_WEB_DEV_PROXY_TARGET`
|
|
- `MAESTRO_CONFIG` — override the unified config-file path.
|
|
|
|
## Build
|
|
|
|
The SPA bundle lives in `../../web` and is embedded into the binary at compile
|
|
time via `include_dir!`. Build the frontend first:
|
|
|
|
```sh
|
|
pnpm -C web install
|
|
pnpm -C web build
|
|
cargo build -p maestro-web --release
|
|
```
|
|
|
|
Or, from the repo root:
|
|
|
|
```sh
|
|
just web
|
|
```
|
|
|
|
A fresh checkout compiles even without `pnpm build` (an empty `web/dist`
|
|
exists in-tree); the binary just serves a placeholder until the bundle is
|
|
built.
|
|
|
|
## Run
|
|
|
|
```sh
|
|
target/release/maestro-web
|
|
```
|
|
|
|
Then point a browser at `http://127.0.0.1:8080/`.
|
|
|
|
## Development workflow for the frontend
|
|
|
|
Use Vite directly so you get HMR. From `web/`:
|
|
|
|
```sh
|
|
pnpm dev
|
|
```
|
|
|
|
Vite serves on `:5173` and proxies `/api/*` straight to `maestrod` at
|
|
`127.0.0.1:7070` (override with `MAESTRO_REGISTRY=...`). You don't need to
|
|
rebuild the Rust binary while iterating on the frontend.
|