Add multi-user web server variant (browser UI behind a reverse proxy)

New web/ subfolder: a Node http server that serves the SAME tooling as the
Electron app to any browser, with per-session MCMS credentials so several
operators can use it at once behind a TLS reverse proxy. Mobile-responsive.

Not a fork — it reuses the core and the UI:
- serves renderer/app.js + app.css verbatim and rewrites index.html on the
  fly (mobile viewport + browser window.api shim + responsive overlay)
- web/public/api-web.js: a drop-in window.api over fetch + NDJSON streaming;
  Electron file dialogs -> <input type=file>, CSV-to-Downloads -> Blob
- web/server.js: per-session McmsClient (cookie pfw_sid), idle expiry,
  routes mirroring the IPC handlers, NDJSON for the 6 streaming ops
- pure Node http, no new deps (borrows ../node_modules tough-cookie)

Shared improvements (benefit both apps):
- src/dashboard.js: extracted, pure dashboard aggregation (main.js now uses
  it); adds abnormal-Tx detection alongside abnormal-Rx
- renderer: dashboard health stats are now clickable — abnormal Rx / Tx /
  lasers-off pop a list of the offending devices (like the iOS app), via a
  new showListModal

Docs: web/README.md (run + reverse-proxy + security), CLAUDE.md §16.

Verified: node --check all; started the server and confirmed transformed
index, shared-asset serving, 401 auth gate, login (per-session client), and
NDJSON wiring; rendered login + dashboard drilldown + mobile over HTTP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jon Vanvik 2026-06-23 14:07:23 +02:00
parent 9106e5a071
commit 96b0264179
10 changed files with 1096 additions and 109 deletions

99
web/README.md Normal file
View file

@ -0,0 +1,99 @@
# PON Fleet — web server
A multi-user, browser-based front end for the same tooling as the Electron
**PON Fleet Upgrader** (Dashboard / ONU firmware upgrades / OLT PON
flooding mode). Built to sit behind a TLS-terminating reverse proxy so
several operators can use it at once, **each with their own MCMS login**.
## How it relates to the Electron app
It does **not** duplicate the app — it reuses it:
- **Core logic** comes straight from `../src/` (`mcms-api.js`,
`bank-strategy.js`, `dashboard.js`). One source of truth for every MCMS
quirk.
- **The UI is the same files.** The server serves `../renderer/app.js` and
`../renderer/app.css` as-is and rewrites `../renderer/index.html` on the
fly (adds a mobile viewport, swaps in the browser API shim + a small
responsive overlay). So the Electron app and the web app never drift.
- The only web-specific code is `server.js`, `public/api-web.js` (a
`window.api` shim: Electron IPC → `fetch` + NDJSON streaming, Electron
file dialogs → browser file pickers, CSV-to-Downloads → Blob download),
and `public/web.css` (responsive overlay).
Because of the `../` references this folder **must** live inside
`pon-fleet-upgrader/` (it borrows the already-installed `tough-cookie` from
`../node_modules`). No separate `npm install` is needed.
## Run
```bash
cd pon-fleet-upgrader/web
npm start # node server.js — listens on 127.0.0.1:8080
npm run check # node --check on server.js + api-web.js
```
Then browse to <http://127.0.0.1:8080>. Each browser logs in with its own
MCMS host + credentials; the server keeps a separate `McmsClient` (separate
cookie jar) per session.
### Configuration (env)
| Var | Default | Meaning |
|---|---|---|
| `PORT` | `8080` | listen port |
| `HOST` | `127.0.0.1` | bind address — keep on localhost behind a proxy |
| `PFW_IDLE_MINUTES` | `30` | idle-session expiry (logs the MCMS session out) |
| `PFW_SECURE_COOKIE` | off | force the `Secure` cookie flag (else inferred from `X-Forwarded-Proto: https`) |
| `PFW_VERBOSE` | off | verbose MCMS request logging (very noisy with many users) |
## Reverse proxy
Terminate TLS at the proxy and forward to `127.0.0.1:8080`. **Disable
response buffering** so the NDJSON progress streams (bulk delete, FEC
pre-flight, flood change, …) arrive live.
Caddy:
```
mcms-tools.example.net {
reverse_proxy 127.0.0.1:8080 {
flush_interval -1 # don't buffer streamed responses
}
}
```
nginx:
```nginx
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off; # stream NDJSON progress live
proxy_read_timeout 300s; # bulk ops can run a while
}
```
## Security notes
- The server holds each logged-in user's **live MCMS session cookie in
memory**. Run it behind HTTPS, keep `HOST=127.0.0.1`, and rely on the
idle timeout. The session cookie is `HttpOnly`, `SameSite=Lax`, and
`Secure` behind TLS.
- "Accept self-signed TLS certificate" disables verification **to MCMS**
for that session only (same as the desktop app).
- There's no app-level user directory — anyone who can reach the page and
has valid MCMS credentials can log in. Restrict network access at the
proxy (mTLS / SSO / IP allow-list) if you need more than MCMS auth.
- Several operators running bulk operations at once multiply load on MCMS;
the page-size-100 / timeout mitigations from the main `CLAUDE.md` §5
still apply per session.
## What differs from the desktop app
- **CSV files download through the browser** instead of auto-saving to
`~/Downloads` (a server can't write to each user's machine). Same
filenames, same BOM/CRLF/quoted format.
- Firmware `.bin` and plan-CSV selection use the browser file picker.
- Everything else — views, filters, FEC logic, flood-mode rules, the
dashboard and its drill-downs — is the shared renderer code.