Added basic AceBase support (#4398)

This commit is contained in:
Ylian Saint-Hilaire 2022-08-13 18:32:17 -07:00
parent 92e3d2e528
commit 1f239481b7
5 changed files with 296 additions and 9 deletions

View File

@ -150,7 +150,7 @@ module.exports.objKeysToLower = function (obj, exceptions) {
return obj; return obj;
}; };
// Escape and unexcape feild names so there are no invalid characters for MongoDB // Escape and unescape feild names so there are no invalid characters for MongoDB
module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24'); }; module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24'); };
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); }; module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
@ -161,6 +161,12 @@ module.exports.unEscapeLinksFieldName = function (doc) { if (doc.links != null)
//module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; }; //module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; };
module.exports.unEscapeAllLinksFieldName = function (docs) { for (var i in docs) { docs[i] = module.exports.unEscapeLinksFieldName(docs[i]); } return docs; }; module.exports.unEscapeAllLinksFieldName = function (docs) { for (var i in docs) { docs[i] = module.exports.unEscapeLinksFieldName(docs[i]); } return docs; };
// Escape field names for aceBase
var aceEscFields = ['links', 'ssh', 'rdp', 'notify'];
module.exports.aceEscapeFieldNames = function (docx) { var doc = Object.assign({}, docx); for (var k in aceEscFields) { if (typeof doc[aceEscFields[k]] == 'object') { doc[aceEscFields[k]] = Object.assign({}, doc[aceEscFields[k]]); for (var i in doc[aceEscFields[k]]) { var ue = encodeURIComponent(i); if (ue !== i) { doc[aceEscFields[k]][ue] = doc[aceEscFields[k]][i]; delete doc[aceEscFields[k]][i]; } } } } return doc; };
module.exports.aceUnEscapeFieldNames = function (doc) { for (var k in aceEscFields) { if (typeof doc[aceEscFields[k]] == 'object') { for (var j in doc[aceEscFields[k]]) { var ue = decodeURIComponent(j); if (ue !== j) { doc[aceEscFields[k]][ue] = doc[aceEscFields[k]][j]; delete doc[aceEscFields[k]][j]; } } } } return doc; };
module.exports.aceUnEscapeAllFieldNames = function (docs) { for (var i in docs) { docs[i] = module.exports.aceUnEscapeFieldNames(docs[i]); } return docs; };
// Validation methods // Validation methods
module.exports.validateString = function (str, minlen, maxlen) { return ((str != null) && (typeof str == 'string') && ((minlen == null) || (str.length >= minlen)) && ((maxlen == null) || (str.length <= maxlen))); }; module.exports.validateString = function (str, minlen, maxlen) { return ((str != null) && (typeof str == 'string') && ((minlen == null) || (str.length >= minlen)) && ((maxlen == null) || (str.length <= maxlen))); };
module.exports.validateInt = function (int, minval, maxval) { return ((int != null) && (typeof int == 'number') && ((minval == null) || (int >= minval)) && ((maxval == null) || (int <= maxval))); }; module.exports.validateInt = function (int, minval, maxval) { return ((int != null) && (typeof int == 'number') && ((minval == null) || (int >= minval)) && ((maxval == null) || (int <= maxval))); };

271
db.js
View File

@ -235,7 +235,10 @@ module.exports.CreateDB = function (parent, func) {
obj.removeDomain = function (domainName, func) { obj.removeDomain = function (domainName, func) {
var pendingCalls; var pendingCalls;
// Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now. // Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now.
if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) { if (obj.databaseType == 7) {
// AceBase
} else if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) {
// MariaDB, MySQL or PostgreSQL // MariaDB, MySQL or PostgreSQL
pendingCalls = 2; pendingCalls = 2;
sqlDbQuery('DELETE FROM main WHERE domain = $1', [domainName], function () { if (--pendingCalls == 0) { func(); } }); sqlDbQuery('DELETE FROM main WHERE domain = $1', [domainName], function () { if (--pendingCalls == 0) { func(); } });
@ -260,7 +263,10 @@ module.exports.CreateDB = function (parent, func) {
// TODO: Remove all meshes that dont have any links // TODO: Remove all meshes that dont have any links
// Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now. // Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now.
if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) { if (obj.databaseType == 7) {
// AceBase
} else if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) {
// MariaDB, MySQL or PostgreSQL // MariaDB, MySQL or PostgreSQL
obj.RemoveAllOfType('event', function () { }); obj.RemoveAllOfType('event', function () { });
obj.RemoveAllOfType('power', function () { }); obj.RemoveAllOfType('power', function () { });
@ -371,7 +377,10 @@ module.exports.CreateDB = function (parent, func) {
if (meshChange) { obj.Set(docs[i]); } if (meshChange) { obj.Set(docs[i]); }
} }
} }
if (obj.databaseType == 6) { if (obj.databaseType == 7) {
// AceBase
} else if (obj.databaseType == 6) {
// Postgres // Postgres
sqlDbQuery('DELETE FROM Main WHERE ((extra != NULL) AND (extra LIKE (\'mesh/%\')) AND (extra != ANY ($1)))', [meshlist], function (err, response) { }); sqlDbQuery('DELETE FROM Main WHERE ((extra != NULL) AND (extra LIKE (\'mesh/%\')) AND (extra != ANY ($1)))', [meshlist], function (err, response) { });
} else if ((obj.databaseType == 4) || (obj.databaseType == 5)) { } else if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
@ -429,7 +438,10 @@ module.exports.CreateDB = function (parent, func) {
// Get the number of records in the database for various types, this is the slow NeDB way. // 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. // 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.getStats = function (func) {
if (obj.databaseType == 6) { if (obj.databaseType == 7) {
// AceBase
// TODO
} else if (obj.databaseType == 6) {
// PostgreSQL // PostgreSQL
// TODO // TODO
} else if (obj.databaseType == 5) { } else if (obj.databaseType == 5) {
@ -641,7 +653,15 @@ module.exports.CreateDB = function (parent, func) {
}); });
} }
if (parent.args.mariadb || parent.args.mysql) { if (parent.args.acebase) {
// AceBase database setup
obj.databaseType = 7;
const { AceBase } = require('acebase');
// For information on AceBase sponsor: https://github.com/appy-one/acebase/discussions/100
obj.file = new AceBase('meshcentral', { sponsor: ((typeof parent.args.acebase == 'object') && (parent.args.acebase.sponsor)), logLevel: 'error', storage: { path: parent.datapath } });
// Get all the databases ready
obj.file.ready(function () { setupFunctions(func); }); // Completed setup of AceBase
} else if (parent.args.mariadb || parent.args.mysql) {
var connectinArgs = (parent.args.mariadb) ? parent.args.mariadb : parent.args.mysql; var connectinArgs = (parent.args.mariadb) ? parent.args.mariadb : parent.args.mysql;
var dbname = (connectinArgs.database != null) ? connectinArgs.database : 'meshcentral'; var dbname = (connectinArgs.database != null) ? connectinArgs.database : 'meshcentral';
@ -1179,7 +1199,244 @@ module.exports.CreateDB = function (parent, func) {
} }
function setupFunctions(func) { function setupFunctions(func) {
if (obj.databaseType == 6) { if (obj.databaseType == 7) {
// Database actions on the main collection (AceBase)
obj.Set = function (data, func) {
data = common.escapeLinksFieldNameEx(data);
var xdata = performTypedRecordEncrypt(data);
obj.dbCounters.fileSet++;
obj.file.ref('meshcentral/' + encodeURIComponent(xdata._id)).set(common.aceEscapeFieldNames(xdata)).then(function (ref) { if (func) { func(); } })
};
obj.Get = function (id, func) {
obj.file.ref('meshcentral/' + encodeURIComponent(id)).get(function (snapshot) {
if (snapshot.exists()) { func(null, performTypedRecordDecrypt([common.aceUnEscapeFieldNames(snapshot.val())])); } else { func(null, []); }
});
};
obj.GetAll = function (func) {
obj.file.query('meshcentral').get(function (snapshots) {
const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, common.aceUnEscapeAllFieldNames(docs));
});
};
obj.GetHash = function (id, func) {
obj.file.ref('meshcentral/' + encodeURIComponent(id)).get({ include: ['hash'] }, function (snapshot) {
if (snapshot.exists()) { func(null, snapshot.val()); } else { func(null, null); }
});
};
obj.GetAllTypeNoTypeField = function (type, domain, func) {
obj.file.query('meshcentral').take(999999).filter('type', '==', type).filter('domain', '==', domain).get({ exclude: ['type'] }, function (snapshots) {
const docs = [];
for (var i in snapshots) { const x = snapshots[i].val(); docs.push(x); }
func(null, common.aceUnEscapeAllFieldNames(docs));
});
}
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
if (meshes.length == 0) { func(null, []); return; }
var query = obj.file.query('meshcentral').take(999999).filter('type', '==', type).filter('domain', '==', domain);
if (id) { query = query.filter('_id', '==', id); }
if (extrasids == null) {
query = query.filter('meshid', 'in', meshes);
query.get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); });
} else {
// TODO: This is a slow query as we did not find a filter-or-filter, so we query everything and filter manualy.
query.get(function (snapshots) {
const docs = [];
for (var i in snapshots) { const x = snapshots[i].val(); if ((extrasids.indexOf(x._id) >= 0) || (meshes.indexOf(x.meshid) >= 0)) { docs.push(x); } }
func(null, performTypedRecordDecrypt(docs));
});
}
};
obj.GetAllTypeNodeFiltered = function (nodes, domain, type, id, func) {
var query = obj.file.query('meshcentral').take(999999).filter('type', '==', type).filter('domain', '==', domain).filter('nodeid', 'in', nodes);
if (id) { query = query.filter('_id', '==', id); }
query.get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); });
};
obj.GetAllType = function (type, func) {
obj.file.query('meshcentral').take(999999).filter('type', '==', type).get(function (snapshots) {
const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); }
func(null, common.aceUnEscapeAllFieldNames(performTypedRecordDecrypt(docs)));
});
};
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.query('meshcentral').take(999999).filter('_id', 'in', ids).filter('domain', '==', domain).filter('type', '==', type).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.query('meshcentral').take(999999).filter('type', '==', 'user').filter('domain', '==', domain).filter('email', '==', email).get({ exclude: ['type'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.query('meshcentral').take(999999).filter('type', '==', 'user').filter('domain', '==', domain).filter('email', '==', email).filter('emailVerified', '==', true).get({ exclude: ['type'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.Remove = function (id, func) { obj.file.ref('meshcentral/' + encodeURIComponent(id)).remove().then(function () { if (func) { func(); } }); };
obj.RemoveAll = function (func) { obj.file.query('meshcentral').remove().then(function () { if (func) { func(); } }); };
obj.RemoveAllOfType = function (type, func) { obj.file.query('meshcentral').filter('type', '==', type).remove().then(function () { if (func) { func(); } }); };
obj.InsertMany = function (data, func) { var count = data.length; for (var i in data) { obj.file.ref('meshcentral/' + encodeURIComponent(data[i]._id)).set(common.aceEscapeFieldNames(data[i])).then(function (ref) { if (func && (--count == 0)) { func(); } }) } }; // Insert records directly, no link escaping
obj.RemoveMeshDocuments = function (id) { obj.file.query('meshcentral').filter('meshid', '==', id).remove(); obj.file.ref('meshcentral/' + encodeURIComponent('nt' + id)).remove(); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { obj.file.query('meshcentral').filter('domain', '==', domain).remove().then(function () { if (func) { func(); } }); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
obj.getLocalAmtNodes = function (func) { obj.file.query('meshcentral').take(999999).filter('type', '==', 'node').filter('host', 'exists').filter('host', '!=', null).filter('intelamt', 'exists').get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.getAmtUuidMeshNode = function (domainid, mtype, uuid, func) { obj.file.query('meshcentral').take(999999).filter('type', '==', 'node').filter('domain', '==', domainid).filter('mtype', '!=', mtype).filter('intelamt.uuid', '==', uuid).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.isMaxType = function (max, type, domainid, func) { if (max == null) { func(false); } else { obj.file.query('meshcentral').take(999999).filter('type', '==', type).filter('domain', '==', domainid).get({ snapshots: false }, function (snapshots) { func((snapshots.length > max), snapshots.length); }); } }
// Database actions on the events collection
obj.GetAllEvents = function (func) { obj.file.query('events').take(999999).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); }); };
obj.StoreEvent = function (event, func) {
if (typeof event.account == 'object') { event = Object.assign({}, event); event.account = common.aceEscapeFieldNames(event.account); }
obj.dbCounters.eventsSet++;
obj.file.ref('events').push(event).then(function (userRef) { if (func) { func(); } });
};
obj.GetEvents = function (ids, domain, func) {
// This request is slow since we have not found a .filter() that will take two arrays and match a single item.
obj.file.query('events').filter('domain', '==', domain).take(999999).sort('time', false).get({ exclude: ['_id', 'domain', 'node', 'type'] }, function (snapshots) {
const docs = [];
for (var i in snapshots) {
const doc = snapshots[i].val();
if ((doc.ids == null) || (!Array.isArray(doc.ids))) continue;
var found = false;
for (var j in doc.ids) { if (ids.indexOf(doc.ids[j]) >= 0) { found = true; } } // Check if one of the items in both arrays matches
if (found) { delete doc.ids; if (typeof doc.account == 'object') { doc.account = common.aceUnEscapeFieldNames(doc.account); } docs.push(doc); }
}
func(null, docs);
});
};
obj.GetEventsWithLimit = function (ids, domain, limit, func) {
// This request is slow since we have not found a .filter() that will take two arrays and match a single item.
obj.file.query('events').filter('domain', '==', domain).take(limit).sort('time', false).get({ exclude: ['_id', 'domain', 'node', 'type'] }, function (snapshots) {
const docs = [];
for (var i in snapshots) {
const doc = snapshots[i].val();
if ((doc.ids == null) || (!Array.isArray(doc.ids))) continue;
var found = false;
for (var j in doc.ids) { if (ids.indexOf(doc.ids[j]) >= 0) { found = true; } } // Check if one of the items in both arrays matches
if (found) { delete doc.ids; if (typeof doc.account == 'object') { doc.account = common.aceUnEscapeFieldNames(doc.account); } docs.push(doc); }
}
func(null, docs);
});
};
obj.GetUserEvents = function (ids, domain, userid, func) {
obj.file.query('events').take(999999).filter('domain', '==', domain).filter('userid', 'in', userid).filter('ids', 'in', ids).sort('time', false).get({ exclude: ['_id', 'domain', 'node', 'type', 'ids'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.GetUserEventsWithLimit = function (ids, domain, userid, limit, func) {
obj.file.query('events').take(limit).filter('domain', '==', domain).filter('userid', 'in', userid).filter('ids', 'in', ids).sort('time', false).get({ exclude: ['_id', 'domain', 'node', 'type', 'ids'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.GetEventsTimeRange = function (ids, domain, msgids, start, end, func) {
obj.file.query('events').take(999999).filter('domain', '==', domain).filter('ids', 'in', ids).filter('msgid', 'in', msgids).filter('time', 'between', [start, end]).sort('time', false).get({ exclude: ['type', '_id', 'domain', 'node'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.GetUserLoginEvents = function (domain, userid, func) {
obj.file.query('events').take(999999).filter('domain', '==', domain).filter('action', 'in', ['authfail', 'login']).filter('userid', '==', userid).filter('msgArgs', 'exists').sort('time', false).get({ include: ['action', 'time', 'msgid', 'msgArgs', 'tokenName'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) {
obj.file.query('events').take(limit).filter('domain', '==', domain).filter('nodeid', '==', nodeid).sort('time', false).get({ exclude: ['type', 'etype', '_id', 'domain', 'ids', 'node', 'nodeid'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.GetNodeEventsSelfWithLimit = function (nodeid, domain, userid, limit, func) {
obj.file.query('events').take(limit).filter('domain', '==', domain).filter('nodeid', '==', nodeid).filter('userid', '==', userid).sort('time', false).get({ exclude: ['type', 'etype', '_id', 'domain', 'ids', 'node', 'nodeid'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.RemoveAllEvents = function (domain) {
obj.file.query('events').take(999999).filter('domain', '==', domain).remove().then(function () { if (func) { func(); } });;
};
obj.RemoveAllNodeEvents = function (domain, nodeid) {
if ((domain == null) || (nodeid == null)) return;
obj.file.query('events').take(999999).filter('domain', '==', domain).filter('nodeid', '==', nodeid).remove().then(function () { if (func) { func(); } });;
};
obj.RemoveAllUserEvents = function (domain, userid) {
if ((domain == null) || (userid == null)) return;
obj.file.query('events').take(999999).filter('domain', '==', domain).filter('userid', '==', userid).remove().then(function () { if (func) { func(); } });;
};
obj.GetFailedLoginCount = function (userid, domainid, lastlogin, func) {
obj.file.query('events').take(999999).filter('domain', '==', domainid).filter('userid', '==', userid).filter('time', '>', lastlogin).sort('time', false).get({ snapshots: false }, function (snapshots) { func(null, snapshots.length); });
}
// Database actions on the power collection
obj.getAllPower = function (func) {
obj.file.query('power').take(999999).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.storePowerEvent = function (event, multiServer, func) {
if (multiServer != null) { event.server = multiServer.serverid; }
obj.file.ref('power').push(event).then(function (userRef) { if (func) { func(); } });
};
obj.getPowerTimeline = function (nodeid, func) {
obj.file.query('power').take(999999).filter('nodeid', 'in', ['*', nodeid]).sort('time').get({ exclude: ['_id', 'nodeid', 's'] }, function (snapshots) {
const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs);
});
};
obj.removeAllPowerEvents = function () {
obj.file.query('power').take(999999).remove().then(function () { if (func) { func(); } });
};
obj.removeAllPowerEventsForNode = function (nodeid) {
if (nodeid == null) return;
obj.file.query('power').take(999999).filter('nodeid', '==', nodeid).remove().then(function () { if (func) { func(); } });
};
// Database actions on the SMBIOS collection
if (obj.smbiosfile != null) {
obj.GetAllSMBIOS = function (func) {
obj.file.query('smbios').take(999999).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
obj.SetSMBIOS = function (smbios, func) {
obj.file.ref('meshcentral/' + encodeURIComponent(smbios._id)).set(smbios).then(function (ref) { if (func) { func(); } })
};
obj.RemoveSMBIOS = function (id) {
obj.file.query('smbios').filter('_id', 'in', id).take(999999).remove().then(function () { if (func) { func(); } });
};
obj.GetSMBIOS = function (id, func) {
obj.file.query('smbios').filter('_id', 'in', id).take(1).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); });
};
}
// Database actions on the Server Stats collection
obj.SetServerStats = function (data, func) {
obj.file.ref('stats').push(data).then(function (userRef) { if (func) { func(); } });
};
obj.GetServerStats = function (hours, func) {
var t = new Date();
t.setTime(t.getTime() - (60 * 60 * 1000 * hours));
obj.file.query('stats').take(999999).filter('time', '>', t).get({ exclude: ['_id', 'cpu'] }, function (snapshots) {
const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs);
});
};
// Read a configuration file from the database
obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); }
// Write a configuration file to the database
obj.setConfigFile = function (path, data, func) { obj.Set({ _id: 'cfile/' + path, type: 'cfile', data: data.toString('base64') }, func); }
// List all configuration files
obj.listConfigFiles = function (func) {
obj.file.query('meshcentral').take(999999).filter('type', '==', 'cfile').sort('_id').get(function (snapshots) {
const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs);
});
}
// Get all configuration files
obj.getAllConfigFiles = function (password, func) {
obj.file.query('meshcentral').take(999999).filter('type', '==', 'cfile').sort('_id').get(function (snapshots) {
const docs = [];
for (var i in snapshots) { docs.push(snapshots[i].val()); }
var r = null;
for (var i = 0; i < docs.length; i++) {
var name = docs[i]._id.split('/')[1];
var data = obj.decryptData(password, docs[i].data);
if (data != null) { if (r == null) { r = {}; } r[name] = data; }
}
func(r);
});
}
// Get database information
obj.getDbStats = function (func) {
obj.stats = { c: 5 };
obj.file.query('meshcentral').take(999999).get({ snapshots: false }, function (snapshots) { obj.stats.meshcentral = snapshots.length; if (--obj.stats.c == 0) { delete obj.stats.c; func(obj.stats); } });
obj.file.query('events').take(999999).get({ snapshots: false }, function (snapshots) { obj.stats.events = snapshots.length; if (--obj.stats.c == 0) { delete obj.stats.c; func(obj.stats); } });
obj.file.query('power').take(999999).get({ snapshots: false }, function (snapshots) { obj.stats.power = snapshots.length; if (--obj.stats.c == 0) { delete obj.stats.c; func(obj.stats); } });
obj.file.query('smbios').take(999999).get({ snapshots: false }, function (snapshots) { obj.stats.smbios = snapshots.length; if (--obj.stats.c == 0) { delete obj.stats.c; func(obj.stats); } });
obj.file.query('stats').take(999999).get({ snapshots: false }, function (snapshots) { obj.stats.serverstats = snapshots.length; if (--obj.stats.c == 0) { delete obj.stats.c; func(obj.stats); } });
}
// Plugin operations
if (obj.pluginsActive) {
obj.addPlugin = function (plugin, func) { plugin.type = 'plugin'; obj.file.ref('plugin/' + encodeURIComponent(plugin._id)).set(plugin).then(function (ref) { if (func) { func(); } }) }; // Add a plugin
obj.getPlugins = function (func) { obj.file.query('plugin').take(999999).sort('name').get({ exclude: ['type'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); }); }; // Get all plugins
obj.getPlugin = function (id, func) { obj.file.query('plugin').take(999999).filter('_id', '==', id).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, docs); }); }; // Get plugin
obj.deletePlugin = function (id, func) { obj.file.ref('plugin/' + encodeURIComponent(id)).remove().then(function () { if (func) { func(); } }); }; // Delete plugin
obj.setPluginStatus = function (id, status, func) { obj.file.ref('plugin/' + encodeURIComponent(id)).update(args).then(function (ref) { if (func) { func(); } }) };
obj.updatePlugin = function (id, args, func) { delete args._id; obj.file.ref('plugin/' + encodeURIComponent(id)).set(args).then(function (ref) { if (func) { func(); } }) };
}
} else if (obj.databaseType == 6) {
// Database actions on the main collection (Postgres) // Database actions on the main collection (Postgres)
obj.Set = function (value, func) { obj.Set = function (value, func) {
obj.dbCounters.fileSet++; obj.dbCounters.fileSet++;
@ -2003,7 +2260,7 @@ module.exports.CreateDB = function (parent, func) {
const newAutoBackupPath = parent.path.join(backupPath, newAutoBackupFile); const newAutoBackupPath = parent.path.join(backupPath, newAutoBackupFile);
r += 'DB Name: ' + dbname + '\r\n'; r += 'DB Name: ' + dbname + '\r\n';
r += 'DB Type: ' + ['None', 'NeDB', 'MongoJS', 'MongoDB', 'MariaDB', 'MySQL'][obj.databaseType] + '\r\n'; r += 'DB Type: ' + ['None', 'NeDB', 'MongoJS', 'MongoDB', 'MariaDB', 'MySQL', 'AceBase'][obj.databaseType] + '\r\n';
r += 'BackupPath: ' + backupPath + '\r\n'; r += 'BackupPath: ' + backupPath + '\r\n';
r += 'newAutoBackupFile: ' + newAutoBackupFile + '\r\n'; r += 'newAutoBackupFile: ' + newAutoBackupFile + '\r\n';
r += 'newAutoBackupPath: ' + newAutoBackupPath + '\r\n'; r += 'newAutoBackupPath: ' + newAutoBackupPath + '\r\n';

View File

@ -35,6 +35,13 @@
} }
} }
}, },
"acebase": {
"type": "object",
"description": "Add this section to enable AceBase database support, this is a local database system much like NeDB.",
"properties": {
"sponsor": { "type": "boolean", "default": false, "description": "Set true to remove the AceBase banner on startup." },
}
},
"mySQL": { "mySQL": {
"type": "object", "type": "object",
"description": "Add this section to connect MeshCentral to a MySQL database instance.", "description": "Add this section to connect MeshCentral to a MySQL database instance.",

View File

@ -802,6 +802,20 @@ function CreateMeshCentralServer(config, args) {
require('./db.js').CreateDB(obj, require('./db.js').CreateDB(obj,
function (db) { function (db) {
//db.Get('user//admin', function (err, docs) { console.log('GetResult', err, docs); });
//db.Set({ _id: 'user//admin', type: 'user', domain: 'a', test: 'this is a user' }, function () { console.log('SetResult'); });
//db.Get('user//admin', function (err, docs) { console.log('GetResult', err, docs); });
//db.GetAll(function (err, docs) { console.log('GetAll', err, docs); });
//db.GetAllTypeNoTypeField('user', 'a', function (err, docs) { console.log('GetAllTypeNoTypeField', err, docs); });
//db.isMaxType(10, 'user', 'a', function (max, count) { console.log('yy', max, count); })
//db.StoreEvent({ test: "this is an event" }, function () { console.log('event stored'); });
//db.GetAllEvents(function (err, docs) { console.log('events', docs); });
//return;
obj.db = db; obj.db = db;
obj.db.SetupDatabase(function (dbversion) { obj.db.SetupDatabase(function (dbversion) {
// See if any database operations needs to be completed // See if any database operations needs to be completed
@ -1198,12 +1212,13 @@ function CreateMeshCentralServer(config, args) {
// Lower case all keys in the config file // Lower case all keys in the config file
common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']);
// Grad some of the values from the original config.json file if present. // Grab some of the values from the original config.json file if present.
config2['mysql'] = config['mysql']; config2['mysql'] = config['mysql'];
config2['mariadb'] = config['mariadb']; config2['mariadb'] = config['mariadb'];
config2['mongodb'] = config['mongodb']; config2['mongodb'] = config['mongodb'];
config2['mongodbcol'] = config['mongodbcol']; config2['mongodbcol'] = config['mongodbcol'];
config2['dbencryptkey'] = config['dbencryptkey']; config2['dbencryptkey'] = config['dbencryptkey'];
config2['acebase'] = config['acebase'];
// We got a new config.json from the database, let's use it. // We got a new config.json from the database, let's use it.
config = obj.config = config2; config = obj.config = config2;
@ -3894,6 +3909,7 @@ function mainStart() {
if (config.settings.mongodb != null) { modules.push('mongodb@4.1.0'); modules.push('saslprep'); } // Add MongoDB, official driver. if (config.settings.mongodb != null) { modules.push('mongodb@4.1.0'); modules.push('saslprep'); } // Add MongoDB, official driver.
if (config.settings.postgres != null) { modules.push('pg@8.7.1'); modules.push('pgtools@0.3.2'); } // Add Postgres, Postgres driver. if (config.settings.postgres != null) { modules.push('pg@8.7.1'); modules.push('pgtools@0.3.2'); } // Add Postgres, Postgres driver.
if (config.settings.mariadb != null) { modules.push('mariadb'); } // Add MariaDB, official driver. if (config.settings.mariadb != null) { modules.push('mariadb'); } // Add MariaDB, official driver.
if (config.settings.acebase != null) { modules.push('acebase'); } // Add AceBase, official driver.
if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module. if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module.
if (config.settings.plugins != null) { modules.push('semver'); } // Required for version compat testing and update checks if (config.settings.plugins != null) { modules.push('semver'); } // Required for version compat testing and update checks
if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent'); } // Required for HTTP/HTTPS proxy support if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent'); } // Required for HTTP/HTTPS proxy support

View File

@ -3,6 +3,7 @@
"__comment__": "This is a sample configuration file, all values and sections that start with underscore (_) are ignored. Edit a section and remove the _ in front of the name. Refer to the user's guide for details.", "__comment__": "This is a sample configuration file, all values and sections that start with underscore (_) are ignored. Edit a section and remove the _ in front of the name. Refer to the user's guide for details.",
"settings": { "settings": {
"_cert": "myserver.mydomain.com", "_cert": "myserver.mydomain.com",
"_acebase": { "_sponsor": true },
"_mongoDb": "mongodb://127.0.0.1:27017", "_mongoDb": "mongodb://127.0.0.1:27017",
"_mongoDbName": "meshcentral", "_mongoDbName": "meshcentral",
"_mongoDbChangeStream": true, "_mongoDbChangeStream": true,