ponfw/renderer/index.html
Jon Vanvik ea251c3498 Remove edge glow; prefill MCMS URL from env; drop self-signed-cert option
- Remove the animated neon edge-glow entirely (HTML element + CSS) — it
  couldn't be made to follow the border reliably on iOS Safari.
- Add PFW_MCMS_URL: the web server prefills the login "Host URL" field with
  it (injected as the input's value in the served index; still editable).
  Documented in env.example + web README.
- Remove the "Accept self-signed TLS certificate" option: the checkbox is
  gone and both backends now always use rejectUnauthorized:true — MCMS must
  present a valid certificate. Dropped the acceptSelfSigned plumbing from the
  renderer and both login handlers.

Docs: CLAUDE.md (drop edge-glow section, update login note) + web README.

Verified: node --check; no stale refs (in-self-signed / acceptSelfSigned /
edge-glow / beamspin); served index carries value="…" only when
PFW_MCMS_URL is set, and no self-signed checkbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:49:54 +02:00

413 lines
16 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>PON Fleet Upgrader</title>
<link rel="stylesheet" href="app.css" />
</head>
<body>
<header class="topbar">
<div class="brand">PON Fleet Upgrader</div>
<nav id="tabs" class="tabs hidden">
<button class="tab" data-tab="dashboard" data-view="#view-dashboard">Dashboard</button>
<button class="tab" data-tab="onu" data-view="#view-onu-info">ONU</button>
<button class="tab" data-tab="olt" data-view="#view-olts">OLT</button>
</nav>
<div class="session" id="session-label">Not connected</div>
<button id="btn-logout" class="ghost hidden">Disconnect</button>
</header>
<!-- ================= Login view ================= -->
<section id="view-login" class="view">
<div class="card">
<h1>Connect to MCMS</h1>
<p class="muted">
Session-cookie auth against a Ciena MicroClimate Management System.
The host should be the root URL of the PON Manager web UI
(e.g. <code>https://mcms.example.net</code>).
</p>
<label>Host URL<input id="in-host" type="url" placeholder="https://mcms.example.net" /></label>
<label>Email / username<input id="in-user" type="text" autocomplete="username" /></label>
<label>Password<input id="in-pass" type="password" autocomplete="current-password" /></label>
<div class="row">
<button id="btn-login" class="primary">Connect</button>
<span id="login-error" class="error"></span>
</div>
</div>
</section>
<!-- ================= Dashboard view ================= -->
<section id="view-dashboard" class="view hidden">
<div class="panel-main dash-wrap">
<div class="fleet-head">
<h2>Dashboard</h2>
<div class="row">
<span id="dash-cache" class="dash-cache" title=""></span>
<label class="inline dash-toggle">
<input id="dash-extra" type="checkbox" />
Detailed stats (Rx / FEC scan)
</label>
<button id="dash-refresh" class="ghost">↻ Refresh</button>
</div>
</div>
<p class="muted small" id="dash-status">Connect and the network overview loads here.</p>
<div class="dash-scroll">
<div class="dash-grid" id="dash-tiles"></div>
<div id="dash-sections"></div>
</div>
</div>
</section>
<!-- ================= ONU info / stats view ================= -->
<section id="view-onu-info" class="view hidden">
<div class="panel-left">
<nav class="subtabs">
<button class="subtab" data-subview="#view-onu-info">Info / stats</button>
<button class="subtab" data-subview="#view-fleet">Fleet &amp; upgrade</button>
</nav>
<h2>Find ONU</h2>
<label>Search (name / address / serial)
<input id="onu-info-search" type="search" placeholder="e.g. ORKANGER or GNXS05…" />
</label>
<label>Equipment / version contains
<input id="onu-info-eq" type="search" placeholder="e.g. FT-XGS2110 or EV051" />
</label>
<label>PON mode
<select id="onu-info-pon">
<option value="">Any</option>
<option value="GPON">GPON</option>
<option value="XGS-PON">XGS-PON</option>
<option value="10G-EPON">10G-EPON</option>
</select>
</label>
<label>Registration status
<select id="onu-info-status">
<option value="">Any</option>
<option value="__down__">Down (Dereg / Dying Gasp / Disabled)</option>
<option value="Registered">Registered</option>
<option value="Deregistered">Deregistered</option>
<option value="Dying Gasp">Dying Gasp</option>
<option value="Disabled">Disabled</option>
<option value="__unknown__">Unknown (no OLT state)</option>
</select>
</label>
<button id="onu-info-load" class="primary">Load fleet</button>
<p class="muted small" id="onu-info-status-line">Load the fleet, then pick an ONU.</p>
<div class="onu-info-list" id="onu-info-list"></div>
</div>
<div class="panel-main">
<div id="onu-info-detail" class="onu-detail">
<p class="muted">Select an ONU on the left to see its stats and the CPE (customer router) behind it.</p>
</div>
</div>
</section>
<!-- ================= Fleet view ================= -->
<section id="view-fleet" class="view hidden">
<div class="panel-left">
<nav class="subtabs">
<button class="subtab" data-subview="#view-onu-info">Info / stats</button>
<button class="subtab" data-subview="#view-fleet">Fleet &amp; upgrade</button>
</nav>
<h2>Filters</h2>
<label>Name / address contains
<input id="filter-text" type="search" placeholder="e.g. ORKANGER" />
</label>
<label>Equipment ID contains
<input id="filter-equipment" type="text" placeholder="e.g. FT-XGS2110" />
</label>
<label>PON mode
<select id="filter-pon">
<option value="">Any</option>
<option value="GPON">GPON</option>
<option value="XGS-PON">XGS-PON</option>
<option value="10G-EPON">10G-EPON</option>
</select>
</label>
<label>Active version matches
<input id="filter-version" type="text" placeholder="e.g. EV05110R" />
</label>
<label>Exclude active version
<input id="filter-exclude-version" type="text" placeholder="e.g. EV05120R (hide already-upgraded)" />
</label>
<label>Model / version family
<input id="filter-model" type="text" placeholder="e.g. EV051 (Interos Everest 5.11.x)" />
</label>
<label>Registration status
<select id="filter-status">
<option value="">Any</option>
<option value="__down__">Down (Deregistered / Dying Gasp / Disabled)</option>
<option value="Registered">Registered</option>
<option value="Deregistered">Deregistered</option>
<option value="Dying Gasp">Dying Gasp</option>
<option value="Disabled">Disabled</option>
<option value="Disallowed Admin">Disallowed Admin</option>
<option value="Disallowed Error">Disallowed Error</option>
<option value="Disallowed Reg ID">Disallowed Reg ID</option>
<option value="Unspecified">Unspecified</option>
<option value="Unprovisioned">Unprovisioned</option>
<option value="__unknown__">Unknown (no OLT state)</option>
</select>
</label>
<label>Down for at least (days)
<input id="filter-down-days" type="number" min="0" step="1" placeholder="e.g. 30" />
</label>
<button id="btn-refresh" class="primary">Load fleet</button>
<p class="muted small" id="fleet-status"></p>
<h2>Selection</h2>
<div class="selected-summary">
<div><strong id="sel-count">0</strong> ONUs selected</div>
<div class="row">
<button id="btn-select-all">Select all matching</button>
<button id="btn-select-none" class="ghost">Clear</button>
</div>
</div>
<button id="btn-to-campaign" class="primary" disabled>Continue to upgrade →</button>
<button id="btn-delete-selected" class="danger" disabled>Delete selected from MCMS…</button>
<p class="muted small" id="delete-status"></p>
<h2>After-upgrade verification</h2>
<p class="muted small">Open a saved <code>pon-upgrade-*.csv</code> to compare the recorded FEC + optical readings against the current state of those ONUs.</p>
<button id="btn-to-verify" class="ghost">Verify previous upgrade…</button>
<h2>OLT flooding mode</h2>
<p class="muted small">Bulk-check or fix PON flooding mode on OLT NNI services. Default rule: match <code>s0.c76.c0</code> with mode <code>private</code> and switch to <code>auto</code>.</p>
<button id="btn-to-olts" class="ghost">Open OLT inspector…</button>
</div>
<div class="panel-main">
<div class="fleet-head">
<h2>Fleet (<span id="fleet-count">0</span>)</h2>
<div class="row small muted">
<span>Tip: use filters to narrow to one model before selecting all.</span>
</div>
</div>
<div class="fleet-table-wrap">
<table class="fleet-table" id="fleet-table">
<thead>
<tr>
<th><input type="checkbox" id="th-check" /></th>
<th>Serial</th>
<th>Name / address</th>
<th>Equipment ID</th>
<th>PON</th>
<th>Status</th>
<th>Last seen</th>
<th>Active bank</th>
<th>Active version</th>
<th>Inactive version</th>
</tr>
</thead>
<tbody id="fleet-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ================= Upgrade campaign view ================= -->
<section id="view-campaign" class="view hidden">
<div class="panel-left">
<h2>Campaign</h2>
<label>Target firmware file
<select id="fw-select"></select>
</label>
<label>Target version
<input id="fw-version" type="text" placeholder="e.g. EV05120R" />
</label>
<div class="row">
<button id="btn-upload-fw" class="ghost">Upload .bin…</button>
<button id="btn-reload-fw" class="ghost">Refresh list</button>
</div>
<h2>Execution</h2>
<label>Mode
<select id="mode-select">
<option value="task">Bulk task (Procedure 8, recommended)</option>
<option value="peronu">Per-ONU PUT (Procedure 7)</option>
</select>
</label>
<label id="row-schedule">Scheduled start (UTC)
<input id="in-schedule" type="datetime-local" />
</label>
<p class="muted small">
Writes always go to the inactive bank (FW Bank Ptr toggled to <code>1 active</code>, or <code>1</code> if unset).
MCMS flips the pointer when the download completes; the ONU reboots into the new image on its next reboot.
</p>
<div class="row">
<button id="btn-back" class="ghost">← Back to fleet</button>
<button id="btn-preview" class="primary">Preview plan</button>
</div>
<div class="row">
<button id="btn-execute" class="danger" disabled>Execute upgrade</button>
</div>
<p id="exec-status" class="small muted"></p>
</div>
<div class="panel-main">
<h2>Dry-run preview</h2>
<p class="muted small">Nothing has been written yet. Review the plan before clicking Execute.</p>
<div class="fleet-table-wrap">
<table class="fleet-table">
<thead>
<tr>
<th>Serial</th>
<th>Name</th>
<th>Active → new active slot</th>
<th>Current slot 0</th>
<th>Current slot 1</th>
<th>Target version</th>
<th>FEC health</th>
<th>Status</th>
</tr>
</thead>
<tbody id="preview-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ================= Verify view ================= -->
<section id="view-verify" class="view hidden">
<div class="panel-left">
<h2>Verify previous upgrade</h2>
<p class="muted small">
Pick a saved upgrade-plan CSV (defaults to <code>~/Downloads</code>).
The app re-fetches each ONU's current FEC counters, optical levels,
and active firmware version, then computes a before/after comparison
and saves a verification CSV alongside the original.
</p>
<button id="btn-pick-plan" class="primary">Open plan CSV…</button>
<p class="muted small" id="verify-status"></p>
<h2>Summary</h2>
<div class="selected-summary" id="verify-summary">
<div class="muted small">Open a CSV to start.</div>
</div>
<div class="row">
<button id="btn-verify-back" class="ghost">← Back to fleet</button>
<button id="btn-save-verify" class="primary" disabled>Re-save report</button>
</div>
<p id="verify-save-status" class="small muted"></p>
</div>
<div class="panel-main">
<div class="fleet-head">
<h2>Comparison (<span id="verify-count">0</span>)</h2>
<div class="row small muted">
<span>Note: ONU FEC counters typically reset on the upgrade reboot, so "after &lt; before" is normal. Look at the FEC flag and verdict columns for the actual signal.</span>
</div>
</div>
<div class="fleet-table-wrap">
<table class="fleet-table" id="verify-table">
<thead>
<tr>
<th>Serial</th>
<th>Name</th>
<th>Target → Active now</th>
<th>Applied?</th>
<th>FEC before</th>
<th>FEC now</th>
<th>Optical (RX/TX dBm)</th>
<th>Verdict</th>
</tr>
</thead>
<tbody id="verify-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ================= OLT inspector view ================= -->
<section id="view-olts" class="view hidden">
<div class="panel-left">
<h2>Filters</h2>
<label>NNI TAG MATCH pattern
<input id="olt-tag-pattern" type="text" value="s0.c76.c0" placeholder="e.g. s0.c76.c0 or s0.c76.*" />
</label>
<label>Flood-mode filter
<select id="olt-mode-filter">
<option value="any" selected>Any</option>
<option value="private">Private (PON FLOOD ID set)</option>
<option value="auto">Auto (no PON FLOOD ID)</option>
</select>
</label>
<label>OLT name contains
<input id="olt-name-filter" type="search" placeholder="e.g. OLT5" />
</label>
<button id="btn-load-olts" class="primary">Load OLTs</button>
<p class="muted small" id="olt-status"></p>
<h2>Selection</h2>
<div class="selected-summary">
<div><strong id="olt-sel-count">0</strong> OLT(s) selected · <strong id="olt-svc-count">0</strong> service slot(s) will change</div>
<div class="row">
<button id="btn-olt-select-all">Select all matching</button>
<button id="btn-olt-select-none" class="ghost">Clear</button>
</div>
</div>
<h2>Actions</h2>
<p class="muted small">Applied together to every matching NNI service on the selected OLTs, in one GET→edit→PUT per OLT.</p>
<label>Flooding mode
<select id="olt-flood-action">
<option value="">No change</option>
<option value="private2auto" selected>Private → Auto (remove PON FLOOD ID)</option>
<option value="auto2private">Auto → Private (set PON FLOOD ID = 0)</option>
</select>
</label>
<label>DHCPv4 filter
<select id="olt-dhcpv4-action">
<option value="">No change</option>
<option value="pass2umt" selected>pass → umt</option>
<option value="umt2pass">umt → pass</option>
</select>
</label>
<label>DHCPv6 filter
<select id="olt-dhcpv6-action">
<option value="">No change</option>
<option value="pass2umt" selected>pass → umt</option>
<option value="umt2pass">umt → pass</option>
</select>
</label>
<div class="row">
<button id="btn-olts-back" class="ghost">← Back to fleet</button>
<button id="btn-olt-execute" class="danger" disabled>Execute change…</button>
</div>
<p id="olt-exec-status" class="small muted"></p>
</div>
<div class="panel-main">
<div class="fleet-head">
<h2>OLTs (<span id="olt-count">0</span> shown, <span id="olt-total">0</span> total)</h2>
<div class="row small muted">
<span>Each row shows the matching NNI services. Selecting an OLT queues every matching service on it for change.</span>
</div>
</div>
<div class="fleet-table-wrap">
<table class="fleet-table" id="olt-table">
<thead>
<tr>
<th><input type="checkbox" id="olt-th-check" /></th>
<th>OLT MAC</th>
<th>Name</th>
<th>PON mode</th>
<th>FW</th>
<th>Matching NNI services</th>
</tr>
</thead>
<tbody id="olt-tbody"></tbody>
</table>
</div>
</div>
</section>
<script src="app.js"></script>
</body>
</html>