netanalyzer/integrations/storeDetail.js
Joseph McQueen 459301cc4d chore: Phase 0 initial hygiene
- Initialize git repository
- Add comprehensive .gitignore (protects .env and secrets)
- Fix package.json (correct main entry, add metadata)
- Expand .env.example with all required variables and comments
- Add README.md with architecture, setup, and commands
- Clean up empty scaffolding directories (logs removed, agents/models marked)
- Backup previous .env file locally

This establishes a safe foundation before further development.
2026-06-24 13:32:37 -04:00

344 lines
No EOL
16 KiB
JavaScript

const { getStoreLocation, getStoreRegisters, getStorePrinters, getStorePaymentTerminals } = require('../services/siw');
const { findMerakiNetwork, getMerakiDeviceAvailabilities, getMerakiClients } = require('../services/meraki');
const { formatStoreReport } = require('../utils/formatter');
function formatLastSeen(lastSeen) {
if (!lastSeen) return 'N/A';
const now = new Date();
const seen = new Date(lastSeen);
const diffMs = now - seen;
const diffMin = Math.floor(diffMs / 60000);
if (diffMin < 1) return 'Just now';
if (diffMin < 60) return `${diffMin} min ago`;
const hours = Math.floor(diffMin / 60);
return `${hours} hr ago`;
}
async function getStoreDetail(storeNumber) {
console.log(`🔍 Starting full analysis for store ${storeNumber}`);
let locationData, merakiNetwork, registers, printers, paymentTerminals, merakiClients = [], mdmDevices = [];
try {
// Phase 1: Fast lookups
[locationData, merakiNetwork] = await Promise.all([
getStoreLocation(storeNumber),
findMerakiNetwork(storeNumber)
]);
// Phase 2: Heavy parallel lookups
const parallelPromises = [
getStoreRegisters(storeNumber),
getStorePrinters(storeNumber),
getStorePaymentTerminals(storeNumber)
];
if (merakiNetwork) {
parallelPromises.push(
getMerakiClients(merakiNetwork.id),
require('../services/mdm').getMDMDevices(storeNumber)
);
}
const results = await Promise.all(parallelPromises);
registers = results[0];
printers = results[1];
paymentTerminals = results[2];
if (merakiNetwork) {
console.log(`Meraki Network:`)
console.log(JSON.stringify(merakiNetwork, null, 2))
merakiClients = results[3] || [];
mdmDevices = results[4] || [];
}
console.log(`✅ Fetched: ${registers?.length || 0} registers, ${printers?.length || 0} printers, ${paymentTerminals?.length || 0} payment terminals`);
if (merakiNetwork) {
console.log(`✅ Fetched ${merakiClients.length} Meraki clients`);
console.log(`✅ Fetched ${mdmDevices.length} MDM devices`);
}
} catch (err) {
console.error('❌ Error fetching data:', err.message);
}
let report = formatStoreReport(locationData, storeNumber);
// === Meraki Network Hardware ===
if (merakiNetwork) {
report += `**🌐 [${merakiNetwork.name}](${merakiNetwork.url})**\n\n`;
let activeDevices = [];
try {
const availabilities = await getMerakiDeviceAvailabilities(merakiNetwork.id);
activeDevices = availabilities.filter(d => d.status !== 'dormant');
} catch (e) {
console.error('Device availabilities failed:', e.message);
}
// Sort: Rear Switch → Front Switch → APs
activeDevices.sort((a, b) => {
const nameA = (a.name || '').trim().toUpperCase();
const nameB = (b.name || '').trim().toUpperCase();
if (nameA.includes('SWR') || /SW.*R\d?$/.test(nameA)) return -1;
if (nameB.includes('SWR') || /SW.*R\d?$/.test(nameB)) return 1;
if (nameA.includes('SWF') || /SW.*F\d/.test(nameA)) return -1;
if (nameB.includes('SWF') || /SW.*F\d/.test(nameB)) return 1;
return nameA.localeCompare(nameB);
});
report += `**🛠️ Active Network Devices (${activeDevices.length})**\n`;
activeDevices.forEach(dev => {
const statusEmoji = dev.status === 'online' ? '✅' : '⚠️';
const deviceName = (dev.name || dev.serial || 'Unknown').trim();
report += `**${deviceName}** — ${statusEmoji} ${dev.status}`;
if (dev.serial && merakiNetwork.url) {
// Extract short network code from the URL (e.g. JCz-ucnd)
const urlParts = merakiNetwork.url.split('/n/');
const networkCode = urlParts[1] ? urlParts[1].split('/')[0] : merakiNetwork.id;
let link = `${urlParts[0]}/n/${networkCode}`;
if (dev.productType === 'switch') {
link += `/manage/switches/${dev.serial}/summary`;
} else if (dev.productType === 'wireless') {
link += `/manage/access_points/${dev.serial}/summary`;
} else {
link += `/manage/${dev.serial}/summary`;
}
report += ` → [Dashboard](${link})`;
}
report += `\n`;
});
} else {
report += `\n⚠️ No matching Meraki network found.\n`;
}
// === Store Registers ===
if (registers && registers.length > 0) {
report += `\n**🖥️ Store Registers (${registers.length})**\n`;
registers.forEach(reg => {
const regNum = reg.register_number ? `Register ${reg.register_number}` : 'Register';
const brand = reg.brand_display_name || 'Unknown';
const type = reg.register_type_name || 'Unknown';
const name = (reg.register_display_name || reg.printer_name || 'Unknown').trim();
const match = merakiClients.find(c =>
c?.description &&
(c.description.trim().toLowerCase() === name.toLowerCase() ||
c.description.includes(name))
);
const status = match
? (match.status === 'Online' ? '✅ Online' : '❌ Offline')
: '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const deviceName = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${regNum}** — ${brand}${type}${status}${lastSeen}${deviceName}${connection}${link}\n`;
});
}
// === Store Printers (USB filtered) ===
const filteredPrinters = (printers || []).filter(p =>
(p.connection_type_name || '').toLowerCase() !== 'usb'
);
if (filteredPrinters.length > 0) {
report += `\n**🖨️ Store Printers (${filteredPrinters.length})**\n`;
filteredPrinters.forEach(printer => {
const name = (printer.printer_name || 'Unknown').trim();
const model = printer.printer_model_name || 'N/A';
const match = merakiClients.find(c =>
c?.description &&
(c.description.trim().toLowerCase() === name.toLowerCase() ||
c.description.includes(name))
);
const status = match
? (match.status === 'Online' ? '✅ Online' : '❌ Offline')
: '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const deviceName = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${name}** — ${model}${status}${lastSeen}${deviceName}${connection}${link}\n`;
});
}
if (paymentTerminals && paymentTerminals.length > 0) {
report += `\n**💳 Payment Terminals (${paymentTerminals.length})**\n`;
paymentTerminals.forEach(term => {
const deviceName = term.device_name || 'Unknown Terminal';
const adyenName = term.adyen_device_name || 'N/A';
const model = term.device_model_name || 'N/A';
const type = term.device_type_name || 'N/A';
const match = merakiClients.find(c =>
c?.description &&
(c.description.includes(deviceName) ||
c.description.includes(adyenName) ||
(term.ip_address && c.description.includes(term.ip_address.split('.')[0])))
);
const status = match
? (match.status === 'Online' ? '✅ Online' : '❌ Offline')
: '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${deviceName}** — ${adyenName}${model}${type}${status}${lastSeen}${recentDevice}${connection}${link}\n`;
});
}
if (mdmDevices && mdmDevices.length > 0) {
const servers = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('SRV'));
const mobileRegs = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('MR'));
const custDisplays = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('CD'));
const iphones = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('IPH'));
// === Store Servers ===
if (servers.length > 0) {
report += `\n**🖥️ Store Servers (${servers.length})**\n`;
servers.forEach(dev => {
const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Server';
const match = merakiClients.find(c =>
c?.description &&
(c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName)))
);
const status = match ? (match.status === 'Online' ? '✅ Online' : '❌ Offline') : '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${name}** — ${status}${lastSeen}${recentDevice}${connection}${link}\n`;
});
}
// === Mobile Registers ===
if (mobileRegs.length > 0) {
report += `\n**📱 Mobile Registers (${mobileRegs.length})**\n`;
mobileRegs.forEach(dev => {
const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Mobile Register';
const match = merakiClients.find(c =>
c?.description &&
(c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName)))
);
const status = match ? (match.status === 'Online' ? '✅ Online' : '❌ Offline') : '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${name}** — ${status}${lastSeen}${recentDevice}${connection}${link}\n`;
});
}
// === Customer Displays ===
if (custDisplays.length > 0) {
report += `\n**🖥️ Customer Displays (${custDisplays.length})**\n`;
custDisplays.forEach(dev => {
const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Display';
const match = merakiClients.find(c =>
c?.description &&
(c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName)))
);
const status = match ? (match.status === 'Online' ? '✅ Online' : '❌ Offline') : '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${name}** — ${status}${lastSeen}${recentDevice}${connection}${link}\n`;
});
}
// === Store iPhones ===
if (iphones.length > 0) {
report += `\n**📱 Store iPhones (${iphones.length})**\n`;
iphones.forEach(dev => {
const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown iPhone';
const match = merakiClients.find(c =>
c?.description &&
(c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName)))
);
const status = match ? (match.status === 'Online' ? '✅ Online' : '❌ Offline') : '❓ Unknown';
const lastSeen = match ? formatLastSeen(match.lastSeen) : '';
const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : '';
const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : '';
let link = '';
if (match && merakiNetwork?.url) {
const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id;
link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`;
}
report += `**${name}** — ${status}${lastSeen}${recentDevice}${connection}${link}\n`;
});
}
}
return report;
}
module.exports = { getStoreDetail };