Store Health Dashboard
Find a file
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
bot chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
config chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
integrations chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
models chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
services chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
tests chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
utils chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
.env.example chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
.gitignore chore: Phase 0 initial hygiene 2026-06-24 13:32:37 -04:00
.prettierignore chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
.prettierrc.json chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
constants.js chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
eslint.config.js chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
package-lock.json chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
package.json chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
README.md chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
remoteAgent.js chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00
server.js chore: project cleanup — dead code, logging, tests, WS hardening 2026-06-24 17:21:22 -04:00

NetAnalyzer

Webex bot that provides store-level network and device health analysis by correlating data from Meraki, SIW (Store Information Warehouse), and Workspace ONE MDM.

What it does

  • store <number> — Full detailed report of a store's registers, printers, payment terminals, network devices, MDM inventory, and their online/offline status via Meraki clients.
  • analyze <number> — Higher-level health summary with an overall score and prioritized issues (network, POS systems, peripherals).

The bot helps operations teams quickly understand the connectivity and device health state of a retail location.

Architecture

┌─────────────────┐          ┌─────────────────────┐
│  Webex Bot      │◄────────►│  NetAnalyzer Server │
│  (Webex rooms)  │  Webex   │  (server.js)        │
└─────────────────┘          └─────────┬───────────┘
                                       │
                                       │ WebSocket (authenticated)
                                       ▼
                              ┌─────────────────────┐
                              │  Remote Agent       │◄──► SIW API (Basic Auth)
                              │  (remoteAgent.js)   │◄──► Workspace ONE MDM
                              └─────────────────────┘
                                       │
                                       ▼
                              Meraki API (direct from server)

Why the remote agent?
SIW and some MDM systems are only reachable from specific internal networks. The remote agent runs in that environment and proxies requests back to the main NetAnalyzer server over an authenticated WebSocket.

Prerequisites

  • Node.js >= 18
  • A Webex bot account with access token
  • Meraki API key with organization access
  • Access to your organization's SIW API and Workspace ONE (AirWatch) environment
  • Ability to run the remote agent on a machine that can reach SIW/MDM

Setup

  1. Clone and install

    git clone <repo>
    cd netanalyzer
    npm install
    
  2. Configure environment

    cp .env.example .env
    # Edit .env with your real credentials
    

    Required values are documented in .env.example.

  3. (Optional but recommended) Rotate all secrets If you previously had credentials in the repository, rotate:

    • Webex bot token
    • Meraki API key
    • SIW credentials
    • Workspace ONE client secret + tenant code
    • WS_TOKEN

Running

Main server + bot (where the Webex connection lives)

npm start
# or for development with auto-reload
npm run dev

This starts:

  • The Webex bot framework (listens for messages in Webex spaces)
  • The WebSocket server on the port defined in WS_PORT (default 8080)

Remote agent (run on a machine that can reach internal systems)

# On the internal machine
node remoteAgent.js

Make sure WS_URL in its environment points to the main server with the correct WS_TOKEN.

Bot Commands

In any Webex space where the bot is a member:

  • store 782 — Detailed device inventory and connectivity for store 782
  • analyze 782 — Quick health summary with score for store 782

The bot also responds to variations containing "store" or "analyze".

Project Structure

.
├── bot/
│   └── handlers.js          # Webex command handlers (store / analyze / help)
├── config/
│   └── index.js             # Centralized env-driven configuration + validation
├── integrations/
│   ├── storeDetail.js       # Full store report builder (Meraki + SIW + MDM)
│   └── storeHealth.js       # Health score + summary
├── models/
│   ├── Store.js             # Store domain model (normalizes SIW location)
│   └── HealthReport.js      # Score / issue accumulator
├── services/
│   ├── meraki.js            # Meraki API client (cached, retried)
│   ├── siw.js               # SIW calls (proxied via the remote agent)
│   ├── mdm.js               # Workspace ONE MDM client (retried)
│   └── websocket.js         # WS server + proxyRequest helper
├── tests/
│   ├── *.test.js            # Unit tests
│   ├── integration/         # Integration tests using mocks
│   └── mocks/               # Service mocks for tests
├── utils/
│   ├── logger.js            # Structured JSON logger (LOG_LEVEL aware)
│   ├── merakiMatcher.js     # Device ↔ Meraki client matching
│   ├── retry.js             # withRetry wrapper (exponential backoff)
│   └── validate.js          # Input parsers
├── constants.js             # Shared constants (STORE_MODES, MDM device types)
├── remoteAgent.js           # Lightweight proxy client (run on internal host)
├── server.js                # Main entry point (bot + WS server)
├── package.json
├── eslint.config.js
├── .prettierrc.json
└── .env.example

Logging

The app emits single-line JSON to stdout/stderr via utils/logger.js. Set LOG_LEVEL in .env to one of debug | info | warn | error (default: info).

Security Notes

  • Never commit .env (it is gitignored, along with .env.bak.*).
  • The WebSocket connection between server and remote agent is protected by a shared WS_TOKEN. The remote agent now sends the token in an Authorization: Bearer header (the legacy ?token=... query parameter still works for older deployments but should be migrated).
  • Only one remote agent may be connected at a time; a newer connection replaces the older one and any in-flight proxy requests are rejected (rather than silently hanging).
  • All SIW communication uses Basic Auth and is only performed through the remote agent.
  • Meraki and MDM calls use tokens that should be scoped to the minimum necessary permissions.

Development

npm run lint          # eslint + prettier check
npm run lint:fix      # auto-fix lint issues
npm run format        # prettier write
npm test              # jest (unit + integration tests using mocks)
npm run test:watch

License

ISC