--- module: dashboard.overview title: Fleet Overview tab: Claude order: 40 audience: dev --- ## Identity - Frontend: `hiveops-dashboard` (Svelte/Vite SPA, port 5181, `dashboard.bcos.cloud`). **No backend, no DB, no Kafka of its own.** - Component: `hiveops-dashboard/frontend/src/components/Dashboard/Dashboard.svelte`; data: `frontend/src/lib/{api.ts,stores.ts}`. - Single axios base: `window.__APP_CONFIG__.apiUrl` (`VITE_API_URL`, default `http://localhost:8080`), set by `frontend/entrypoint.sh`. ## Endpoints (METHOD + path, relative to `apiUrl`) | Method | Path | Owner service | Backend mapping | Auth | |---|---|---|---|---| | GET | `/atms/summary` | hiveops-devices | `AtmController` `/api/atms` + `/api/devices` → `/summary` | JWT, institution-scoped, no `@PreAuthorize` | | GET | `/atms/dashboard/stats` | hiveops-incident | `AtmDashboardController` `/api/atms/dashboard/stats` | JWT, institution-scoped, `@Cacheable("dashboardStats")` | | GET | `/fleet/tasks/stats` | hiveops-fleet | `FleetApiController` `/api/fleet/{stats,tasks/stats}` | `hasAnyRole('USER','CUSTOMER','ADMIN','MSP_ADMIN','BCOS_ADMIN')` | | GET | `/users/me` | hiveops-incident | `UserController` `/api/users/me` | JWT (claims only) | ## Owning service per data element - ATM totals / connected / disconnected / neverConnected / byModel → **devices** (`atms` table, status from `agentConnectionStatus`/`last_heartbeat`). - openIncidents / criticalIncidents / incidentsThisWeek / recentIncidents / totalAtms / operationalAtms → **incident** (`incidents`, `atms`). - totalTasks / pendingTasks / runningTasks / completedTasks / failedTasks / totalArtifacts / totalModulesReporting → **fleet** (`fleet_tasks`, `fleet_artifacts`). ## Tables - `hiveiq_devices.atms`, `hiveiq_devices.atm_properties` - `hiveiq_incident.incidents`, `hiveiq_incident.atms` - `hiveiq_fleet.fleet_tasks`, `hiveiq_fleet.fleet_artifacts` (also `fleet_task_history`, `device_summary` — NOT read by this feature's stats) ## Kafka - `hiveops.device.events` — published by devices, consumed into `device_summary` mirrors in fleet/incident/reports/recon. **Not in this feature's request path** (dashboard reads primary tables directly). ## Gotchas - `/atms/dashboard/stats` exists in **both** hiveops-incident AND hiveops-devices. Only the **incident** one populates incident fields. Routing determines which answers. - `/atms/summary` (devices) and `/atms/dashboard/stats` (incident) share the `/atms/` prefix but land on **different services** — relies on nginx path routing (ops repo, not in tree). - `FleetTaskService.getStats()` hard-codes `completedTasks=0`, `failedTasks=0`; never sets `totalModulesReporting` (→ always 0 on the page). Do NOT treat as an outage. - `getStats()` `pendingTasks` = only `AUTO_UPDATE` PENDING (undercounts other kinds). - `getStats()` ignores its `institutionKeys` arg → task/artifact totals are fleet-wide even for scoped users (inconsistent with scoped ATM/incident numbers). - Each SPA loader `try/catch`es independently: one failing call zeroes only its card, silently (`console.error` only). - CUSTOMER role only hides the "View all" incidents link; not an admin surface. - Auto-refresh state in `localStorage['dashboard_autoRefresh']`; 60s tick. ## Do NOT - Do NOT add endpoints or tables to `hiveops-dashboard` — it is a pure client; put new data in the owning service. - Do NOT assume `/atms/dashboard/stats` hits incident — verify routing before diagnosing wrong incident counts. - Do NOT report Completed/Failed/Modules-Reporting = 0 as a bug in the dashboard; it originates in `FleetTaskService.getStats`. - Do NOT expect fleet task/artifact totals to respect institution scope. - Do NOT write to `device_summary` to "fix" dashboard ATM counts — those come from the devices `atms` table. - Do NOT add role guards to `/atms/summary` or `/atms/dashboard/stats` assuming MSP_ADMIN-only; current auth is any valid JWT, institution-scoped. Refs: [platform.service-ownership] · [platform.kafka] · [platform.data-architecture] · [dashboard.overview]