Surface handset registrations and RSSI, tighten reboot health to 7 days, and consolidate Base / Handsets & RF / Network & RTP sections. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
// services/dectStatus/captureRawXml.js
|
||
//
|
||
// Fetch raw status.xml from store DECT bases via the on-prem relay.
|
||
// Used by GET /api/dect/raw-xml/:storeNumber and scripts/fetchDectStatusXml.js.
|
||
|
||
import { collectPhoneStatus } from '../phoneService.js';
|
||
import { discoverDectBases } from '../dectDiscovery.js';
|
||
import { collectRawAll } from '../dectCollectorService.js';
|
||
import { getDectRelayHub } from '../dectRelayHub.js';
|
||
import { logger } from '../../utils/logger.js';
|
||
|
||
const LOG_SCOPE = 'dect:capture';
|
||
|
||
/**
|
||
* @param {string} storeNum
|
||
* @param {object} [opts]
|
||
* @param {string} [opts.baseFilter] optional IP or MAC filter
|
||
* @returns {Promise<object>}
|
||
*/
|
||
export async function captureDectRawXml(storeNum, opts = {}) {
|
||
const store = String(storeNum || '').trim();
|
||
if (!store || !/^\d{2,4}$/.test(store)) {
|
||
const err = new Error('storeNum must be a 2–4 digit store number');
|
||
err.code = 'INVALID_STORE';
|
||
throw err;
|
||
}
|
||
|
||
if (!process.env.DECT_RELAY_AGENT_TOKEN) {
|
||
const err = new Error('DECT_RELAY_AGENT_TOKEN is not configured on this bot');
|
||
err.code = 'RELAY_NOT_CONFIGURED';
|
||
throw err;
|
||
}
|
||
|
||
let relay = null;
|
||
try {
|
||
relay = getDectRelayHub().status();
|
||
} catch (err) {
|
||
logger(LOG_SCOPE, `Relay status unavailable: ${err.message}`, 'warn');
|
||
relay = { connected: false };
|
||
}
|
||
|
||
const phoneData = await collectPhoneStatus(store);
|
||
const { bases, warnings: discoveryWarnings } = discoverDectBases(phoneData || {});
|
||
|
||
let targets = bases;
|
||
const baseFilter = (opts.baseFilter || '').trim();
|
||
if (baseFilter) {
|
||
targets = filterBases(bases, baseFilter);
|
||
if (targets.length === 0) {
|
||
const err = new Error(`No discovered base matched "${baseFilter}" for store ${store}`);
|
||
err.code = 'BASE_NOT_FOUND';
|
||
err.knownBases = bases.map((b) => ({ ip: b.ip, mac: b.mac, name: b.name }));
|
||
throw err;
|
||
}
|
||
}
|
||
|
||
const results = await collectRawAll(targets);
|
||
|
||
return {
|
||
storeNum: store,
|
||
relay,
|
||
discoveryWarnings,
|
||
bases: results.map((r) => ({
|
||
mac: r.base?.mac || null,
|
||
ip: r.base?.ip || null,
|
||
webexId: r.base?.webexId || null,
|
||
name: r.base?.name || null,
|
||
ok: r.ok,
|
||
byteLength: r.byteLength,
|
||
rawXml: r.rawXml,
|
||
sectionInventory: r.sectionInventory,
|
||
parsed: r.data,
|
||
verdict: r.verdict,
|
||
elapsedMs: r.elapsedMs,
|
||
error: r.error,
|
||
})),
|
||
};
|
||
}
|
||
|
||
function filterBases(bases, raw) {
|
||
const needle = String(raw).trim().toLowerCase();
|
||
const needleMac = needle.replace(/[^0-9a-f]/g, '');
|
||
return bases.filter((b) => {
|
||
if (b.ip && b.ip.toLowerCase() === needle) return true;
|
||
if (b.mac) {
|
||
const macHex = b.mac.replace(/[^0-9a-fA-F]/g, '').toLowerCase();
|
||
if (macHex === needleMac && needleMac.length === 12) return true;
|
||
if (b.mac.toLowerCase() === needle) return true;
|
||
}
|
||
if (b.name && b.name.toLowerCase().includes(needle)) return true;
|
||
return false;
|
||
});
|
||
}
|