netanalyzer/integrations/storeHealth.js
Joseph McQueen 0a46d25bd6 chore: project cleanup — dead code, logging, tests, WS hardening
- Delete unused dataMerger, formatter, Device model, dead service exports,
  and the empty agents/ + .gitkeep placeholders.
- Extract STORE_MODES and MDM device-type filters into a shared constants.js.
- Anchor bot regexes (^help|^store|^analyze) so "analyze store 305" no
  longer fires both handlers; replace catch-all noise.
- Hoist inline require() calls in integrations to top-of-file imports.
- Harden WebSocket server: Authorization header support, single-agent
  enforcement, bounded pending requests, server-level error handler,
  coalesced cache refresh in Meraki client.
- Wrap Meraki/MDM network calls with withRetry; add request timeouts.
- Migrate all console.* calls onto utils/logger.js (LOG_LEVEL aware);
  drive Webex framework logLevel from env.
- Refactor storeDetail.js: shared renderClientLine + buildMdmSection
  helpers cut duplication roughly in half.
- Refresh README structure, document LOG_LEVEL, add npm run agent script,
  add jest testMatch + new tests (handlers, HealthReport, Store, ws).

Verified: npm run lint clean, 7 suites / 31 tests passing.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 17:21:22 -04:00

243 lines
8.5 KiB
JavaScript

