- 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.
146 lines
No EOL
3.9 KiB
JavaScript
146 lines
No EOL
3.9 KiB
JavaScript
const axios = require('axios');
|
|
const config = require('../config');
|
|
|
|
const merakiAxios = axios.create({
|
|
baseURL: config.meraki.baseUrl,
|
|
headers: {
|
|
'X-Cisco-Meraki-API-Key': config.meraki.apiKey,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
// In-memory cache
|
|
let cachedNetworks = [];
|
|
let lastCacheTime = 0;
|
|
const CACHE_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours
|
|
|
|
async function refreshMerakiNetworksCache() {
|
|
const start = Date.now();
|
|
console.log('🔄 Refreshing Meraki networks cache...');
|
|
|
|
try {
|
|
const url = `/organizations/${config.meraki.orgId}/networks?perPage=5000`;
|
|
const res = await merakiAxios.get(url);
|
|
cachedNetworks = res.data || [];
|
|
lastCacheTime = Date.now();
|
|
|
|
console.log(`✅ Cached ${cachedNetworks.length} Meraki networks (${Date.now() - start}ms)`);
|
|
} catch (err) {
|
|
console.error('❌ Failed to refresh Meraki networks cache:', err.message);
|
|
}
|
|
}
|
|
|
|
async function getMerakiNetworks(forceRefresh = false) {
|
|
const now = Date.now();
|
|
if (forceRefresh || cachedNetworks.length === 0 || (now - lastCacheTime > CACHE_TTL_MS)) {
|
|
await refreshMerakiNetworksCache();
|
|
}
|
|
return cachedNetworks;
|
|
}
|
|
|
|
async function findMerakiNetwork(storeNum) {
|
|
if (!storeNum) return null;
|
|
|
|
const raw = String(storeNum).trim();
|
|
const match = raw.match(/\d+/);
|
|
if (!match) return null;
|
|
|
|
let searchTerm = match[0].padStart(5, '0').slice(-5);
|
|
|
|
const networks = await getMerakiNetworks();
|
|
|
|
console.log(`🔍 Searching for store "${raw}" → 5-digit term "${searchTerm}"`);
|
|
|
|
let bestMatch = null;
|
|
let bestScore = -1;
|
|
|
|
for (const net of networks) {
|
|
const name = (net.name || '').toLowerCase();
|
|
const term = searchTerm.toLowerCase();
|
|
|
|
if (name.includes(term)) {
|
|
const score = (name.includes(` ${term}`) || name.includes(`-${term}`) || name.includes(term + ' ')) ? 100 : 50;
|
|
if (score > bestScore) {
|
|
bestScore = score;
|
|
bestMatch = net;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestMatch) {
|
|
console.log(`✅ Best Meraki match: ${bestMatch.name} (ID: ${bestMatch.id})`);
|
|
return bestMatch;
|
|
}
|
|
|
|
console.log(`❌ No Meraki network found for store ${storeNum}`);
|
|
return null;
|
|
}
|
|
|
|
async function getNetworkClients(networkId) {
|
|
if (!networkId) return [];
|
|
try {
|
|
const res = await merakiAxios.get(`/networks/${networkId}/clients?timespan=7200`); // last 2 hours
|
|
return res.data || [];
|
|
} catch (err) {
|
|
console.error(`❌ Failed to get clients for network ${networkId}:`, err.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function getAllMerakiDevices(networkId) {
|
|
if (!networkId) return [];
|
|
try {
|
|
const res = await merakiAxios.get(`/networks/${networkId}/devices`);
|
|
return res.data || [];
|
|
} catch (err) {
|
|
console.error(`❌ Failed to get devices for network ${networkId}:`, err.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function getMerakiDeviceAvailabilities(networkId) {
|
|
if (!networkId || !config.meraki.orgId) return [];
|
|
|
|
try {
|
|
const res = await merakiAxios.get(
|
|
`/organizations/${config.meraki.orgId}/devices/availabilities`,
|
|
{
|
|
params: { networkIds: [networkId] }
|
|
}
|
|
);
|
|
return res.data || [];
|
|
} catch (err) {
|
|
console.error(`❌ Device availabilities failed:`, err.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function getMerakiClients(networkId) {
|
|
if (!networkId) return [];
|
|
|
|
try {
|
|
// 7 days = much better chance of catching registers & printers
|
|
const timespanSeconds = 7 * 24 * 60 * 60; // 604800
|
|
|
|
const res = await merakiAxios.get(
|
|
`/networks/${networkId}/clients?perPage=1000×pan=${timespanSeconds}`
|
|
);
|
|
|
|
const clients = res.data || [];
|
|
console.log(`✅ Fetched ${clients.length} Meraki clients (7-day timespan) [getMerakiClients]`);
|
|
|
|
return clients;
|
|
} catch (err) {
|
|
console.error(`❌ Failed to fetch Meraki clients:`, err.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
findMerakiNetwork,
|
|
getNetworkClients,
|
|
getMerakiClients,
|
|
getAllMerakiDevices,
|
|
getMerakiDeviceAvailabilities,
|
|
refreshMerakiNetworksCache
|
|
}; |