Adds web/deploy/install.sh — full LXC setup in one command. It derives the repo path from its own location (clone anywhere; /opt/ponfw recommended), installs git/node/npm if missing (checks Node >= 18), creates the ponfw service user, runs npm install --omit=dev, writes /etc/pon-fleet-web.env on first run, and installs+enables the systemd unit with WorkingDirectory set to the checkout. Idempotent; warns if cloned under /home (ProtectHome blocks it). README + CLAUDE.md §16 updated to lead with install.sh and clarify the clone location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
161 lines
7.1 KiB
Markdown
161 lines
7.1 KiB
Markdown
# 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 — `0.0.0.0` if the proxy is on another host |
|
|
| `PFW_CACHE_TTL_SECONDS` | `60` | shared upstream cache TTL; `0` disables (see Caching) |
|
|
| `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) |
|
|
|
|
## Caching (fewer MCMS requests)
|
|
|
|
The server keeps a small in-memory cache so multiple operators don't each
|
|
hammer MCMS. The heavy bulk **reads** — ONU configs, ONU states, OLT states,
|
|
OLT configs, controller configs, firmware inventory — are cached with a TTL
|
|
(`PFW_CACHE_TTL_SECONDS`, default 60s) and **keyed by MCMS host**, so:
|
|
|
|
- Everyone pointed at the same MCMS shares one snapshot. Ten operators
|
|
loading the fleet within a minute trigger **one** upstream fetch, not ten.
|
|
- A **single-flight** guard means even simultaneous cold loads collapse into
|
|
one upstream request (the rest await it).
|
|
- Different MCMS hosts never share, and the dashboard's light path reuses the
|
|
same OLT-STATE snapshot the fleet view already fetched.
|
|
|
|
What is **not** cached (always live, by design):
|
|
|
|
- Per-ONU `getOnuConfig` used right before a firmware PUT (read-modify-write
|
|
must see the current bank state).
|
|
- The FEC pre-flight and verify per-ONU `getOnuState` (operators want the
|
|
reading at decision time).
|
|
|
|
**Writes invalidate** the affected datasets for that MCMS host, so a delete /
|
|
firmware push / flood-mode change is reflected on the next load instead of
|
|
waiting out the TTL. Tune the TTL up to cut MCMS load further, or set
|
|
`PFW_CACHE_TTL_SECONDS=0` to disable caching entirely. `POST /api/_cacheStats`
|
|
(authenticated) shows hits/misses/coalesced; `POST /api/_cacheClear` drops the
|
|
caller's MCMS-host snapshots.
|
|
|
|
## Deploy on a Debian 13 LXC (clone-to-run)
|
|
|
|
Files in `web/deploy/`: `install.sh` (one-shot setup), `update.sh`, a systemd
|
|
unit, and an env template.
|
|
|
|
**Where to clone:** anywhere — `install.sh` points the service at wherever it
|
|
finds itself. `/opt/ponfw` is recommended: it's the FHS spot for this kind of
|
|
software, and the hardened unit sets `ProtectHome=true`, which would block the
|
|
service from reading a checkout under `/home`.
|
|
|
|
```bash
|
|
# --- as root on the LXC, once ---
|
|
git clone ssh://forgejo@int.git.vanvikinternet.no/Svorka/ponfw.git /opt/ponfw
|
|
sudo /opt/ponfw/web/deploy/install.sh
|
|
$EDITOR /etc/pon-fleet-web.env # optional: HOST / PFW_SECURE_COOKIE / TTL
|
|
sudo systemctl restart pon-fleet-web # if you edited the env
|
|
```
|
|
|
|
`install.sh` is idempotent and:
|
|
|
|
- installs `git` / `nodejs` / `npm` if missing (and checks Node ≥ 18),
|
|
- creates the unprivileged `ponfw` system user,
|
|
- runs `npm install --omit=dev` (tough-cookie only; skips Electron),
|
|
- writes `/etc/pon-fleet-web.env` from the template on first run,
|
|
- installs the systemd unit with `WorkingDirectory` set to this checkout,
|
|
then enables + starts it,
|
|
- prints the LXC IP and the NPM forward target.
|
|
|
|
The checkout is root-owned and read-only to the service; the service runs as
|
|
`ponfw` and writes nothing to disk.
|
|
|
|
```bash
|
|
journalctl -u pon-fleet-web -f # logs
|
|
sudo /opt/ponfw/web/deploy/update.sh # update: git pull + npm install + restart
|
|
```
|
|
|
|
## Behind Nginx Proxy Manager
|
|
|
|
Point an NPM **Proxy Host** at the server:
|
|
|
|
- Forward Hostname/IP: the LXC's address · Forward Port: `8080`
|
|
- Scheme: `http` (NPM terminates TLS; request an SSL cert in NPM)
|
|
- Enable **Block Common Exploits**; add your **Access List** for auth/IP
|
|
allow-listing (this is your access control — the app itself has no user
|
|
directory beyond MCMS login).
|
|
- **Websockets Support** on is harmless. The progress streams (bulk delete,
|
|
FEC pre-flight, flood change) are plain NDJSON over HTTP and already send
|
|
`X-Accel-Buffering: no`, which nginx/NPM honour, so they stream live with
|
|
no extra config. If you ever see progress arrive only at the end, add to
|
|
the host's **Advanced** tab:
|
|
|
|
```nginx
|
|
proxy_buffering off;
|
|
proxy_read_timeout 300s;
|
|
```
|
|
|
|
If NPM runs on a different host than this LXC, set `HOST=0.0.0.0` in
|
|
`/etc/pon-fleet-web.env` and restrict reachability to the NPM host via the
|
|
LXC firewall + the NPM access list.
|
|
|
|
## 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.
|