const { getStoreLocation, getStorePaymentTerminals, getStorePrinters } = require('../services/siw');
const {
findMerakiNetwork,
getMerakiDeviceAvailabilities,
getMerakiClients,
} = require('../services/meraki');
const { getMDMDevices } = require('../services/mdm');
const { findMatchingClient, getClientStatus } = require('../utils/merakiMatcher');
const { createHealthReport } = require('../models/HealthReport');
const { MDM_DEVICE_TYPES, filterMdmByType } = require('../constants');
const logger = require('../utils/logger');
// Configurable scoring penalties (higher = worse impact)
const SCORING = {
noMerakiNetwork: 50,
switchOffline: 15,
apOffline: 5,
apAlerting: 3,
serverOffline: 35,
noMdmData: 20,
paymentTerminalOffline: 5,
printerOffline: 4,
// Mobile registers, customer displays and iPhones currently contribute to issues list only
};
async function getStoreHealth(storeNumber, _mode = 'default') {
logger.info('Running health analysis', { storeNumber });
let locationData,
merakiNetwork,
merakiClients = [],
mdmDevices = [],
paymentTerminals = [],
printers = [];
try {
[locationData, merakiNetwork] = await Promise.all([
getStoreLocation(storeNumber),
findMerakiNetwork(storeNumber),
]);
if (merakiNetwork) {
[merakiClients, mdmDevices, paymentTerminals, printers] = await Promise.all([
getMerakiClients(merakiNetwork.id),
getMDMDevices(storeNumber),
getStorePaymentTerminals(storeNumber),
getStorePrinters(storeNumber),
]);
}
} catch (err) {
logger.error('Health analysis error', { error: err.message });
}
const healthReport = createHealthReport(storeNumber, locationData?.name || 'Unknown Store');
// === Network Infrastructure ===
if (merakiNetwork) {
healthReport.addSection(
'**🌐 Network Infrastructure**',
`- Meraki Network: ✅ [${merakiNetwork.name}](${merakiNetwork.url})\n`
);
try {
const avail = await getMerakiDeviceAvailabilities(merakiNetwork.id);
const switches = avail.filter(d => d.productType === 'switch' && d.status !== 'dormant');
const aps = avail.filter(d => d.productType === 'wireless' && d.status !== 'dormant');
const swOnline = switches.filter(d => d.status === 'online').length;
const apOnline = aps.filter(d => d.status === 'online').length;
const apAlerting = aps.filter(d => d.status === 'alerting').length;
let netContent = `- Switching: ${swOnline}/${switches.length} online\n`;
netContent += `- Access Points: ${apOnline}/${aps.length} online`;
if (apAlerting > 0) {
netContent += ` (${apAlerting} alerting)`;
}
netContent += `\n\n`;
healthReport.addSection('', netContent); // append
// Scoring & Issues
if (swOnline < switches.length) {
const offlineSw = switches.length - swOnline;
healthReport.deduct(offlineSw * SCORING.switchOffline, `${offlineSw} switch(es) offline`);
}
if (apOnline < aps.length || apAlerting > 0) {
const totalApIssues = aps.length - apOnline;
if (apAlerting > 0) {
healthReport.deduct(
totalApIssues * SCORING.apAlerting,
`${apAlerting} access point(s) alerting (check cabling/power)`
);
} else {
healthReport.deduct(
totalApIssues * SCORING.apOffline,
`${totalApIssues} access point(s) offline`
);
}
}
} catch (e) {
logger.error('Meraki availability error', { error: e.message });
}
} else {
healthReport.addSection('**🌐 Network Infrastructure**', `- Meraki Network: ❌ Not Found\n\n`);
healthReport.deduct(SCORING.noMerakiNetwork, 'No Meraki network found');
}
// === Core Systems ===
healthReport.addSection('**🖥️ POS Systems**', '');
if (mdmDevices && mdmDevices.length > 0) {
const servers = filterMdmByType(mdmDevices, MDM_DEVICE_TYPES.SERVER);
const mobileRegs = filterMdmByType(mdmDevices, MDM_DEVICE_TYPES.MOBILE_REGISTER);
const custDisplays = filterMdmByType(mdmDevices, MDM_DEVICE_TYPES.CUSTOMER_DISPLAY);
const iphones = filterMdmByType(mdmDevices, MDM_DEVICE_TYPES.IPHONE);
// Server (heavy penalty)
let serverStatus = '❓ Unknown';
if (servers.length > 0) {
const srv = servers[0];
const match = findMatchingClient(merakiClients, {
UserName: srv.UserName,
DeviceFriendlyName: srv.DeviceFriendlyName,
});
serverStatus = getClientStatus(match);
if (serverStatus === '❌ Offline') {
healthReport.deduct(
SCORING.serverOffline,
`Store Server ${srv?.UserName || srv?.DeviceFriendlyName} is offline`
);
}
}
healthReport.addSection('', `- Store Server: ${serverStatus}\n`);
// Mobile Registers
let mobileOnline = 0;
mobileRegs.forEach(reg => {
const name = (reg.UserName || reg.DeviceFriendlyName || '').trim();
const match = findMatchingClient(merakiClients, {
UserName: reg.UserName,
DeviceFriendlyName: reg.DeviceFriendlyName,
});
const status = getClientStatus(match);
if (status === '✅ Online') mobileOnline++;
if (status === '❌ Offline') healthReport.addIssue(`Mobile Register ${name} is offline`);
});
healthReport.addSection(
'',
`- Mobile Registers: ${mobileOnline}/${mobileRegs.length} online\n`
);
// Customer Displays & iPhones (lighter penalty)
let cdOnline = 0;
custDisplays.forEach(cd => {
const name = (cd.UserName || cd.DeviceFriendlyName || '').trim();
const match = findMatchingClient(merakiClients, {
UserName: cd.UserName,
DeviceFriendlyName: cd.DeviceFriendlyName,
});
const status = getClientStatus(match);
if (status === '✅ Online') cdOnline++;
if (status === '❌ Offline') healthReport.addIssue(`Customer Display ${name} is offline`);
});
healthReport.addSection('', `- Customer Displays: ${cdOnline}/${custDisplays.length} online\n`);
let iphoneOnline = 0;
iphones.forEach(phone => {
const name = (phone.UserName || phone.DeviceFriendlyName || '').trim();
const match = findMatchingClient(merakiClients, {
UserName: phone.UserName,
DeviceFriendlyName: phone.DeviceFriendlyName,
});
const status = getClientStatus(match);
if (status === '✅ Online') iphoneOnline++;
if (status === '❌ Offline') healthReport.addIssue(`iPhone ${name} is offline`);
});
healthReport.addSection('', `- Store iPhones: ${iphoneOnline}/${iphones.length} online\n\n`);
} else {
healthReport.addSection('', `- No MDM data available\n\n`);
healthReport.deduct(SCORING.noMdmData);
}
// === POS & Peripherals (with per-device penalty) ===
healthReport.addSection('**💳 POS Peripherals**', '');
// Payment Terminals
if (paymentTerminals && paymentTerminals.length > 0) {
let onlinePayments = 0;
paymentTerminals.forEach(term => {
const match = findMatchingClient(merakiClients, {
name: term.device_name,
adyenName: term.adyen_device_name,
ip_address: term.ip_address,
});
const terminalName = (term.device_name || term.adyen_device_name || 'unknown').trim();
const status = getClientStatus(match);
if (status === '✅ Online') onlinePayments++;
if (status === '❌ Offline') {
healthReport.deduct(
SCORING.paymentTerminalOffline,
`Payment Terminal ${terminalName} is offline`
);
}
});
healthReport.addSection(
'',
`- Payment Terminals: ${onlinePayments}/${paymentTerminals.length} online\n`
);
}
// Printers
const filteredPrinters = (printers || []).filter(
p => (p.connection_type_name || '').toLowerCase() !== 'usb'
);
if (filteredPrinters.length > 0) {
let onlinePrinters = 0;
filteredPrinters.forEach(printer => {
const name = (printer.printer_name || '').trim().toLowerCase();
const match = findMatchingClient(merakiClients, { name: printer.printer_name });
const status = getClientStatus(match);
if (status === '✅ Online') onlinePrinters++;
if (status === '❌ Offline') {
healthReport.deduct(SCORING.printerOffline, `Printer ${name} is offline`);
}
});
healthReport.addSection(
'',
`- Printers: ${onlinePrinters}/${filteredPrinters.length} online\n`
);
}
// Finalize and return
const report = healthReport.finalize();
return { summary: report.summary };
}
module.exports = { getStoreHealth };