ThingsBoard Monitoring Integration — Revival Phase 0: Rebase & Reconciliation
Context
Epic #104 and its sub-issues #105–#110 define a ThingsBoard integration to power a realtime
“Quan trắc” (monitoring) screen inside “Tổng quan” (dashboard overview). Two incompatible
implementations exist:
master: a “phase 0” model where the frontend authenticates directly to ThingsBoard using
per-user credentials fetched from the backend (GET /thingsboard/credentials, which returns
the user’s ThingsBoard password in plaintext — a security defect, and a violation of the
documented backend rule “KHÔNG trả về password hash trong response”). Real, shipped work also
exists here:ThingsboardClientService(shared-tenant REST client used bydevices,
telemetry,health) and gateway RPC command dispatch/persistence fixes.issue-104(unmerged, last commit 2026-05-22, diverged 29 commits frommaster, which has
since advanced 15 commits): a backend-proxy model matching the epic’s original architecture
decision (“ThingsBoard là lớp xử lý chính… Hệ thống hiện tại là lớp sản phẩm, cung cấp UI duy
nhất”). This branch already implements normalized telemetry contracts, an org-scoped resolver,
a realtime WebSocket gateway (/monitoring), snapshot/history query APIs, an alert-ack alias,
and a frontend monitoring page + hooks — but it predates 15 commits’ worth ofmasterchanges
and has at least one direct schema conflict with it.
Decision: adopt the backend-proxy model (issue-104’s architecture) as the accepted design,
and treat the frontend-direct-to-ThingsBoard flow on master as tech debt to remove. This is
Phase 0 of a multi-phase effort. It only covers reviving issue-104’s code onto current
master and reconciling conflicts — it does not cover closing the feature gaps identified
in the epic audit (error model/idempotency, RBAC matrix documentation, full observability,
audit trail for RPC commands, or moving the MVP screen into “Tổng quan”). Those are separate,
later specs, tracked as GitHub sub-issues #105–#110 under epic #104.
Goal
Produce a new branch, revive/thingsboard-monitoring, based on current master, containing
the issue-104 monitoring functionality, with all conflicts resolved, dead/insecure code
removed, and tests/build green — ready for its own PR review. issue-104 itself is left
untouched (reference only, not force-pushed, not deleted).
Out of scope (deferred to later specs, tracked under #105–#110)
- Error model / retryable-vs-non-retryable codes / idempotency keys (#105 gap)
- RBAC matrix documentation for
monitoring:*permissions, service-to-service auth (#106 gap) - Message-ordering guarantees, latency SLA definition/measurement (#107 gap)
- Correlation-id logging, throughput/error-rate metrics, tracing, alerting, dashboards (#108)
- Moving the monitoring page from a standalone
/monitoringroute into “Tổng quan”, nav wiring
(#109 gap) - Audit trail for RPC commands, RBAC-scoped command bridge, retry/backoff policy (#110 gap)
Branch strategy
- Create
revive/thingsboard-monitoringfrommaster(currently atf5f08199f). - Do not rebase or force-push
issue-104. It stays as historical reference until the revival
branch is merged, then can be deleted. - Port
issue-104’s 29 commits by squashing into logical groups and re-applying on top of
currentmaster, skipping commits that are either unrelated (accidentally carried topnav
spacing changes) or superseded by independentmasterwork (see “Commits dropped” below).
Commit groups to port
feat(monitoring): define normalized telemetry contracts + harden validation
(squash off974b989b,7d02d1062)feat(monitoring): add snapshot and history api surface + tenant contract hardening
(squash of8641ef03e,7e2bfb188,9d56135de,569ff61af)feat(monitoring): implement ThingsBoard adapter core + realtime stream proxy
(squash of8a67aba3b,aced14be0,5e0615a9e,b6e1c7d3e,7c40877ae,a9b390320,
0a386ee21)feat(monitoring): add reconnect/observability baseline + org-scoped rpc proxy
(squash of5acf4e5eb,bf3ac2008)feat(alerts): expose ack alias endpoint for monitoring contract(6d4a9b56d)feat(frontend): add monitoring dashboard page, data service and hooks
(squash ofe49b0b134,54d8cf6ba)test(monitoring): enforce API and tenant-scope contracts end-to-end
(squash of5419e38bc,012f08a57)
Commits dropped (not ported)
929c60133,bee413a15(topnav spacing) — unrelated, accidentally on this branch.59c281377(JWT for WebSocket auth) — superseded;master’s
backend/src/websocket/websocket.gateway.tsalready verifies JWT via
handshake.auth.token. The newmonitoring-stream.gateway.ts(namespace/monitoring)
will follow the same pattern directly rather than porting this commit.4104e0aae,242dcca74,69755fad8(compose/EMQX/install-gate changes) — superseded;
master’sdocker-compose.ymlalready definesTHINGSBOARD_BASE_URL,
THINGSBOARD_USERNAME,THINGSBOARD_PASSWORD,THINGSBOARD_INGEST_TOKENfor the backend
service (consumed by the existingThingsboardClientService). The EMQX/MQTT broker changes
in this group are unrelated to the monitoring feature and out of scope here.
Schema reconciliation
master and issue-104 each independently added a ThingsBoard device-id column to devices,
with incompatible names/types, and neither has production data yet:
master |
issue-104 |
|
|---|---|---|
| Migration | 1778500000000-AddTbDeviceIdToDevices.ts |
1779362177924-AddThingsboardDeviceIdToDevices.ts |
| Column | tb_device_id |
thingsboard_device_id |
| Type | varchar(100) |
uuid |
| Index | none | unique |
Resolution:
- Drop
master’sAddTbDeviceIdToDevicesmigration and itsdown-equivalent effect (no
production data exists, confirmed by user). - Add a new migration (fresh timestamp, after
master’s current head) implementing
issue-104’s version:thingsboard_device_id uuidwith a unique index. - Rename
Device.tbDeviceId→Device.thingsboardDeviceIdin the entity and everywhere it’s
referenced onmaster:thingsboard-client.service.ts(sendRpccall site),
devices.service.ts,device-command-dispatch.service.ts, and any specs.
Dead/insecure code removal
The GET /thingsboard/credentials flow is confirmed unused by any frontend page or component
(verified via full-repo search) and returns a plaintext password — remove entirely:
backend/src/thingsboard/thingsboard.controller.ts(the whole controller — it has no other
routes)ThingsboardService.getCredentialsinthingsboard.service.ts(delete the service if nothing
else uses it after this removal)TbCredentialsDtofrontend/src/stores/thingsboard.store.tsUser.thingsboardUsername/User.thingsboardPasswordcolumns — add a new migration to drop
both columns (no production data, confirmed by user) and remove the fields from theUser
entity. The original1745200000000-AddThingsboardCredentialsToUsersmigration file is left
in place (historical record), reverted by the new drop migration going forward.
ThingsboardClientService, thingsboard.module.ts’s existing provider wiring for that service,
and all of devices/telemetry/health usage of it are untouched — that is real, working
backend-to-ThingsBoard integration unrelated to this cleanup.
Verification plan
- Run
bun run test(Jest, neverbun test) for affected specs after each commit group is
ported: ThingsBoard adapter service, monitoring scope resolver, monitoring stream gateway,
monitoring controller (end-to-end tenant-scope contract test from group 7). - Run both new migrations up then down (
migration:run/ revert) on a dev database to confirm
symmetry: device id column swap, and theusersTB-credential column drop. - Run
bun run check(Biome) across changed workspaces. - Manual smoke test: call
/api/v1/monitoring/snapshotwith a valid JWT and confirm org-scoped
results; loadMonitoringPage.tsxat its current standalone/monitoringroute (relocation
into “Tổng quan” is a later spec) and confirm the realtime connection indicator and snapshot
table render.
Testing
Existing Jest specs ported in commit group 7 (and any spec files bundled with groups 1–4) must
pass unmodified in intent — only import paths / renamed fields (thingsboardDeviceId) change.
No new test scenarios are introduced in this phase; new test coverage for the deferred gaps
(#105–#110) belongs to their respective specs.