mbproxy: close out the dashboard code-review minor findings
Resolves the remaining Minor items from the 2026-05-15 review so the web-UI dashboard work has no open follow-ups: a real-HubConnection end-to-end test for the SignalR feed, stable mbproxy.admin.broadcast.* log-event names, keyboard/aria accessibility on the fleet table, frontend JS hardening (URL-decode guard, NaN guards, shared util.js), reconciler<->capture-registry coverage, throwing-sink and embedded-asset tests, broadcaster polish, and a soft upper bound on AdminPushIntervalMs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -119,9 +119,9 @@ Server-push cadence (milliseconds) for the admin dashboard's SignalR feed. Every
|
||||
|
||||
| Field | Type | Default | Range |
|
||||
|-------|------|---------|-------|
|
||||
| `AdminPushIntervalMs` | int | `1000` | `> 0` |
|
||||
| `AdminPushIntervalMs` | int | `1000` | `1`–`60000` |
|
||||
|
||||
`MbproxyOptionsValidator` and `ReloadValidator` both reject values `<= 0`. The broadcaster additionally floors the effective interval at 100 ms. Source: `MbproxyOptions.AdminPushIntervalMs`.
|
||||
`MbproxyOptionsValidator` and `ReloadValidator` both reject values outside `1`–`60000` ms — the upper bound is a soft guard against a typo (e.g. a seconds value pasted as milliseconds) that would make the "live" feed effectively non-live. The broadcaster additionally floors the effective interval at 100 ms. Source: `MbproxyOptions.AdminPushIntervalMs`.
|
||||
|
||||
## `Mbproxy.Plcs[]`
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ The UI is a Bootstrap 5 single-page app served from embedded assets under `src/M
|
||||
|
||||
1. **App bar** — service version, formatted uptime, accepted-reload count, and a live SignalR connection-state pill.
|
||||
2. **Aggregate strip** — six cards: listeners bound/configured, total connected clients, fleet PDU/s (rate derived client-side from successive snapshots), PLCs in `recovering`, total backend exceptions, fleet cache hit ratio. The recovering / exceptions cards highlight when non-zero.
|
||||
3. **KPI table** — one row per configured PLC, Tier-1 columns only: PLC name, backend `host:listenPort`, state chip (`bound` green / `recovering` amber / `stopped` grey), clients, PDU/s, RTT ms, exception total, coalesce %, cache %, keepalive. The table is client-side filterable (name/host search, state, "problems only") and sortable. Clicking a row opens that PLC's detail page in a new tab.
|
||||
3. **KPI table** — one row per configured PLC, Tier-1 columns only: PLC name, backend `host:listenPort`, state chip (`bound` green / `recovering` amber / `stopped` grey), clients, PDU/s, RTT ms, exception total, coalesce %, cache %, keepalive. The table is client-side filterable (name/host search, state, "problems only") and sortable — column headers are keyboard-operable (Tab to focus, Enter/Space to sort) and carry `aria-sort`. The PLC name is a link that opens that PLC's detail page in a new tab.
|
||||
|
||||
### Connection detail (`GET /plc/{name}`)
|
||||
|
||||
|
||||
@@ -138,6 +138,48 @@ Fires when the admin endpoint cannot bind its configured `AdminPort`. The servic
|
||||
|
||||
**Operator action:** change `Mbproxy:AdminPort` in `appsettings.json` to a free port. Hot-reload picks up the change; the admin endpoint rebinds without a service restart.
|
||||
|
||||
### mbproxy.admin.broadcast.snapshot.failed
|
||||
|
||||
**Level:** Error · **EventId:** 72 · **Source:** `src/Mbproxy/Admin/StatusBroadcaster.cs`
|
||||
|
||||
No structured properties; the exception is attached.
|
||||
|
||||
Fires when the live-dashboard push loop cannot build a status snapshot. The current push cycle is skipped; the loop retries on the next interval. The proxy data path is unaffected.
|
||||
|
||||
**Operator action:** none if isolated. A sustained rate means the status-snapshot builder is consistently throwing — capture the attached exception and investigate.
|
||||
|
||||
### mbproxy.admin.broadcast.fleet.failed
|
||||
|
||||
**Level:** Error · **EventId:** 73 · **Source:** `src/Mbproxy/Admin/StatusBroadcaster.cs`
|
||||
|
||||
No structured properties; the exception is attached.
|
||||
|
||||
Fires when the push loop fails to deliver the fleet snapshot to dashboard subscribers (a SignalR transport fault). The loop continues; per-PLC detail pushes are still attempted.
|
||||
|
||||
**Operator action:** none if isolated. Sustained occurrences mean the SignalR feed is unhealthy — the dashboard's "live" feed is stale even though the proxy is fine.
|
||||
|
||||
### mbproxy.admin.broadcast.detail.failed
|
||||
|
||||
**Level:** Error · **EventId:** 74 · **Source:** `src/Mbproxy/Admin/StatusBroadcaster.cs`
|
||||
|
||||
| Property | Type | Meaning |
|
||||
|----------|------|---------|
|
||||
| `Plc` | `string` | Configured PLC name whose detail push failed. |
|
||||
|
||||
Fires when the push loop fails to deliver a per-PLC detail snapshot to that PLC's detail-page subscribers. The loop continues with the remaining PLCs.
|
||||
|
||||
**Operator action:** none if isolated. Sustained occurrences for one `Plc` mean that PLC's detail page is not receiving live updates.
|
||||
|
||||
### mbproxy.admin.broadcast.loop.terminated
|
||||
|
||||
**Level:** Error · **EventId:** 75 · **Source:** `src/Mbproxy/Admin/StatusBroadcaster.cs`
|
||||
|
||||
No structured properties; the exception is attached.
|
||||
|
||||
Fires when the live-dashboard push loop itself terminates on an unhandled exception (not the expected cancellation at shutdown). The dashboard's live feed stops entirely until the admin endpoint is rebound (an `AdminPort` hot-reload restarts the loop).
|
||||
|
||||
**Operator action:** alert. The live feed is dead; capture the attached exception and restart the admin endpoint (toggle `Mbproxy:AdminPort`) or the service.
|
||||
|
||||
### mbproxy.shutdown.complete
|
||||
|
||||
**Level:** Information · **EventId:** 80 · **Source:** `src/Mbproxy/Diagnostics/ShutdownCoordinator.cs`
|
||||
@@ -531,7 +573,7 @@ Each subsystem owns a single `*LogEvents.cs` static partial class with `[LoggerM
|
||||
- `src/Mbproxy/Proxy/Cache/CacheLogEvents.cs` — response cache.
|
||||
- `src/Mbproxy/Proxy/RewriterLogEvents.cs` — BCD rewriting and exception passthrough.
|
||||
|
||||
Lifecycle events (`startup.*`, `listener.*`, `admin.*`, `shutdown.*`, `config.reload.*`) live as private `[LoggerMessage]` declarations next to the class that emits them — see `ProxyWorker.cs`, `PlcListener.cs`, `PlcListenerSupervisor.cs`, `AdminEndpointHost.cs`, `ShutdownCoordinator.cs`, and `ConfigReconciler.cs`. New subsystems should follow the `*LogEvents.cs` pattern when they accumulate more than two events.
|
||||
Lifecycle events (`startup.*`, `listener.*`, `admin.*`, `shutdown.*`, `config.reload.*`) live as private `[LoggerMessage]` declarations next to the class that emits them — see `ProxyWorker.cs`, `PlcListener.cs`, `PlcListenerSupervisor.cs`, `AdminEndpointHost.cs`, `StatusBroadcaster.cs` (the `admin.broadcast.*` family), `ShutdownCoordinator.cs`, and `ConfigReconciler.cs`. New subsystems should follow the `*LogEvents.cs` pattern when they accumulate more than two events.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
|
||||
Reference in New Issue
Block a user