Partial support for device paging support on the server side, allows a user to view only a subset of the devices.
Ylian Saint-Hilaire committed
Aug 31, 2022 at 11:28 UTC
15c882f24ed62fe7e21d8b67d8af98401899da07
2 files changed
+142
-47
db.js
+30
-21
@@ -1329,20 +1329,21 @@ module.exports.CreateDB = function (parent, func) {
1329
func(err, performTypedRecordDecrypt(docs));
1330
});
1331
};
1332
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
1332
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
1333
+ if (limit == 0) { limit = -1; } // In SQLite, no limit is -1
1334
if (id && (id != '')) {
1334
- sqlDbQuery('SELECT doc FROM main WHERE (id = $1) AND (type = $2) AND (domain = $3) AND (extra IN (' + dbMergeSqlArray(meshes) + '))', [id, type, domain], function (err, docs) {
1335
+ sqlDbQuery('SELECT doc FROM main WHERE (id = $1) AND (type = $2) AND (domain = $3) AND (extra IN (' + dbMergeSqlArray(meshes) + ')) LIMIT $4 OFFSET $5', [id, type, domain, limit, skip], function (err, docs) {
1336
if (docs != null) { for (var i in docs) { delete docs[i].type; if (docs[i].links != null) { docs[i] = common.unEscapeLinksFieldName(docs[i]); } } }
1337
func(err, performTypedRecordDecrypt(docs));
1338
});
1339
} else {
1340
if (extrasids == null) {
1340
- sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND (extra IN (' + dbMergeSqlArray(meshes) + '))', [type, domain], function (err, docs) {
1341
+ sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND (extra IN (' + dbMergeSqlArray(meshes) + ')) LIMIT $3 OFFSET $4', [type, domain, limit, skip], function (err, docs) {
1342
if (docs != null) { for (var i in docs) { delete docs[i].type; if (docs[i].links != null) { docs[i] = common.unEscapeLinksFieldName(docs[i]); } } }
1343
func(err, performTypedRecordDecrypt(docs));
1344
});
1345
} else {
1345
- sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND ((extra IN (' + dbMergeSqlArray(meshes) + ')) OR (id IN (' + dbMergeSqlArray(extrasids) + ')))', [type, domain], function (err, docs) {
1346
+ sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND ((extra IN (' + dbMergeSqlArray(meshes) + ')) OR (id IN (' + dbMergeSqlArray(extrasids) + '))) LIMIT $3 OFFSET $4', [type, domain, limit, skip], function (err, docs) {
1347
if (docs != null) { for (var i in docs) { delete docs[i].type; if (docs[i].links != null) { docs[i] = common.unEscapeLinksFieldName(docs[i]); } } }
1348
func(err, performTypedRecordDecrypt(docs));
1349
});
@@ -1550,7 +1551,7 @@ module.exports.CreateDB = function (parent, func) {
1551
func(null, common.aceUnEscapeAllFieldNames(docs));
1552
});
1553
}
1553
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
1554
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
1555
if (meshes.length == 0) { func(null, []); return; }
1556
var query = obj.file.query('meshcentral').take(999999).filter('type', '==', type).filter('domain', '==', domain);
1557
if (id) { query = query.filter('_id', '==', id); }
@@ -1780,14 +1781,15 @@ module.exports.CreateDB = function (parent, func) {
1781
obj.GetAll = function (func) { sqlDbQuery('SELECT domain, doc FROM main', null, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
1782
obj.GetHash = function (id, func) { sqlDbQuery('SELECT doc FROM main WHERE id = $1', [id], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
1783
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM main WHERE type = $1 AND domain = $2', [type, domain], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); }); };
1783
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
1784
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
1785
+ if (limit == 0) { limit = 0xFFFFFFFF; }
1786
if (id && (id != '')) {
1785
- sqlDbQuery('SELECT doc FROM main WHERE (id = $1) AND (type = $2) AND (domain = $3) AND (extra = ANY ($4))', [id, type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1787
+ sqlDbQuery('SELECT doc FROM main WHERE (id = $1) AND (type = $2) AND (domain = $3) AND (extra = ANY ($4)) LIMIT $5 OFFSET $6', [id, type, domain, meshes, limit, skip], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1788
} else {
1789
if (extrasids == null) {
1788
- sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND (extra = ANY ($3))', [type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); }, true);
1790
+ sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND (extra = ANY ($3)) LIMIT $4 OFFSET $5', [type, domain, meshes, limit, skip], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); }, true);
1791
} else {
1790
- sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND ((extra = ANY ($3)) OR (id = ANY ($4)))', [type, domain, meshes, extrasids], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1792
+ sqlDbQuery('SELECT doc FROM main WHERE (type = $1) AND (domain = $2) AND ((extra = ANY ($3)) OR (id = ANY ($4))) LIMIT $5 OFFSET $6', [type, domain, meshes, extrasids, limit, skip], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1793
}
1794
}
1795
};
@@ -1947,13 +1949,14 @@ module.exports.CreateDB = function (parent, func) {
1949
obj.GetAll = function (func) { sqlDbQuery('SELECT domain, doc FROM main', null, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
1950
obj.GetHash = function (id, func) { sqlDbQuery('SELECT doc FROM main WHERE id = ?', [id], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
1951
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); }); };
1950
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
1952
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
1953
+ if (limit == 0) { limit = 0xFFFFFFFF; }
1954
if ((meshes == null) || (meshes.length == 0)) { meshes = ''; } // MySQL can't handle a query with IN() on an empty array, we have to use an empty string instead.
1955
if ((extrasids == null) || (extrasids.length == 0)) { extrasids = ''; } // MySQL can't handle a query with IN() on an empty array, we have to use an empty string instead.
1956
if (id && (id != '')) {
1954
- sqlDbQuery('SELECT doc FROM main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?)', [id, type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1957
+ sqlDbQuery('SELECT doc FROM main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?) LIMIT ? OFFSET ?', [id, type, domain, meshes, limit, skip], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1958
} else {
1956
- sqlDbQuery('SELECT doc FROM main WHERE type = ? AND domain = ? AND (extra IN (?) OR id IN (?))', [type, domain, meshes, extrasids], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1959
+ sqlDbQuery('SELECT doc FROM main WHERE type = ? AND domain = ? AND (extra IN (?) OR id IN (?)) LIMIT ? OFFSET ?', [type, domain, meshes, extrasids, limit, skip], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, performTypedRecordDecrypt(docs)); });
1960
}
1961
};
1962
obj.GetAllTypeNodeFiltered = function (nodes, domain, type, id, func) {
@@ -2177,15 +2180,21 @@ module.exports.CreateDB = function (parent, func) {
2180
obj.GetAll = function (func) { obj.file.find({}).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
2181
obj.GetHash = function (id, func) { obj.file.find({ _id: id }).project({ _id: 0, hash: 1 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
2182
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }).project({ type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
2180
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
2183
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
2184
if (extrasids == null) {
2182
- var x = { type: type, domain: domain, meshid: { $in: meshes } };
2185
+ const x = { type: type, domain: domain, meshid: { $in: meshes } };
2186
if (id) { x._id = id; }
2184
- obj.file.find(x, { type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2187
+ var f = obj.file.find(x, { type: 0 });
2188
+ if (skip > 0) f = f.skip(skip); // Skip records
2189
+ if (limit > 0) f = f.limit(limit); // Limit records
2190
+ f.toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2191
} else {
2186
- var x = { type: type, domain: domain, $or: [ { meshid: { $in: meshes } }, { _id: { $in: extrasids } } ] };
2192
+ const x = { type: type, domain: domain, $or: [ { meshid: { $in: meshes } }, { _id: { $in: extrasids } } ] };
2193
if (id) { x._id = id; }
2188
- obj.file.find(x, { type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2194
+ var f = obj.file.find(x, { type: 0 });
2195
+ if (skip > 0) f = f.skip(skip); // Skip records
2196
+ if (limit > 0) f = f.limit(limit); // Limit records
2197
+ f.toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2198
}
2199
};
2200
obj.GetAllTypeNodeFiltered = function (nodes, domain, type, id, func) {
@@ -2409,18 +2418,18 @@ module.exports.CreateDB = function (parent, func) {
2418
obj.GetAll = function (func) { obj.file.find({}, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
2419
obj.GetHash = function (id, func) { obj.file.find({ _id: id }, { _id: 0, hash: 1 }, func); };
2420
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
2412
- //obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) {
2421
+ //obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, skip, limit, func) {
2422
//var x = { type: type, domain: domain, meshid: { $in: meshes } };
2423
//if (id) { x._id = id; }
2424
//obj.file.find(x, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2425
//};
2417
- obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
2426
+ obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, skip, limit, func) {
2427
if (extrasids == null) {
2419
- var x = { type: type, domain: domain, meshid: { $in: meshes } };
2428
+ const x = { type: type, domain: domain, meshid: { $in: meshes } };
2429
if (id) { x._id = id; }
2430
obj.file.find(x, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2431
} else {
2423
- var x = { type: type, domain: domain, $or: [{ meshid: { $in: meshes } }, { _id: { $in: extrasids } }] };
2432
+ const x = { type: type, domain: domain, $or: [{ meshid: { $in: meshes } }, { _id: { $in: extrasids } }] };
2433
if (id) { x._id = id; }
2434
obj.file.find(x, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
2435
}
meshuser.js
+112
-26
@@ -105,6 +105,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
105
obj.domain = domain;
106
obj.ws = ws;
107
108
+ // Information related to the current page the user is looking at
109
+ obj.deviceSkip = 0; // How many devices to skip
110
+ obj.deviceLimit = 0; // How many devices to view
111
+ obj.visibleDevices = null; // An object of visible nodeid's if the user is in paging mode
112
+
113
// Check if we are a cross-domain administrator
114
if (parent.parent.config.settings.managecrossdomain && (parent.parent.config.settings.managecrossdomain.indexOf(user._id) >= 0)) { obj.crossDomain = true; }
115
@@ -414,6 +419,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
419
// If this session is logged in using a loginToken and the token is removed, disconnect.
420
if ((req.session.loginToken != null) && (typeof event == 'object') && (event.action == 'loginTokenChanged') && (event.removed != null) && (event.removed.indexOf(req.session.loginToken) >= 0)) { delete req.session; obj.close(); return; }
421
422
+ // If this user is not viewing all devices and paging, check if this event is in the current page
423
+ if (isEventWithinPage(ids) == false) return;
424
+
425
// Normally, only allow this user to receive messages from it's own domain.
426
// If the user is a cross domain administrator, allow some select messages from different domains.
427
if ((event.domain == null) || (event.domain == domain.id) || ((obj.crossDomain === true) && (allowedCrossDomainMessages.indexOf(event.action) >= 0))) {
@@ -706,7 +714,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
714
}
715
716
// Request a list of all nodes
709
- db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', command.id, function (err, docs) {
717
+ db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', command.id, obj.deviceSkip, obj.deviceLimit, function (err, docs) {
718
719
//console.log(err, docs, links, extraids, domain.id, 'node', command.id);
720
@@ -714,10 +722,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
722
parent.common.unEscapeAllLinksFieldName(docs);
723
724
var r = {};
725
+ if (obj.visibleDevices != null) { obj.visibleDevices = {}; }
726
for (i in docs) {
727
// Check device links, if a link points to an unknown user, remove it.
728
parent.cleanDevice(docs[i]);
729
730
+ // If we are paging, add the device to the page here
731
+ if (obj.visibleDevices != null) { obj.visibleDevices[docs[i]._id] = 1; }
732
+
733
// Remove any connectivity and power state information, that should not be in the database anyway.
734
// TODO: Find why these are sometimes saved in the db.
735
if (docs[i].conn != null) { delete docs[i].conn; }
@@ -788,7 +800,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
800
801
r[meshid].push(docs[i]);
802
}
791
- try { ws.send(JSON.stringify({ action: 'nodes', responseid: command.responseid, nodes: r, tag: command.tag })); } catch (ex) { }
803
+ const response = { action: 'nodes', responseid: command.responseid, nodes: r, tag: command.tag };
804
+ if (obj.visibleDevices != null) {
805
+ // If in paging mode, report back the skip and limit values
806
+ response.skip = obj.deviceSkip;
807
+ response.limit = obj.deviceLimit;
808
+ // TODO: Add total device count
809
+ }
810
+ try { ws.send(JSON.stringify(response)); } catch (ex) { }
811
});
812
break;
813
}
@@ -6128,14 +6147,30 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6147
}
6148
6149
function serverCommandLastConnects(command) {
6131
- const links = parent.GetAllMeshIdWithRights(user);
6132
- const extraids = getUserExtraIds();
6133
- db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', null, function (err, docs) {
6134
- if (docs == null) return;
6150
+ if (obj.visibleDevices == null) {
6151
+ // If we are not paging, get all devices visible to this user
6152
+ const links = parent.GetAllMeshIdWithRights(user);
6153
+ const extraids = getUserExtraIds();
6154
+ db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', null, obj.deviceSkip, obj.deviceLimit, function (err, docs) {
6155
+ if (docs == null) return;
6156
+
6157
+ // Create a list of node ids for this user and query them for last device connection time
6158
+ const ids = []
6159
+ for (var i in docs) { ids.push('lc' + docs[i]._id); }
6160
6161
+ // Pull list of last connections only for device owned by this user
6162
+ db.GetAllIdsOfType(ids, domain.id, 'lastconnect', function (err, docs) {
6163
+ if (docs == null) return;
6164
+ const response = {};
6165
+ for (var j in docs) { response[docs[j]._id.substring(2)] = docs[j].time; }
6166
+ obj.send({ action: 'lastconnects', lastconnects: response, tag: command.tag });
6167
+ });
6168
+ });
6169
+ } else {
6170
+ // If we are paging, we know what devices the user is look at
6171
// Create a list of node ids for this user and query them for last device connection time
6172
const ids = []
6138
- for (var i in docs) { ids.push('lc' + docs[i]._id); }
6173
+ for (var i in obj.visibleDevices) { ids.push('lc' + i); }
6174
6175
// Pull list of last connections only for device owned by this user
6176
db.GetAllIdsOfType(ids, domain.id, 'lastconnect', function (err, docs) {
@@ -6144,7 +6179,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6179
for (var j in docs) { response[docs[j]._id.substring(2)] = docs[j].time; }
6180
obj.send({ action: 'lastconnects', lastconnects: response, tag: command.tag });
6181
});
6147
- });
6182
+ }
6183
}
6184
6185
function serverCommandLoginCookie(command) {
@@ -7505,22 +7540,62 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
7540
7541
// Return detailed information about all nodes this user has access to
7542
function getAllDeviceDetailedInfo(type, func) {
7508
- // Get all device groups this user has access to
7509
- var links = parent.GetAllMeshIdWithRights(user);
7510
-
7511
- // Add any nodes with direct rights or any nodes with user group direct rights
7512
- var extraids = getUserExtraIds();
7513
-
7514
- // Request a list of all nodes
7515
- db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', null, function (err, docs) {
7516
- if (docs == null) { docs = []; }
7517
- parent.common.unEscapeAllLinksFieldName(docs);
7518
-
7519
- var results = [], resultPendingCount = 0;
7520
- for (i in docs) {
7521
- // Check device links, if a link points to an unknown user, remove it.
7522
- parent.cleanDevice(docs[i]);
7543
+ // If we are not paging, get all devices visible to this user
7544
+ if (obj.visibleDevices == null) {
7545
+
7546
+ // Get all device groups this user has access to
7547
+ var links = parent.GetAllMeshIdWithRights(user);
7548
+
7549
+ // Add any nodes with direct rights or any nodes with user group direct rights
7550
+ var extraids = getUserExtraIds();
7551
+
7552
+ // Request a list of all nodes
7553
+ db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', null, obj.deviceSkip, obj.deviceLimit, function (err, docs) {
7554
+ if (docs == null) { docs = []; }
7555
+ parent.common.unEscapeAllLinksFieldName(docs);
7556
+
7557
+ var results = [], resultPendingCount = 0;
7558
+ for (i in docs) {
7559
+ // Check device links, if a link points to an unknown user, remove it.
7560
+ parent.cleanDevice(docs[i]);
7561
+
7562
+ // Fetch the node from the database
7563
+ resultPendingCount++;
7564
+ const getNodeFunc = function (node, rights, visible) {
7565
+ if ((node != null) && (visible == true)) {
7566
+ const getNodeSysInfoFunc = function (err, docs) {
7567
+ const getNodeNetInfoFunc = function (err, docs) {
7568
+ var netinfo = null;
7569
+ if ((err == null) && (docs != null) && (docs.length == 1)) { netinfo = docs[0]; }
7570
+ resultPendingCount--;
7571
+ getNodeNetInfoFunc.results.push({ node: parent.CloneSafeNode(getNodeNetInfoFunc.node), sys: getNodeNetInfoFunc.sysinfo, net: netinfo });
7572
+ if (resultPendingCount == 0) { func(getNodeFunc.results, type); }
7573
+ }
7574
+ getNodeNetInfoFunc.results = getNodeSysInfoFunc.results;
7575
+ getNodeNetInfoFunc.nodeid = getNodeSysInfoFunc.nodeid;
7576
+ getNodeNetInfoFunc.node = getNodeSysInfoFunc.node;
7577
+ if ((err == null) && (docs != null) && (docs.length == 1)) { getNodeNetInfoFunc.sysinfo = docs[0]; }
7578
7579
+ // Query the database for network information
7580
+ db.Get('if' + getNodeSysInfoFunc.nodeid, getNodeNetInfoFunc);
7581
+ }
7582
+ getNodeSysInfoFunc.results = getNodeFunc.results;
7583
+ getNodeSysInfoFunc.nodeid = getNodeFunc.nodeid;
7584
+ getNodeSysInfoFunc.node = node;
7585
+
7586
+ // Query the database for system information
7587
+ db.Get('si' + getNodeFunc.nodeid, getNodeSysInfoFunc);
7588
+ } else { resultPendingCount--; }
7589
+ if (resultPendingCount == 0) { func(getNodeFunc.results.join('\r\n'), type); }
7590
+ }
7591
+ getNodeFunc.results = results;
7592
+ getNodeFunc.nodeid = docs[i]._id;
7593
+ parent.GetNodeWithRights(domain, user, docs[i]._id, getNodeFunc);
7594
+ }
7595
+ });
7596
+ } else {
7597
+ // If we are paging, we know what devices the user is look at
7598
+ for (var id in obj.visibleDevices) {
7599
// Fetch the node from the database
7600
resultPendingCount++;
7601
const getNodeFunc = function (node, rights, visible) {
@@ -7551,10 +7626,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
7626
if (resultPendingCount == 0) { func(getNodeFunc.results.join('\r\n'), type); }
7627
}
7628
getNodeFunc.results = results;
7554
- getNodeFunc.nodeid = docs[i]._id;
7555
- parent.GetNodeWithRights(domain, user, docs[i]._id, getNodeFunc);
7629
+ getNodeFunc.nodeid = id;
7630
+ parent.GetNodeWithRights(domain, user, id, getNodeFunc);
7631
}
7557
- });
7632
+ }
7633
}
7634
7635
// Display a notification message for this session only.
@@ -7721,5 +7796,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
7796
return authFactorCount;
7797
}
7798
7799
+ // Return true if the event is for a device that is part of the currently visible page
7800
+ function isEventWithinPage(ids) {
7801
+ if (obj.visibleDevices == null) return true; // Add devices are visible
7802
+ var r = true;
7803
+ for (var i in ids) {
7804
+ // If the event is for a visible device, return true
7805
+ if (ids[i].startsWith('node/')) { r = false; if (obj.visibleDevices[ids[i]] != null) return true; }
7806
+ }
7807
+ return r; // If this event is not for any specific device, return true
7808
+ }
7809
+
7810
return obj;
7811
};