Fix RemoveAllNodeEvents callers passing only nodeid (silent no-op cleanup) (#7799)

`RemoveAllNodeEvents(domain, nodeid)` in every db.js backend starts with `if ((domain == null) || (nodeid == null)) return;` so a caller that passes only one argument leaves `nodeid` undefined and the early-return swallows the whole call. As a result, every device-removal path that should also be clearing the device's event history has been silently leaving those rows behind on every backend (NeDB/MongoDB TTL-expires them eventually; SQLite, PostgreSQL, MariaDB/MySQL and AceBase keep them until manual cleanup). Four callers fixed here: db.js:244 // removeInactiveDevices meshcentral.js:1101 // test/diagnostic-agent cleanup meshuser.js:2855 // user-triggered manual device removal meshagent.js:101 // agent self-removal on close (temporary/recovery flags) For each, the domain string is already in scope -- `node.domain` on the node-iteration paths and `domain.id` in meshagent (parameter at line 18 of CreateMeshAgent). A fifth caller exists at meshipkvm.js:244 with the same shape, but it is deliberately left for a follow-up. The IP-KVM auto-remove subsystem (Raritan / WebPowerSwitch, gated on the per-mesh auto-remove flag) is niche enough that I could not personally verify it and would rather not patch a path I cannot test. Loosely related (not duplicates): #7787 / #7788 / #7246 are about the time-based event-retention sweep being slow on large Postgres event tables. The per-device cleanup fixed here is a separate code path, but it contributes to the same observed symptom (event tables on SQL backends growing larger than expected) since every device removal since the bug landed has been skipping its event history. == How this was verified == Tested locally with a small harness using Node's built-in `node --test` (no new dependencies). Kept local to my fork rather than introducing a `test/` directory in MeshCentral; happy to send the harness as a separate PR if useful. 1. Arity scan: walks every .js file in the repo, finds every `RemoveAllNodeEvents(...)` call (paren-depth-aware, comment- and string-aware), and asserts each has exactly 2 args. Run on master, the test reports 5 buggy call sites. After this fix, only the intentionally-deferred meshipkvm.js:244 site remains -- the test makes that one easy to revisit. 2. Function-contract test: pins the canonical shape -- null/undefined domain or nodeid short-circuits (the F-DB-08 shape), valid pair reaches the datastore exactly once with the right query, and empty string '' (the default-domain id) is passed through (the guard is `== null`, not `!domain`, so '' is not nullish).

Adam DeWolf committed May 16, 2026 at 03:12 UTC a4d32306132f13ae195f9259cf3bec3955082b1d
4 files changed +4 -4
db.js
+1 -1
@@ -241,7 +241,7 @@ module.exports.CreateDB = function (parent, func) {
241 obj.Remove('si' + node._id); // Remove system information
242 obj.Remove('al' + node._id); // Remove error log last time
243 if (obj.RemoveSMBIOS) { obj.RemoveSMBIOS(node._id); } // Remove SMBios data
244 - obj.RemoveAllNodeEvents(node._id); // Remove all events for this node
244 + obj.RemoveAllNodeEvents(node.domain, node._id); // Remove all events for this node
245 obj.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
246 if (typeof node.pmt == 'string') { obj.Remove('pmt_' + node.pmt); } // Remove Push Messaging Token
247 obj.Get('ra' + node._id, function (err, nodes) {
meshagent.js
+1 -1
@@ -98,7 +98,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
98 db.Remove('si' + obj.dbNodeKey); // Remove system information
99 db.Remove('al' + obj.dbNodeKey); // Remove error log last time
100 if (db.RemoveSMBIOS) { db.RemoveSMBIOS(obj.dbNodeKey); } // Remove SMBios data
101 - db.RemoveAllNodeEvents(obj.dbNodeKey); // Remove all events for this node
101 + db.RemoveAllNodeEvents(domain.id, obj.dbNodeKey); // Remove all events for this node
102 db.removeAllPowerEventsForNode(obj.dbNodeKey); // Remove all power events for this node
103
104 // Event node deletion
meshcentral.js
+1 -1
@@ -1098,7 +1098,7 @@ function CreateMeshCentralServer(config, args) {
1098 db.Remove('lc' + node._id); // Remove last connect time
1099 db.Remove('si' + node._id); // Remove system information
1100 if (db.RemoveSMBIOS) { db.RemoveSMBIOS(node._id); } // Remove SMBios data
1101 - db.RemoveAllNodeEvents(node._id); // Remove all events for this node
1101 + db.RemoveAllNodeEvents(node.domain, node._id); // Remove all events for this node
1102 db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
1103 if (typeof node.pmt == 'string') { db.Remove('pmt_' + node.pmt); } // Remove Push Messaging Token
1104 db.Get('ra' + node._id, function (err, nodes) {
meshuser.js
+1 -1
@@ -2852,7 +2852,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2852 db.Remove('si' + node._id); // Remove system information
2853 db.Remove('al' + node._id); // Remove error log last time
2854 if (db.RemoveSMBIOS) { db.RemoveSMBIOS(node._id); } // Remove SMBios data
2855 - db.RemoveAllNodeEvents(node._id); // Remove all events for this node
2855 + db.RemoveAllNodeEvents(node.domain, node._id); // Remove all events for this node
2856 db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
2857 if (typeof node.pmt == 'string') { db.Remove('pmt_' + node.pmt); } // Remove Push Messaging Token
2858 db.Get('ra' + node._id, function (err, nodes) {