Bug fixes and fixed database performance problem.

This commit is contained in:
Ylian Saint-Hilaire 2019-02-16 12:56:33 -08:00
parent b7df070bd2
commit b824499b40
6 changed files with 95 additions and 53 deletions

47
db.js
View File

@ -41,6 +41,13 @@ module.exports.CreateDB = function (parent) {
var dbcollection = 'meshcentral';
if (obj.parent.args.mongodbcol) { dbcollection = obj.parent.args.mongodbcol; }
obj.file = db.collection(dbcollection);
// Setup MongoDB indexes
obj.file.createIndex({ type: 1, domain: 1, meshid: 1 }, { sparse: 1 }); // Speeds up GetAllTypeNoTypeField() and GetAllTypeNoTypeFieldMeshFiltered()
obj.file.createIndex({ email: 1 }, { sparse: 1 }); // Speeds up GetUserWithEmail() and GetUserWithVerifiedEmail()
obj.file.createIndex({ ids: 1, time: -1 }, { sparse: 1 }); // Speeds up GetEvents() and GetEventsWithLimit()
obj.file.createIndex({ type: 1, node: 1, time: -1 }, { sparse: 1 }); // Speeds up getPowerTimeline()
obj.file.createIndex({ mesh: 1 }, { sparse: 1 }); // Speeds up RemoveMesh()
} else {
// Use NeDB (The default)
obj.databaseType = 1;
@ -72,6 +79,13 @@ module.exports.CreateDB = function (parent) {
// Start NeDB
obj.file = new Datastore(datastoreOptions);
obj.file.persistence.setAutocompactionInterval(3600);
// Setup NeDB indexes
obj.file.ensureIndex({ fieldName: 'type' });
obj.file.ensureIndex({ fieldName: 'domain' });
obj.file.ensureIndex({ fieldName: 'meshid' });
obj.file.ensureIndex({ fieldName: 'node' });
obj.file.ensureIndex({ fieldName: 'email' });
}
obj.SetupDatabase = function (func) {
@ -232,27 +246,30 @@ module.exports.CreateDB = function (parent) {
} catch (ex) { return null; }
}
// Get the number of records in the database for various types, this is the slow NeDB way. TODO: MongoDB can use group() to do this faster.
// Get the number of records in the database for various types, this is the slow NeDB way.
// WARNING: This is a terrible query for database performance. Only do this when needed. This query will look at almost every document in the database.
obj.getStats = function (func) {
obj.file.count({ type: 'node' }, function (err, nodeCount) {
obj.file.count({ type: 'mesh' }, function (err, meshCount) {
obj.file.count({ type: 'power' }, function (err, powerCount) {
obj.file.count({ type: 'user' }, function (err, userCount) {
obj.file.count({ type: 'ifinfo' }, function (err, nodeInterfaceCount) {
obj.file.count({ type: 'note' }, function (err, noteCount) {
obj.file.count({ type: 'smbios' }, function (err, nodeSmbiosCount) {
obj.file.count({ type: 'lastconnect' }, function (err, nodeLastConnectCount) {
obj.file.count({ }, function (err, totalCount) {
func({ nodes: nodeCount, meshes: meshCount, powerEvents: powerCount, users: userCount, nodeInterfaces: nodeInterfaceCount, notes: noteCount, connectEvent: nodeLastConnectCount, smbios: nodeSmbiosCount, total: totalCount });
});
});
});
if (obj.databaseType == 2) {
// MongoDB version
obj.file.aggregate([{ "$group": { _id: "$type", count: { $sum: 1 } } }], function (err, docs) {
var counters = {}, totalCount = 0;
for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } }
func({ nodes: counters['node'], meshes: counters['mesh'], powerEvents: counters['power'], users: counters['user'], total: totalCount });
})
} else {
// NeDB version
obj.file.count({ type: 'node' }, function (err, nodeCount) {
obj.file.count({ type: 'mesh' }, function (err, meshCount) {
obj.file.count({ type: 'power' }, function (err, powerCount) {
obj.file.count({ type: 'user' }, function (err, userCount) {
obj.file.count({}, function (err, totalCount) {
func({ nodes: nodeCount, meshes: meshCount, powerEvents: powerCount, users: userCount, nodeInterfaces: nodeInterfaceCount, notes: noteCount, connectEvent: nodeLastConnectCount, smbios: nodeSmbiosCount, total: totalCount });
});
});
});
});
});
});
}
}
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.

View File

@ -178,15 +178,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Send current server statistics
obj.SendServerStats = function () {
obj.db.getStats(function (data) {
var os = require('os');
var stats = { action: 'serverstats', totalmem: os.totalmem(), freemem: os.freemem() };
if (obj.parent.parent.platform != 'win32') { stats.cpuavg = os.loadavg(); } // else { stats.cpuavg = [ 0.2435345, 0.523234234, 0.6435345345 ]; }
var serverStats = { "User Accounts": Object.keys(obj.parent.users).length, "Device Groups": Object.keys(obj.parent.meshes).length, "Connected Agents": Object.keys(obj.parent.wsagents).length, "Connected Users": Object.keys(obj.parent.wssessions2).length };
if (obj.parent.parent.mpsserver != null) { serverStats['Connected Intel® AMT'] = Object.keys(obj.parent.parent.mpsserver.ciraConnections).length; }
stats.values = { "Server State": serverStats, "Database": { "Records": data.total, "Users": data.users, "Device Groups": data.meshes, "Devices": data.nodes, "Device NetInfo": data.nodeInterfaces, "Device Power Event": data.powerEvents, "Notes": data.notes, "Connection Records": data.connectEvents, "SMBios": data.smbios } }
try { ws.send(JSON.stringify(stats)); } catch (ex) { }
});
var os = require('os');
var stats = { action: 'serverstats', totalmem: os.totalmem(), freemem: os.freemem() };
if (obj.parent.parent.platform != 'win32') { stats.cpuavg = os.loadavg(); } // else { stats.cpuavg = [ 0.2435345, 0.523234234, 0.6435345345 ]; }
var serverStats = { "User Accounts": Object.keys(obj.parent.users).length, "Device Groups": Object.keys(obj.parent.meshes).length, "Connected Agents": Object.keys(obj.parent.wsagents).length, "Connected Users": Object.keys(obj.parent.wssessions2).length };
if (obj.parent.parent.mpsserver != null) { serverStats['Connected Intel® AMT'] = Object.keys(obj.parent.parent.mpsserver.ciraConnections).length; }
stats.values = { "Server State": serverStats }
try { ws.send(JSON.stringify(stats)); } catch (ex) { }
}
// When data is received from the web socket
@ -256,7 +254,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'serverstats':
{
if ((user.siteadmin) != 0) {
if ((user.siteadmin & 21) != 0) { // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
if (obj.common.validateInt(command.interval, 1000, 1000000) == false) {
// Clear the timer
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); obj.serverStatsTimer = null; }
@ -1438,38 +1436,65 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'uploadagentcore':
{
if (user.siteadmin != 0xFFFFFFFF) break;
if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (obj.common.validateString(command.type, 1, 40) == false) break; // Check path
if (command.type == 'default') {
// Send the default core to the agent
obj.parent.parent.updateMeshCore(function () { obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'default'); });
} else if (command.type == 'clear') {
// Clear the mesh agent core on the mesh agent
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'clear');
} else if (command.type == 'recovery') {
// Send the recovery core to the agent
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'recovery');
} else if ((command.type == 'custom') && (obj.common.validateString(command.path, 1, 2048) == true)) {
// Send a mesh agent core to the mesh agent
var file = obj.parent.getServerFilePath(user, domain, command.path);
if (file != null) {
obj.parent.parent.fs.readFile(file.fullpath, 'utf8', function (err, data) {
if (err != null) {
data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'custom', data);
// Change the device
obj.db.Get(command.nodeid, function (err, nodes) {
if (nodes.length != 1) return;
var node = nodes[0];
// Get the mesh for this device
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] == null || (((mesh.links[user._id].rights & 16) == 0) && (user.siteadmin != 0xFFFFFFFF))) { return; }
if (command.type == 'default') {
// Send the default core to the agent
obj.parent.parent.updateMeshCore(function () { obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'default'); });
} else if (command.type == 'clear') {
// Clear the mesh agent core on the mesh agent
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'clear');
} else if (command.type == 'recovery') {
// Send the recovery core to the agent
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'recovery');
} else if ((command.type == 'custom') && (obj.common.validateString(command.path, 1, 2048) == true)) {
// Send a mesh agent core to the mesh agent
var file = obj.parent.getServerFilePath(user, domain, command.path);
if (file != null) {
obj.parent.parent.fs.readFile(file.fullpath, 'utf8', function (err, data) {
if (err != null) {
data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, 'custom', data);
}
});
}
});
}
}
}
});
break;
}
case 'agentdisconnect':
{
// Force mesh agent disconnection
if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (obj.common.validateInt(command.disconnectMode) == false) break; // Check disconnect mode
obj.parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
if (obj.common.validateInt(command.disconnectMode) == false) return; // Check disconnect mode
// Change the device
obj.db.Get(command.nodeid, function (err, nodes) {
if (nodes.length != 1) return;
var node = nodes[0];
// Get the mesh for this device
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] == null || (((mesh.links[user._id].rights & 16) == 0) && (user.siteadmin != 0xFFFFFFFF))) return;
// Force mesh agent disconnection
obj.parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
}
});
break;
}
case 'close':

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.2.8-m",
"version": "0.2.8-o",
"keywords": [
"Remote Management",
"Intel AMT",

File diff suppressed because one or more lines are too long

View File

@ -3539,7 +3539,7 @@
QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8));
QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0) && (userinfo.siteadmin == 0xFFFFFFFF));
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
// Setup/Refresh Intel AMT tab

View File

@ -2411,7 +2411,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
};
// Send the core module to the mesh agent
@ -2426,7 +2426,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) {
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) {
if (coretype == 'clear') {
// Clear the mesh agent core
agent.agentCoreCheck = 1000; // Tell the agent object we are using a custom core.