fix(notifications): fire "new node" notification for direct Meshtastic nodes (#3796)#3799
Conversation
…c nodes (#3796) notifyNewNode only ran inside the sync DatabaseService.upsertNode() wrapper (used by the MQTT ingestion path). The direct serial/TCP Meshtastic path in meshtasticManager wrote nodes via databaseService.nodes.upsertNode() (the async repository), bypassing that wrapper entirely — so "Newly Found Node" Apprise/ push/desktop notifications never fired for directly-connected Meshtastic nodes. Test and traceroute notifications were unaffected (they call notificationService directly), and MeshCore has its own notifyNewNodeDiscovered callback, which is why only this path was broken. - Extract the incomplete->complete detection + dedup + notify logic (previously duplicated in the PG/MySQL and SQLite branches of upsertNode) into a shared DatabaseService.maybeNotifyNewNode() helper. - Add DatabaseService.upsertNodeAsync(): wraps the same repository write and runs the shared notification check. It short-circuits to a single Set lookup once a node has been notified, so the hot path stays cheap. - Route all 31 databaseService.nodes.upsertNode() calls in meshtasticManager through upsertNodeAsync(). The shared newNodeNotifiedSet dedups across the MQTT and direct paths, and the wasComplete check against persisted state means a device re-dumping its NodeDB on reconnect does not re-notify known nodes. - Tests: new upsertNodeAsync notification suite (fires on completion, skips incomplete, dedups, still persists); add delegating upsertNodeAsync to the four meshtasticManager mocks that assert on the upsert call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NnNR1gD45VxgB249xaTS4
|
Claude finished @Yeraze's task —— View job PR ReviewSummaryThis PR fixes a legitimate bug where "Newly Found Node" notifications never fired for directly-connected (serial/TCP) Meshtastic nodes. The root cause diagnosis is accurate and the fix is well-structured. Here's a detailed breakdown: Code Quality & Design
Dynamic Potential Issues
Race condition between Ghost suppression bypass in Test CoverageThe new test suite in
Gap: no test for the cross-source dedup behavior. A test asserting that node Gap: no test for the The 4 updated test mocks ( PerformanceThe SecurityNo security concerns. This is internal notification plumbing with no new API surface, no user-controlled inputs flowing into the notification path (node data comes from trusted Meshtastic hardware), and no new permissions or auth changes. Minor Nits
Overall AssessmentApprove with optional follow-ups. The bug fix is correct, the dedup/gate logic is sound, the test coverage is good, and the PR description accurately describes the change. The ghost suppression bypass is a pre-existing gap that this PR didn't introduce but slightly expands; it can be addressed in a separate issue if needed. |
) Add two regression tests requested in review: - a node already complete before the upsert (reconnect / NodeDB re-dump) does NOT re-notify (locks in the no-flood guarantee) - a node notified on src1 does NOT re-notify on src2 (documents the nodeNum-only keying of newNodeNotifiedSet) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NnNR1gD45VxgB249xaTS4
|
Claude finished @Yeraze's task —— View job PR ReviewTasks
SummaryThis PR correctly diagnoses and fixes a real bug: Code Quality & Design
Dynamic Potential IssuesGhost suppression bypass (
Test CoverageThe new suite at
The cross-source dedup test (line 791) and the The 4 updated mock files ( Minor nit: The PerformanceNo regressions. The SecurityNo concerns. Node data originates from trusted Meshtastic hardware over the existing transport layer. No new API surface, no user-controlled inputs, no new auth changes. Overall AssessmentApprove. The bug fix is correct, the abstraction is clean, test coverage is thorough (including the reconnect guard and cross-source dedup cases that were flagged in the previous review), and the PR description accurately reflects the implementation. The ghost suppression gap is pre-existing and worth a follow-up issue but is not a blocker. |
Summary
"Newly Found Node" notifications never fired for directly-connected (serial/TCP) Meshtastic nodes. The
notifyNewNodecall lived only inside the syncDatabaseService.upsertNode()wrapper, which the MQTT ingestion path uses — butmeshtasticManagerwrote nodes throughdatabaseService.nodes.upsertNode()(the async repository), bypassing the wrapper and its notification logic entirely. This routes the direct path through a notification-aware upsert so the feature works for the reporter's setup (Apprise + Discord, direct node).Root cause
notifyNewNodewas invoked only fromDatabaseService.upsertNode()(the sync wrapper).meshtasticManager.processNodeInfoMessageProtobuf()and ~30 other node writes calldatabaseService.nodes.upsertNode()directly → no notification.notificationServicedirectly (unaffected); MeshCore has its ownnotifyNewNodeDiscovered()callback (unaffected) — which is why only the direct Meshtastic path was broken.Changes
upsertNode) into a sharedDatabaseService.maybeNotifyNewNode()helper.DatabaseService.upsertNodeAsync()— wraps the same repository write and runs the shared notification check. Short-circuits to a singleSetlookup once a node has been notified, keeping the hot path cheap.databaseService.nodes.upsertNode()calls inmeshtasticManager.tsthroughupsertNodeAsync(). The sharednewNodeNotifiedSetdedups across the MQTT and direct paths, and thewasCompletecheck against persisted state means a device re-dumping its NodeDB on reconnect does not re-notify already-known nodes (no reconnect flood).Why reroute all sites (not just NodeInfo)
A node becomes "complete" (gets
longName+shortName+hwModel) primarily via NodeInfo, but also via the device's NodeDB bulk-sync on connect. Routing every node write through the gated, deduped check catches whichever path first completes a node, without spamming —isNodeCompleterejects"Node !…"placeholders and the dedup set prevents repeats.Issues Resolved
Fixes #3796
Documentation Updates
No documentation changes needed (internal notification plumbing; no API/config surface change).
Testing
tsconfig.server.json; only pre-existingTelemetryChart.tsxnoise remains)upsertNodeAsyncnotification suite: fires on incomplete→complete, skips incomplete nodes, dedups a second upsert, still persists via the repositorymeshtasticManagermocks that assert on the upsert call (delegatingupsertNodeAsync); all 655 meshtasticManager tests pass🤖 Generated with Claude Code