Adds an ONU info page and splits the ONU tab into two sub-tabs (Info / stats, Fleet & upgrade). The ONU tab now lands on Info; campaign/verify keep the Fleet sub-tab lit. ONU info page (#view-onu-info): master-detail over the already-loaded fleet (search + status filter, no new bulk fetch). Per-ONU detail shows identity, link/optical + FEC (read from the merged ONU-STATE), firmware, and a CPE card. CPE (customer router): - McmsClient.getCpe — GET /api/cpe/onu/<id>/ (unversioned, [status,doc] tuple). - src/cpe.js (pure, shared): summarizeCpe + leaseHealth. Renewal rule — <50% of the lease remaining => "renewal overdue" (amber), past end => expired. v4 expiry = DHCP "Remove Time"; v6 spans Last Success -> Preferred Lifetime (T1 midpoint), with Prefix Delegation surfaced. - api:getOnuCpe (main.js + web/server.js, exposed in both bridges). OUI vendor (src/oui.js, pure, shared): lazy fetch of the IEEE oui.csv, prefix->org map, cached in memory ~30 days + best-effort to disk (PFW_CACHE_DIR / temp; PrivateTmp makes /tmp writable). Resolved server-side in getOnuCpe; every failure degrades to "unknown OUI". CPE itself is not TTL-cached (lease must be live); only the OUI list is. Dashboard abnormal Rx/Tx rows are now clickable -> deep-link to that ONU's info page (showListModal gained per-item onClick). Docs: CLAUDE.md §17 + IPC table + intro; README. Verified: node --check; cpe.js/oui.js unit-tested (15/15: lease ok/warn/ expired/unknown + 50% boundary, v6 prefix parse, OUI quoted-CSV parse + lookup); ONU info page + CPE card rendered offscreen over HTTP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81 lines
3.1 KiB
JavaScript
81 lines
3.1 KiB
JavaScript
// Preload: the only bridge between renderer and main. Everything the UI
|
|
// can do is enumerated here — the renderer has no direct node access.
|
|
|
|
const { contextBridge, ipcRenderer } = require('electron');
|
|
|
|
function call(channel, args) {
|
|
return ipcRenderer.invoke(channel, args);
|
|
}
|
|
|
|
contextBridge.exposeInMainWorld('api', {
|
|
login: (opts) => call('api:login', opts),
|
|
logout: () => call('api:logout'),
|
|
|
|
listOnuConfigs: (opts) => call('api:listOnuConfigs', opts),
|
|
listFleet: (opts) => call('api:listFleet', opts),
|
|
fetchStates: (opts) => call('api:fetchStates', opts),
|
|
getOnuConfig: (opts) => call('api:getOnuConfig', opts),
|
|
getOnuCpe: (opts) => call('api:getOnuCpe', opts),
|
|
listOnuFirmware: () => call('api:listOnuFirmware'),
|
|
uploadFirmware: () => call('api:uploadFirmware'),
|
|
|
|
planUpgrade: (opts) => call('api:planUpgrade', opts),
|
|
executePerOnu: (opts) => call('api:executePerOnu', opts),
|
|
executeBulkTask: (opts) => call('api:executeBulkTask', opts),
|
|
getTaskConfig: (opts) => call('api:getTaskConfig', opts),
|
|
getUpgradeStatus: (opts) => call('api:getUpgradeStatus', opts),
|
|
|
|
deleteOnus: (opts) => call('api:deleteOnus', opts),
|
|
|
|
dashboardStats: (opts) => call('api:dashboardStats', opts),
|
|
|
|
listOlts: (opts) => call('api:listOlts', opts),
|
|
executeFloodChange: (opts) => call('api:executeFloodChange', opts),
|
|
|
|
onFloodProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('olt-flood:progress', fn);
|
|
return () => ipcRenderer.removeListener('olt-flood:progress', fn);
|
|
},
|
|
|
|
fetchFecHealth: (opts) => call('api:fetchFecHealth', opts),
|
|
fetchOnuSnapshot: (opts) => call('api:fetchOnuSnapshot', opts),
|
|
savePlanCsv: (opts) => call('api:savePlanCsv', opts),
|
|
saveCsvFile: (opts) => call('api:saveCsvFile', opts),
|
|
openCsvFile: () => call('api:openCsvFile'),
|
|
|
|
// Streaming FEC pre-flight progress (one event per ONU resolved).
|
|
onFecProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('fec:progress', fn);
|
|
return () => ipcRenderer.removeListener('fec:progress', fn);
|
|
},
|
|
|
|
// Streaming verify-snapshot progress.
|
|
onVerifyProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('verify:progress', fn);
|
|
return () => ipcRenderer.removeListener('verify:progress', fn);
|
|
},
|
|
|
|
// Live progress events from the main process while executePerOnu runs.
|
|
onUpgradeProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('upgrade:progress', fn);
|
|
return () => ipcRenderer.removeListener('upgrade:progress', fn);
|
|
},
|
|
|
|
// Streaming state-fetch progress (Equipment ID population).
|
|
onStatesProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('states:progress', fn);
|
|
return () => ipcRenderer.removeListener('states:progress', fn);
|
|
},
|
|
|
|
// Streaming delete progress.
|
|
onDeleteProgress: (handler) => {
|
|
const fn = (_ev, payload) => handler(payload);
|
|
ipcRenderer.on('delete:progress', fn);
|
|
return () => ipcRenderer.removeListener('delete:progress', fn);
|
|
},
|
|
});
|