From f1ea4ae1b8281eec21f64a9d1324d26dba39fe4c Mon Sep 17 00:00:00 2001 From: Ryan Blenis Date: Wed, 30 Oct 2019 04:17:17 -0400 Subject: [PATCH] gui plugin updates partial --- db.js | 23 ++++++++++ meshuser.js | 19 +++++++++ pluginHandler.js | 90 ++++++++++++++++++++++++++++++++++++++- public/images/plus32.png | Bin 0 -> 656 bytes public/styles/style.css | 47 ++++++++++++++++++++ views/default.handlebars | 66 +++++++++++++++++++++++++++- 6 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 public/images/plus32.png diff --git a/db.js b/db.js index 2e882e74..e4861996 100644 --- a/db.js +++ b/db.js @@ -442,6 +442,9 @@ module.exports.CreateDB = function (parent, func) { }); } }); + + // Setup plugin info collection + obj.pluginsfile = db.collection('plugins'); setupFunctions(func); // Completed setup of MongoDB }); @@ -543,6 +546,9 @@ module.exports.CreateDB = function (parent, func) { }); } }); + + // Setup plugin info collection + obj.pluginsfile = db.collection('plugins'); setupFunctions(func); // Completed setup of MongoJS } else { @@ -604,6 +610,10 @@ module.exports.CreateDB = function (parent, func) { obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days) obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events + // Setup plugin info collection + obj.pluginsfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-plugins.db'), autoload: true }); + obj.pluginsfile.persistence.setAutocompactionInterval(36000); + setupFunctions(func); // Completed setup of NeDB } @@ -741,6 +751,19 @@ module.exports.CreateDB = function (parent, func) { func(r); }); } + + // Add a plugin + obj.addPlugin = function (plugin) { obj.pluginsfile.insertOne(plugin); }; + + // Get all plugins + obj.getPlugins = function (func) { obj.pluginsfile.find().sort({ name: 1 }).toArray(func); }; + + // Get plugin + obj.getPlugin = function (id, func) { obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).toArray(func); }; + + // Delete plugin + obj.deletePlugin = function (id) { obj.pluginsfile.deleteOne({ _id: id }); }; + } else { // Database actions on the main collection (NeDB and MongoJS) obj.Set = function (data, func) { diff --git a/meshuser.js b/meshuser.js index 8d817960..9f59d764 100644 --- a/meshuser.js +++ b/meshuser.js @@ -3102,6 +3102,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } break; } + case 'plugins': { + if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled + parent.db.getPlugins(function(err, docs) { + try { ws.send(JSON.stringify({ action: 'updatePluginList', list: docs, result: err })); } catch (ex) { } + }); + break; + } + case 'addplugin': { + // @Ylianst - Do we need a new permission here? + if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled + parent.parent.pluginHandler.addPlugin(command.url); + break; + } + case 'removeplugin': { + // @Ylianst - Do we need a new permission here? + if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled + parent.parent.pluginHandler.removePlugin(command.id); + break; + } case 'plugin': { if (parent.parent.pluginHandler == null) break; // If the plugin's are not supported, reject this command. command.userid = user._id; diff --git a/pluginHandler.js b/pluginHandler.js index cb96ec42..5b328ee6 100644 --- a/pluginHandler.js +++ b/pluginHandler.js @@ -71,6 +71,13 @@ module.exports.pluginHandler = function (parent) { for (const i of pages) { i.style.display = 'none'; } QV(id, true); }; + obj.addPluginEx = function() { + meshserver.send({ action: 'addplugin', url: Q('pluginurlinput').value}); + }; + obj.addPluginDlg = function() { + setDialogMode(2, "Plugin URL", 3, obj.addPluginEx, ''); + focusTextBox('pluginurlinput'); + }; return obj; };`; return str; } @@ -152,7 +159,88 @@ module.exports.pluginHandler = function (parent) { } } return panel; - } + }; + + obj.isValidConfig = function(conf, url) { // check for the required attributes + var isValid = true; + if (!( + typeof conf.name == 'string' + && typeof conf.version == 'string' + && typeof conf.author == 'string' + && typeof conf.description == 'string' + && typeof conf.hasAdminPanel == 'boolean' + && typeof conf.homepage == 'string' + && typeof conf.changelogUrl == 'string' + && typeof conf.configUrl == 'string' + && typeof conf.repository == 'object' + && typeof conf.repository.type == 'string' + && typeof conf.repository.url == 'string' + && typeof conf.meshCentralCompat == 'string' + // && conf.configUrl == url // make sure we're loading a plugin from its desired config + )) isValid = false; + // more checks here? + return isValid; + }; + + obj.addPlugin = function(url) { + var https = require('https'); + //var pit = obj.path.join(obj.pluginPath, ) + https.get(url, function(res) { + var configStr = ''; + res.on('data', function(chunk){ + configStr += chunk; + }); + res.on('end', function(){ + if (configStr[0] == '{') { + try { + var pluginConfig = JSON.parse(configStr); + if (obj.isValidConfig(pluginConfig, url)) { + // add to database + // we met the requirements of a valid config, but in case there's extra, let's rebuild for what we need + parent.db.addPlugin({ + "name": pluginConfig.name, + "version": pluginConfig.version, + "description": pluginConfig.description, + "hasAdminPanel": pluginConfig.hasAdminPanel, + "homepage": pluginConfig.homepage, + "changelogUrl": pluginConfig.changelogUrl, + "configUrl": pluginConfig.configUrl, + "repository": { + "type": pluginConfig.repository.type, + "url": pluginConfig.repository.url + }, + "meshCentralCompat": pluginConfig.meshCentralCompat, + "status": 0 // 0: disabled, 1: enabled + }); + parent.db.getPlugins(function(err, docs){ + var targets = ['*', 'server-users']; + parent.DispatchEvent(targets, obj, { action: 'updatePluginList', list: docs }); + + }) + } else { + // @TODO return error to user + } + + } catch (e) { console.log('Error processing addPlugin request. Check that you have valid JSON.'); } + } + }); + + }).on('error', function(e) { + console.log("Got error: " + e.message); + }); + /* const file = fs.createWriteStream("file.jpg"); + const request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) { + response.pipe(file); + }); */ + }; + + obj.getPlugins = function() { + var p = parent.db.getPlugins(); + if (typeof p == 'undefined' || p.length == 0) { + return null; + } + return p; + } return obj; }; \ No newline at end of file diff --git a/public/images/plus32.png b/public/images/plus32.png new file mode 100644 index 0000000000000000000000000000000000000000..f2e17cce77962f316452e75ad7bc4c2f5202ab7c GIT binary patch literal 656 zcmV;B0&o3^P)AwEHl7?B>$6k4hY1KJjukd+g?3 z8HkIaxlEM$bufqiSD&WKq^94}K3{=K0sXcGZW%lg*K-7_ z(Se{IF_mboz!Rz_r^b1l!*2CvS7c-veV4Hbs_J6c9VC0i4`X`q{lDeIR68vNVp qZ~~1f>zCfDir{}PPP2@vJpTiH%i)aF0IlQz0000