Early work on Postgres SQL support.

Ylian Saint-Hilaire committed Nov 2, 2021 at 19:44 UTC 97bd723e4e47af6a3f16802ee5a81746434d1121
4 files changed +70 -10
MeshCentralServer.njsproj
+8
@@ -645,6 +645,7 @@
645 <Folder Include="typings\globals\is-plain-object\" />
646 <Folder Include="typings\globals\jsbn\" />
647 <Folder Include="typings\globals\klaw\" />
648 + <Folder Include="typings\globals\load-json-file\" />
649 <Folder Include="typings\globals\localforage\" />
650 <Folder Include="typings\globals\lru-cache\" />
651 <Folder Include="typings\globals\marked\" />
@@ -655,10 +656,13 @@
656 <Folder Include="typings\globals\object-assign\" />
657 <Folder Include="typings\globals\once\" />
658 <Folder Include="typings\globals\passport\" />
659 + <Folder Include="typings\globals\pg-pool\" />
660 + <Folder Include="typings\globals\split2\" />
661 <Folder Include="typings\globals\sprintf-js\" />
662 <Folder Include="typings\globals\type-check\" />
663 <Folder Include="typings\globals\underscore\" />
664 <Folder Include="typings\globals\uuid\" />
665 + <Folder Include="typings\globals\window-size\" />
666 <Folder Include="views\" />
667 </ItemGroup>
668 <ItemGroup>
@@ -681,6 +685,7 @@
685 <TypeScriptCompile Include="typings\globals\is-plain-object\index.d.ts" />
686 <TypeScriptCompile Include="typings\globals\jsbn\index.d.ts" />
687 <TypeScriptCompile Include="typings\globals\klaw\index.d.ts" />
688 + <TypeScriptCompile Include="typings\globals\load-json-file\index.d.ts" />
689 <TypeScriptCompile Include="typings\globals\localforage\index.d.ts" />
690 <TypeScriptCompile Include="typings\globals\lru-cache\index.d.ts" />
691 <TypeScriptCompile Include="typings\globals\marked\index.d.ts" />
@@ -691,10 +696,13 @@
696 <TypeScriptCompile Include="typings\globals\object-assign\index.d.ts" />
697 <TypeScriptCompile Include="typings\globals\once\index.d.ts" />
698 <TypeScriptCompile Include="typings\globals\passport\index.d.ts" />
699 + <TypeScriptCompile Include="typings\globals\pg-pool\index.d.ts" />
700 + <TypeScriptCompile Include="typings\globals\split2\index.d.ts" />
701 <TypeScriptCompile Include="typings\globals\sprintf-js\index.d.ts" />
702 <TypeScriptCompile Include="typings\globals\type-check\index.d.ts" />
703 <TypeScriptCompile Include="typings\globals\underscore\index.d.ts" />
704 <TypeScriptCompile Include="typings\globals\uuid\index.d.ts" />
705 + <TypeScriptCompile Include="typings\globals\window-size\index.d.ts" />
706 <TypeScriptCompile Include="typings\index.d.ts" />
707 </ItemGroup>
708 <Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
db.js
+45 -8
@@ -230,7 +230,7 @@ module.exports.CreateDB = function (parent, func) {
230 // TODO: Remove all meshes that dont have any links
231
232 // Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now.
233 - if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
233 + if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) {
234 // MariaDB or MySQL
235 obj.RemoveAllOfType('event', function () { });
236 obj.RemoveAllOfType('power', function () { });
@@ -640,6 +640,43 @@ module.exports.CreateDB = function (parent, func) {
640 });
641 setTimeout(function () { tempDatastore.end(); }, 2000);
642 }
643 + } else if (parent.args.postgres) {
644 + // Postgres SQL
645 + var connectinArgs = parent.args.postgres;
646 + var dbname = (connectinArgs.database != null) ? connectinArgs.database : 'meshcentral';
647 + obj.databaseType = 6;
648 + const pgtools = require('pgtools');
649 + pgtools.createdb(connectinArgs, dbname, function (err, res) {
650 + const { Pool, Client } = require('pg');
651 + connectinArgs.database = dbname;
652 + Datastore = new Client(connectinArgs);
653 + Datastore.connect()
654 + parent.debug('db', 'Checking tables...');
655 + sqlDbBatchExec([
656 + 'CREATE TABLE IF NOT EXISTS main (id VARCHAR(256) PRIMARY KEY NOT NULL, type CHAR(32), domain CHAR(64), extra CHAR(255), extraex CHAR(255), doc JSON)',
657 + 'CREATE TABLE IF NOT EXISTS events(id SERIAL PRIMARY KEY, time TIMESTAMP, domain CHAR(64), action CHAR(255), nodeid CHAR(255), userid CHAR(255), doc JSON)',
658 + 'CREATE TABLE IF NOT EXISTS eventids(fkid INT NOT NULL, target CHAR(255), CONSTRAINT fk_eventid FOREIGN KEY (fkid) REFERENCES events (id) ON DELETE CASCADE ON UPDATE RESTRICT)',
659 + 'CREATE TABLE IF NOT EXISTS serverstats (time TIMESTAMP PRIMARY KEY, expire TIMESTAMP, doc JSON)',
660 + 'CREATE TABLE IF NOT EXISTS power (id SERIAL PRIMARY KEY, time TIMESTAMP, nodeid CHAR(255), doc JSON)',
661 + 'CREATE TABLE IF NOT EXISTS smbios (id CHAR(255) PRIMARY KEY, time TIMESTAMP, expire TIMESTAMP, doc JSON)',
662 + 'CREATE TABLE IF NOT EXISTS plugin (id SERIAL PRIMARY KEY, doc JSON)'
663 + ], function (results) {
664 + parent.debug('db', 'Checking indexes...');
665 + sqlDbExec('CREATE INDEX ndxtypedomainextra ON main (type, domain, extra)', null, function (err, response) { });
666 + sqlDbExec('CREATE INDEX ndxextra ON main (extra)', null, function (err, response) { });
667 + sqlDbExec('CREATE INDEX ndxextraex ON main (extraex)', null, function (err, response) { });
668 + sqlDbExec('CREATE INDEX ndxeventstime ON events(time)', null, function (err, response) { });
669 + sqlDbExec('CREATE INDEX ndxeventsusername ON events(domain, userid, time)', null, function (err, response) { });
670 + sqlDbExec('CREATE INDEX ndxeventsdomainnodeidtime ON events(domain, nodeid, time)', null, function (err, response) { });
671 + sqlDbExec('CREATE INDEX ndxeventids ON eventids(target)', null, function (err, response) { });
672 + sqlDbExec('CREATE INDEX ndxserverstattime ON serverstats (time)', null, function (err, response) { });
673 + sqlDbExec('CREATE INDEX ndxserverstatexpire ON serverstats (expire)', null, function (err, response) { });
674 + sqlDbExec('CREATE INDEX ndxpowernodeidtime ON power (nodeid, time)', null, function (err, response) { });
675 + sqlDbExec('CREATE INDEX ndxsmbiostime ON smbios (time)', null, function (err, response) { });
676 + sqlDbExec('CREATE INDEX ndxsmbiosexpire ON smbios (expire)', null, function (err, response) { });
677 + setupFunctions(func);
678 + });
679 + });
680 } else if (parent.args.mongodb) {
681 // Use MongoDB
682 obj.databaseType = 3;
@@ -1011,12 +1048,12 @@ module.exports.CreateDB = function (parent, func) {
1048 .then(function (rows) {
1049 conn.release();
1050 const docs = [];
1014 - for (var i in rows) { if (rows[i].doc) { docs.push(performTypedRecordDecrypt((typeof rows[i].doc == 'object')? rows[i].doc : JSON.parse(rows[i].doc))); } }
1051 + for (var i in rows) { if (rows[i].doc) { docs.push(performTypedRecordDecrypt((typeof rows[i].doc == 'object') ? rows[i].doc : JSON.parse(rows[i].doc))); } }
1052 if (func) try { func(null, docs); } catch (ex) { console.log('SQLERR1', ex); }
1053 })
1054 .catch(function (err) { conn.release(); if (func) try { func(err); } catch (ex) { console.log('SQLERR2', ex); } });
1055 }).catch(function (err) { if (func) { try { func(err); } catch (ex) { console.log('SQLERR3', ex); } } });
1019 - } else if (obj.databaseType == 5) { // MySQL
1056 + } else if ((obj.databaseType == 5) || (obj.databaseType == 6)) { // MySQL or Postgres SQL
1057 Datastore.query(query, args, function (error, results, fields) {
1058 if (error != null) {
1059 if (func) try { func(error); } catch (ex) { console.log('SQLERR4', ex); }
@@ -1041,10 +1078,10 @@ module.exports.CreateDB = function (parent, func) {
1078 if (func) try { func(null, rows[0]); } catch (ex) { console.log(ex); }
1079 })
1080 .catch(function (err) { conn.release(); if (func) try { func(err); } catch (ex) { console.log(ex); } });
1044 - }).catch(function (err) { if (func) { try { func(err); } catch (ex) { console.log(ex); } } });
1045 - } else if (obj.databaseType == 5) { // MySQL
1081 + }).catch(function (err) { if (func) { try { func(err); } catch (ex) { console.log(ex); } } });
1082 + } else if ((obj.databaseType == 5) || (obj.databaseType == 6)) { // MySQL or Postgres SQL
1083 Datastore.query(query, args, function (error, results, fields) {
1047 - if (func) try { func(error, results?results[0]:null); } catch (ex) { console.log(ex); }
1084 + if (func) try { func(error, results ? results[0] : null); } catch (ex) { console.log(ex); }
1085 });
1086 }
1087 }
@@ -1061,7 +1098,7 @@ module.exports.CreateDB = function (parent, func) {
1098 .catch(function (err) { conn.release(); if (func) { try { func(err); } catch (ex) { console.log(ex); } } });
1099 })
1100 .catch(function (err) { if (func) { try { func(err); } catch (ex) { console.log(ex); } } });
1064 - } else if (obj.databaseType == 5) { // MySQL
1101 + } else if ((obj.databaseType == 5) || (obj.databaseType == 6)) { // MySQL or Postgres SQL
1102 var Promises = [];
1103 for (var i in queries) { if (typeof queries[i] == 'string') { Promises.push(Datastore.query(queries[i])); } else { Promises.push(Datastore.query(queries[i][0], queries[i][1])); } }
1104 Promise.all(Promises)
@@ -1071,7 +1108,7 @@ module.exports.CreateDB = function (parent, func) {
1108 }
1109
1110 function setupFunctions(func) {
1074 - if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
1111 + if ((obj.databaseType == 4) || (obj.databaseType == 5) || (obj.databaseType == 6)) {
1112 // Database actions on the main collection (MariaDB or MySQL)
1113 obj.Set = function (value, func) {
1114 obj.dbCounters.fileSet++;
meshcentral.js
+1
@@ -3341,6 +3341,7 @@ function mainStart() {
3341 if (config.settings.mysql != null) { modules.push('mysql'); } // Add MySQL.
3342 //if (config.settings.mysql != null) { modules.push('@mysql/xdevapi'); } // Add MySQL, official driver (https://dev.mysql.com/doc/dev/connector-nodejs/8.0/)
3343 if (config.settings.mongodb != null) { modules.push('mongodb@4.1.0'); modules.push('saslprep'); } // Add MongoDB, official driver.
3344 + if (config.settings.postgres != null) { modules.push('pg@8.7.1'); modules.push('pgtools@0.3.2'); } // Add Postgres, Postgres driver.
3345 if (config.settings.mariadb != null) { modules.push('mariadb'); } // Add MariaDB, official driver.
3346 if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module.
3347 if (config.settings.plugins != null) { modules.push('semver'); } // Required for version compat testing and update checks
package.json
+16 -2
@@ -36,6 +36,9 @@
36 "sample-config-advanced.json"
37 ],
38 "dependencies": {
39 + "@yetzt/nedb": "^1.8.0",
40 + "archiver": "^4.0.2",
41 + "archiver-zip-encrypted": "^1.0.10",
42 "body-parser": "^1.19.0",
43 "cbor": "~5.2.0",
44 "compression": "^1.7.4",
@@ -43,13 +46,24 @@
46 "express": "^4.17.0",
47 "express-handlebars": "^3.1.0",
48 "express-ws": "^4.0.0",
49 + "image-size": "^1.0.0",
50 "ipcheck": "^0.1.0",
51 + "loadavg-windows": "^1.1.1",
52 "minimist": "^1.2.5",
53 + "mongodb": "^4.1.0",
54 "multiparty": "^4.2.1",
49 - "@yetzt/nedb": "^1.8.0",
55 "node-forge": "^0.10.0",
56 + "node-rdpjs-2": "^0.3.5",
57 + "node-windows": "^0.1.4",
58 + "otplib": "^10.2.3",
59 + "pg": "^8.7.1",
60 + "pgtools": "^0.3.2",
61 + "saslprep": "^1.0.3",
62 + "ssh2": "^1.5.0",
63 + "web-push": "^3.4.5",
64 "ws": "^5.2.3",
52 - "yauzl": "^2.10.0"
65 + "yauzl": "^2.10.0",
66 + "yubikeyotp": "^0.2.0"
67 },
68 "repository": {
69 "type": "git",