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>
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 viaInstitutionContext.resolveScope()). The map keeps onlystatus === 'OPEN', indexes them byatmId, 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)
devicesApibaseURL =window.__APP_CONFIG__.apiUrl(prodhttps://api.bcos.cloud/devices, devhttp://localhost:8096).incidentApibaseURL =${gatewayUrl}/incident, wheregatewayUrl= devices URL with the trailing/devicesstripped. SoincidentAPI.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 onecircleMarker+ a count badgedivIcon, 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.
Cross-links
- [platform.data-architecture] · [platform.kafka] · [platform.service-ownership] · [technical.devices]