Files
hiveops-guide/backend/src/main/resources/content/devices/map/architect.md
T
johannes 930a4b968e feat(guide): dev audience tier + GUIDE_SERVED_AUDIENCES env cap (prod = customer,internal)
4-tier ladder customer<internal<dev<bcos. Re-tagged Architect/Testing/Claude +
platform/technical from internal->dev; Processes stays bcos. Backend visible()
enforces a per-instance served-audiences cap at the API. bcos.dev/DLX serve all;
production sets GUIDE_SERVED_AUDIENCES=customer,internal so dev/bcos never leave
dev. Verified: capped instance returns only Customer+Internal, 404s dev/bcos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 17:11:00 -04:00

4.6 KiB

module, title, tab, order, audience
module title tab order audience
devices.map Map Architect 30 dev

Internal · architecture. Grounded in AtmMap.svelte, frontend/src/lib/{api,stores}.ts, and the devices/incident controllers, 2026-07-01.

Shape of the feature

The Map is a pure read/aggregate view with zero server-side state of its own. There is no devices.map controller, table, executor, or Kafka topic. The Svelte component orchestrates two backends and does all clustering/colour/filter logic in the browser. See [platform.service-ownership].

AtmMap.svelte (devices SPA, port 5177)
   ├─ loadAtms()       → devicesApi   GET /api/atms            → hiveops-devices (8096)  → atms table
   ├─ loadIncidents()  → incidentApi  GET /incident/incidents  → hiveops-incident (8080) → incidents table
   └─ loadAtmGroups()  → devicesApi   GET /api/atm-groups      → hiveops-devices (8096)  → atm_groups table
        (Leaflet circleMarkers, client-side grouping + colour + filter)

Services involved & how they interact

  • hiveops-devices — system of record for the pins. AtmController (@RequestMapping({"/api/atms","/api/devices"})) serves the ATM inventory incl. latitude/longitude/status/inSupervisor/agentConnectionStatus. AtmGroupController (/api/atm-groups) serves group definitions for the Group filter. See [technical.devices].
  • hiveops-incident — supplies open incidents. IncidentController.getAllIncidents() (GET /api/incidents, institution-scoped via InstitutionContext.resolveScope()). The map keeps only status === 'OPEN', indexes them by atmId, and uses that to (a) colour a pin orange, (b) build the Incident Type filter, (c) render incident tags in the pin popup.

The two calls are independent — an incident-backend outage degrades colour/type-filter but still renders every pin.

Frontend API wiring (lib/api.ts)

  • devicesApi baseURL = window.__APP_CONFIG__.apiUrl (prod https://api.bcos.cloud/devices, dev http://localhost:8096).
  • incidentApi baseURL = ${gatewayUrl}/incident, where gatewayUrl = devices URL with the trailing /devices stripped. So incidentAPI.getAll() hits …/incident/incidents; nginx rewrites that to the controller's /api/incidents.

Data flow (no Kafka in the read path)

The map does not consume Kafka. It reads live from both services on each refresh. Note the platform-level relationship for context: hiveops-devices publishes hiveops.device.events (DEVICE_CREATED/UPDATED/DELETED) which mirrors device data into incident/fleet/reports/recon device_summary tables — but this map bypasses those mirrors and queries devices + incident directly, so it is never stale-mirror-affected. See [platform.kafka], [platform.data-architecture].

Auto-refresh is a browser setInterval; each tick calls loadAtms() + loadIncidents() (interval = settings.dashboardRefreshIntervalSeconds).

DB tables (read-only)

Table DB Service Used for
atms (cols incl. latitude, longitude, status, in_supervisor, agent_connection_status) hiveiq_devices devices Pin position, colour, popup
atm_groups hiveiq_devices devices Group filter options
incidents hiveiq_incident incident Open-incident colour + Incident Type filter + popup tags

No table is written by this view.

Key API endpoints

Method + Path Service Role Purpose
GET /api/atms devices authenticated (no @PreAuthorize) Pins. Falls back to GET /api/atms/paginated?size=9999&groupId=<gid> when a default group is set
GET /api/atm-groups devices authenticated Group filter
GET /api/incidents incident authenticated, institution-scoped Open incidents
PUT /api/atms/{id} devices MSP_ADMIN/BCOS_ADMIN Set lat/long to un-hide an ATM (done from ATM Detail, not the map)

Client-side logic worth knowing

  • Clustering: ATMs are grouped by exact "${latitude},${longitude}" string key; a group of >1 renders one circleMarker + a count badge divIcon, coloured by the worst member (STATUS_PRIORITY: DOWN < MAINTENANCE < INACTIVE < OUT_OF_CASH < IN_SERVICE).
  • Pin colour (getPinColor): not IN_SERVICE/OUT_OF_CASH → red; open incident → amber; OUT_OF_CASH → amber; inSupervisor → blue; else green.
  • Persistence is entirely localStorage: hiveops_map_filters_v2 (current filters + presets), devices_map_autoRefresh, hiveops_defaultGroupId.
  • [platform.data-architecture] · [platform.kafka] · [platform.service-ownership] · [technical.devices]