gui plugin updates partial

This commit is contained in:
Ryan Blenis 2019-10-30 04:17:17 -04:00
parent f14d405320
commit c57ac19cba
6 changed files with 242 additions and 3 deletions

23
db.js
View File

@ -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) {

View File

@ -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;

View File

@ -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, '<input type=text id=pluginurlinput style=width:100% />');
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;
};

BIN
public/images/plus32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

View File

@ -2563,4 +2563,51 @@ a {
padding: 3px;
border-radius: 3px;
background-color: #DDD;
}
#p7tbl {
width: 100%;
border-collapse: collapse;
}
#p7tbl th, #p7tbl td {
text-align: left;
padding: 12px;
}
#p7tbl tr:nth-child(n+2):nth-child(odd) {
background-color: #cfeeff;
}
#p7tbl .chName {
width: 20%;
}
#p7tbl .chDescription {
width: 40%;
}
#p7tbl .chSite {
width: 10%;
}
#p7tbl .chVersion {
width: 10%;
}
#p7tbl .chStatus {
width: 10%;
}
#p7tbl .chAction {
width: 10%;
}
#addPlugin {
background-image: url(../images/plus32.png);
width: 32px;
height: 32px;
float: right;
cursor: pointer;
margin-right: 12px;
}

View File

@ -88,6 +88,9 @@
<div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onclick=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
<div class="lb6"></div>
</div>
<div id=LeftMenuMyPlugins tabindex=0 class="lbbutton" style="display:none" title="My Plugins" onclick=go(7,event) onkeypress="if (event.key=='Enter') { go(7); }">
<div class="lb7"></div>
</div>
</div>
<div id=topbar class=noselect>
<div>
@ -109,6 +112,7 @@
<td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onclick=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
<td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
<td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
<td tabindex=0 id=MainMenuMyPlugins class="topbar_td style3x" onclick=go(7,event) onkeypress="if (event.key == 'Enter') go(7)">My Plugins</td>
<td class="topbar_td_end style3">&nbsp;</td>
</tr>
</table>
@ -405,6 +409,13 @@
<div id="serverStatsTable"></div>
</div>
</div>
<div id=p7 style="display:none">
<h1>My Plugins</h1>
<div id="addPlugin" onclick="return pluginHandler.addPluginDlg();"></div>
<table id="p7tbl">
<tr><th class="chName">Name</th><th class="chDescription">Description</th><th class="chSite">Link</th><th class="chVersion">Version</th><th class="chStatus">Status</th><th class="chAction">Action</th></tr>
</table>
</div>
<div id=p10 style="display:none">
<table style="width:100%" cellpadding="0" cellspacing="0">
<tr>
@ -1042,6 +1053,7 @@
var pluginHandlerBuilder = {{{pluginHandler}}};
var pluginHandler = null;
if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
var installedPluginList = null;
// Console Message Display Timers
var p11DeskConsoleMsgTimer = null;
@ -1295,6 +1307,7 @@
// Fetch list of meshes, nodes, files
meshserver.send({ action: 'meshes' });
meshserver.send({ action: 'nodes', id: '{{currentNode}}' });
meshserver.send({ action: 'plugins' });
if ('{{currentNode}}' == '') { meshserver.send({ action: 'files' }); }
if ('{{viewmode}}' == '') { go(1); }
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
@ -1337,6 +1350,7 @@
QV('p2ServerActionsVersion', siteRights & 16);
QV('MainMenuMyFiles', siteRights & 8);
QV('LeftMenuMyFiles', siteRights & 8);
QV('MainMenuMyPlugins', (pluginHandler != null));
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
@ -2284,6 +2298,12 @@
//console.log(message.msg);
break;
}
case 'updatePluginList': {
// @Ylianst - Do we need a rights check here?
installedPluginList = message.event.list;
updatePluginList();
break;
}
default:
//console.log('Unknown message.event.action', message.event.action);
break;
@ -2337,6 +2357,11 @@
QH('p0span', message.msg);
break;
}
case 'updatePluginList': {
installedPluginList = message.list;
updatePluginList();
break;
}
case 'plugin': {
if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
@ -9250,6 +9275,9 @@
// Remove top bar selection
var mainBarItems = ['MainMenuMyDevices', 'MainMenuMyAccount', 'MainMenuMyEvents', 'MainMenuMyFiles', 'MainMenuMyUsers', 'MainMenuMyServer'];
if (pluginHandler != null) {
mainBarItems.push('MainMenuMyPlugins');
}
for (var i in mainBarItems) {
QC(mainBarItems[i]).remove('fullselect');
QC(mainBarItems[i]).remove('semiselect');
@ -9257,6 +9285,9 @@
// Remove left bar selection
var leftBarItems = ['LeftMenuMyDevices', 'LeftMenuMyAccount', 'LeftMenuMyEvents', 'LeftMenuMyFiles', 'LeftMenuMyUsers', 'LeftMenuMyServer'];
if (pluginHandler != null) {
leftBarItems.push('LeftMenuMyPlugins');
}
for (var i in leftBarItems) {
QC(leftBarItems[i]).remove('lbbuttonsel');
QC(leftBarItems[i]).remove('lbbuttonsel2');
@ -9289,7 +9320,11 @@
// My Server
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
// My Plugins
if (x == 7) QC('MainMenuMyPlugins').add(mainMenuActiveClass);
if (x == 7) QC('LeftMenuMyPlugins').add(leftMenuActiveClass);
// column_l max-height
if (webPageStackMenu && (x >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
@ -9333,7 +9368,34 @@
document.title = decodeURIComponent('{{{extitle}}}');
}
}
function updatePluginList() {
if (installedPluginList.length) {
var tr = Q('p7tbl').querySelectorAll(".p7tblRow");
if (tr.length) {
for (const i in Object.values(tr)) {
tr[i].parentNode.removeChild(tr[i]);
}
}
var statusMap = {
0: 'Disabled',
1: 'Installed'
}
var tbl = Q('p7tbl');
installedPluginList.forEach(function(p){
if (p.hasAdminPanel == true) {
p.name = `<a onclick="return goPlugin('${p._id}');">${p.name}</a>`;
}
p.status = statusMap[p.status];
p.actions = 'TODO'; // Install / Upgrade / Disable / Delete
let tpl = `<td>${p.name}</td><td>${p.description}</td><td><a href="${p.homepage}" target="_blank">Homepage</a></td><td>${p.version}</td><td>${p.status}</td><td>${p.actions}</td>`;
let tr = tbl.insertRow(-1);
tr.innerHTML = tpl;
tr.classList.add('p7tblRow');
});
}
}
// Generic methods
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
function putstore(name, val) {