Sidecar: WebSocket live feed
web.rs adds the website-facing HTTP surface (axum): GET /health and GET /ws. Every shard event is broadcast to all connected WebSocket clients as a JSON text frame. main.rs's event loop now broadcast::sends each event after logging it. A lagging client is warned and kept live (misses events) rather than stalling the others; a dead-but-not-closed socket is caught by a 30s server ping. The feed is live-only -- no replay -- since history belongs to REST + SQLite. This side may be exposed beyond loopback (it is the gatekeeper); it defaults to 127.0.0.1:8080 and wants auth before going public. Verified end to end: a WebSocket client connected to /ws, received ws.hello, then live pong events relayed from the shard through the shard-link -> broadcast -> ws path -- the same path a login, sale, or IDOC alert will take to a browser. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,14 +23,17 @@ Binds `127.0.0.1:7788` and waits for the shard to connect. Boot the shard (or it
|
||||
| Piece | State |
|
||||
|-------|-------|
|
||||
| Shard link (`shard.rs`) | **done** — accepts the shard, reads events, sends commands, re-accepts on disconnect. Verified against the live shard: received `server.hello`, round-tripped a `ping`→`pong`, and reconnected after a sidecar restart. |
|
||||
| WebSocket feed (website ← live events) | not started |
|
||||
| WebSocket feed (`web.rs`) | **done** — `/ws` fans every shard event out to connected clients via a `broadcast`. Verified: a WS client received `ws.hello` then live `pong` events relayed from the shard. Live-only, no replay. |
|
||||
| REST queries (char profile, roster, vendor snapshot, link submit) | not started |
|
||||
| SQLite persistence (event history, economy, cached profiles, link map) | not started |
|
||||
|
||||
The web server binds `127.0.0.1:8080` by default (`WEB_ADDR` in `main.rs`). Routes: `GET /health` → `ok`, `GET /ws` → the live feed. Widen the bind and add auth before exposing it off-host.
|
||||
|
||||
## Design
|
||||
|
||||
- **`shard.rs`** — `serve()` binds the listener and accepts shard connections in a loop. Each connection splits into read/write halves: the read half parses newline-JSON into `ShardEvent { kind, value }` and forwards them; the write half drains an mpsc of command lines. `ShardHandle::send` posts a command to whichever shard is currently connected, and **drops with a warning if none is** — a website query during a shard outage should fail fast and retry, not queue behind a reconnect. Live *events* that must survive an outage are buffered by the shard, not here.
|
||||
- **`main.rs`** — wires it together and, for now, logs events by kind. Later phases fan `ShardEvent`s out to the WebSocket broadcaster and SQLite, and turn REST calls into shard commands via `ShardHandle`.
|
||||
- **`web.rs`** — the website-facing HTTP surface (axum). `AppState` holds the `broadcast::Sender<String>`; each `/ws` client subscribes and forwards every event as a text frame. A client that lags past the broadcast buffer is warned and kept live (it just misses events) rather than stalling the others. This side *may* be exposed beyond loopback — it is the gatekeeper, so add auth when you do.
|
||||
- **`main.rs`** — wires it together: the shard event loop logs each event and `broadcast::send`s it to the WS feed. Later phases also persist to SQLite here and turn REST calls into shard commands via `ShardHandle`.
|
||||
|
||||
## Wire protocol
|
||||
|
||||
|
||||
Reference in New Issue
Block a user