mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add node module update api and expose in palette editor
This commit is contained in:
parent
0720128bd4
commit
e27f5d0460
@ -24,7 +24,6 @@
|
|||||||
url: 'nodes',
|
url: 'nodes',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
RED.nodes.setNodeList(data);
|
RED.nodes.setNodeList(data);
|
||||||
|
|
||||||
var nsCount = 0;
|
var nsCount = 0;
|
||||||
for (var i=0;i<data.length;i++) {
|
for (var i=0;i<data.length;i++) {
|
||||||
var ns = data[i];
|
var ns = data[i];
|
||||||
@ -158,6 +157,9 @@
|
|||||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||||
}
|
}
|
||||||
|
} else if (topic == "node/upgraded") {
|
||||||
|
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
||||||
|
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
||||||
}
|
}
|
||||||
// Refresh flow library to ensure any examples are updated
|
// Refresh flow library to ensure any examples are updated
|
||||||
RED.library.loadFlowLibrary();
|
RED.library.loadFlowLibrary();
|
||||||
|
@ -42,6 +42,10 @@ RED.nodes = (function() {
|
|||||||
var nodeDefinitions = {};
|
var nodeDefinitions = {};
|
||||||
|
|
||||||
var exports = {
|
var exports = {
|
||||||
|
setModulePendingUpdated: function(module,version) {
|
||||||
|
moduleList[module].pending_version = version;
|
||||||
|
RED.events.emit("registry:module-updated",{module:module,version:version});
|
||||||
|
},
|
||||||
getModule: function(module) {
|
getModule: function(module) {
|
||||||
return moduleList[module];
|
return moduleList[module];
|
||||||
},
|
},
|
||||||
@ -78,6 +82,9 @@ RED.nodes = (function() {
|
|||||||
local:ns.local,
|
local:ns.local,
|
||||||
sets:{}
|
sets:{}
|
||||||
};
|
};
|
||||||
|
if (ns.pending_version) {
|
||||||
|
moduleList[ns.module].pending_version = ns.pending_version;
|
||||||
|
}
|
||||||
moduleList[ns.module].sets[ns.name] = ns;
|
moduleList[ns.module].sets[ns.name] = ns;
|
||||||
RED.events.emit("registry:node-set-added",ns);
|
RED.events.emit("registry:node-set-added",ns);
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,17 @@ RED.palette.editor = (function() {
|
|||||||
var eventTimers = {};
|
var eventTimers = {};
|
||||||
var activeFilter = "";
|
var activeFilter = "";
|
||||||
|
|
||||||
|
function semVerCompare(A,B) {
|
||||||
|
var aParts = A.split(".").map(function(m) { return parseInt(m);});
|
||||||
|
var bParts = B.split(".").map(function(m) { return parseInt(m);});
|
||||||
|
for (var i=0;i<3;i++) {
|
||||||
|
var j = aParts[i]-bParts[i];
|
||||||
|
if (j<0) { return -1 }
|
||||||
|
if (j>0) { return 1 }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
function delayCallback(start,callback) {
|
function delayCallback(start,callback) {
|
||||||
var delta = Date.now() - start;
|
var delta = Date.now() - start;
|
||||||
if (delta < 300) {
|
if (delta < 300) {
|
||||||
@ -64,14 +75,21 @@ RED.palette.editor = (function() {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function installNodeModule(id,shade,callback) {
|
function installNodeModule(id,version,shade,callback) {
|
||||||
|
var requestBody = {
|
||||||
|
module: id
|
||||||
|
};
|
||||||
|
if (callback === undefined) {
|
||||||
|
callback = shade;
|
||||||
|
shade = version;
|
||||||
|
} else {
|
||||||
|
requestBody.version = version;
|
||||||
|
}
|
||||||
shade.show();
|
shade.show();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"nodes",
|
url:"nodes",
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: JSON.stringify({
|
data: JSON.stringify(requestBody),
|
||||||
module: id
|
|
||||||
}),
|
|
||||||
contentType: "application/json; charset=utf-8"
|
contentType: "application/json; charset=utf-8"
|
||||||
}).done(function(data,textStatus,xhr) {
|
}).done(function(data,textStatus,xhr) {
|
||||||
shade.hide();
|
shade.hide();
|
||||||
@ -266,19 +284,19 @@ RED.palette.editor = (function() {
|
|||||||
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
|
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (moduleInfo.pending_version) {
|
||||||
nodeEntry.updateButton.hide();
|
nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
|
||||||
// if (loadedIndex.hasOwnProperty(module)) {
|
nodeEntry.updateButton.html(RED._('palette.editor.updated')).addClass('disabled').show();
|
||||||
// if (moduleInfo.version !== loadedIndex[module].version) {
|
} else if (loadedIndex.hasOwnProperty(module)) {
|
||||||
// nodeEntry.updateButton.show();
|
if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
|
||||||
// nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
|
nodeEntry.updateButton.show();
|
||||||
// } else {
|
nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
|
||||||
// nodeEntry.updateButton.hide();
|
} else {
|
||||||
// }
|
nodeEntry.updateButton.hide();
|
||||||
//
|
}
|
||||||
// } else {
|
} else {
|
||||||
// nodeEntry.updateButton.hide();
|
nodeEntry.updateButton.hide();
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -515,7 +533,7 @@ RED.palette.editor = (function() {
|
|||||||
var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
|
var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
|
||||||
$('<span>').html(entry.name).appendTo(titleRow);
|
$('<span>').html(entry.name).appendTo(titleRow);
|
||||||
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
||||||
$('<span>').html(entry.version).appendTo(metaRow);
|
var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
|
||||||
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
|
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
|
||||||
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
|
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
|
||||||
var setCount = $('<span>').appendTo(setButton);
|
var setCount = $('<span>').appendTo(setButton);
|
||||||
@ -524,6 +542,19 @@ RED.palette.editor = (function() {
|
|||||||
var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup);
|
var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup);
|
||||||
updateButton.click(function(evt) {
|
updateButton.click(function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
if ($(this).hasClass('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#palette-module-install-confirm").data('module',entry.name);
|
||||||
|
$("#palette-module-install-confirm").data('version',loadedIndex[entry.name].version);
|
||||||
|
$("#palette-module-install-confirm").data('shade',shade);
|
||||||
|
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.update.body"));
|
||||||
|
$(".palette-module-install-confirm-button-install").hide();
|
||||||
|
$(".palette-module-install-confirm-button-remove").hide();
|
||||||
|
$(".palette-module-install-confirm-button-update").show();
|
||||||
|
$("#palette-module-install-confirm")
|
||||||
|
.dialog('option', 'title', RED._("palette.editor.confirm.update.title"))
|
||||||
|
.dialog('open');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -536,6 +567,7 @@ RED.palette.editor = (function() {
|
|||||||
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body"));
|
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body"));
|
||||||
$(".palette-module-install-confirm-button-install").hide();
|
$(".palette-module-install-confirm-button-install").hide();
|
||||||
$(".palette-module-install-confirm-button-remove").show();
|
$(".palette-module-install-confirm-button-remove").show();
|
||||||
|
$(".palette-module-install-confirm-button-update").hide();
|
||||||
$("#palette-module-install-confirm")
|
$("#palette-module-install-confirm")
|
||||||
.dialog('option', 'title', RED._("palette.editor.confirm.remove.title"))
|
.dialog('option', 'title', RED._("palette.editor.confirm.remove.title"))
|
||||||
.dialog('open');
|
.dialog('open');
|
||||||
@ -555,6 +587,7 @@ RED.palette.editor = (function() {
|
|||||||
setCount: setCount,
|
setCount: setCount,
|
||||||
container: container,
|
container: container,
|
||||||
shade: shade,
|
shade: shade,
|
||||||
|
versionSpan: versionSpan,
|
||||||
sets: {}
|
sets: {}
|
||||||
}
|
}
|
||||||
setButton.click(function(evt) {
|
setButton.click(function(evt) {
|
||||||
@ -732,6 +765,7 @@ RED.palette.editor = (function() {
|
|||||||
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body"));
|
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body"));
|
||||||
$(".palette-module-install-confirm-button-install").show();
|
$(".palette-module-install-confirm-button-install").show();
|
||||||
$(".palette-module-install-confirm-button-remove").hide();
|
$(".palette-module-install-confirm-button-remove").hide();
|
||||||
|
$(".palette-module-install-confirm-button-update").hide();
|
||||||
$("#palette-module-install-confirm")
|
$("#palette-module-install-confirm")
|
||||||
.dialog('option', 'title', RED._("palette.editor.confirm.install.title"))
|
.dialog('option', 'title', RED._("palette.editor.confirm.install.title"))
|
||||||
.dialog('open');
|
.dialog('open');
|
||||||
@ -807,12 +841,34 @@ RED.palette.editor = (function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: RED._("palette.editor.confirm.button.update"),
|
||||||
|
class: "primary palette-module-install-confirm-button-update",
|
||||||
|
click: function() {
|
||||||
|
var id = $(this).data('module');
|
||||||
|
var version = $(this).data('version');
|
||||||
|
var shade = $(this).data('shade');
|
||||||
|
shade.show();
|
||||||
|
installNodeModule(id,version,shade,function(xhr) {
|
||||||
|
if (xhr) {
|
||||||
|
if (xhr.responseJSON) {
|
||||||
|
RED.notify(RED._('palette.editor.errors.updateFailed',{module: id,message:xhr.responseJSON.message}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
$( this ).dialog( "close" );
|
$( this ).dialog( "close" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
RED.events.on('registry:module-updated', function(ns) {
|
||||||
|
refreshNodeModule(ns.module);
|
||||||
|
});
|
||||||
RED.events.on('registry:node-set-enabled', function(ns) {
|
RED.events.on('registry:node-set-enabled', function(ns) {
|
||||||
refreshNodeModule(ns.module);
|
refreshNodeModule(ns.module);
|
||||||
});
|
});
|
||||||
|
@ -68,7 +68,8 @@
|
|||||||
"warnings": {
|
"warnings": {
|
||||||
"undeployedChanges": "node has undeployed changes",
|
"undeployedChanges": "node has undeployed changes",
|
||||||
"nodeActionDisabled": "node actions disabled within subflow",
|
"nodeActionDisabled": "node actions disabled within subflow",
|
||||||
"missing-types": "Flows stopped due to missing node types. Check logs for details."
|
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
||||||
|
"restartRequired": "Node-RED must be restarted to enable upgraded modules"
|
||||||
},
|
},
|
||||||
|
|
||||||
"error": "<strong>Error</strong>: __message__",
|
"error": "<strong>Error</strong>: __message__",
|
||||||
@ -250,7 +251,8 @@
|
|||||||
"nodeEnabled": "Node enabled:",
|
"nodeEnabled": "Node enabled:",
|
||||||
"nodeEnabled_plural": "Nodes enabled:",
|
"nodeEnabled_plural": "Nodes enabled:",
|
||||||
"nodeDisabled": "Node disabled:",
|
"nodeDisabled": "Node disabled:",
|
||||||
"nodeDisabled_plural": "Nodes disabled:"
|
"nodeDisabled_plural": "Nodes disabled:",
|
||||||
|
"nodeUpgraded": "Node module __module__ upgraded to version __version__"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"title": "Manage palette",
|
"title": "Manage palette",
|
||||||
@ -283,6 +285,7 @@
|
|||||||
"disable": "disable",
|
"disable": "disable",
|
||||||
"remove": "remove",
|
"remove": "remove",
|
||||||
"update": "update to __version__",
|
"update": "update to __version__",
|
||||||
|
"updated": "updated",
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"installed": "installed",
|
"installed": "installed",
|
||||||
"loading": "Loading catalogues...",
|
"loading": "Loading catalogues...",
|
||||||
@ -295,7 +298,8 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"catalogLoadFailed": "Failed to load node catalogue.<br>Check the browser console for more information",
|
"catalogLoadFailed": "Failed to load node catalogue.<br>Check the browser console for more information",
|
||||||
"installFailed": "Failed to install: __module__<br>__message__<br>Check the log for more information",
|
"installFailed": "Failed to install: __module__<br>__message__<br>Check the log for more information",
|
||||||
"removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information"
|
"removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information",
|
||||||
|
"updateFailed": "Failed to update: __module__<br>__message__<br>Check the log for more information"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"install": {
|
"install": {
|
||||||
@ -306,10 +310,15 @@
|
|||||||
"body":"Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.",
|
"body":"Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.",
|
||||||
"title": "Remove nodes"
|
"title": "Remove nodes"
|
||||||
},
|
},
|
||||||
|
"update": {
|
||||||
|
"body":"Updating the node will require a restart of Node-RED to complete the update. This must be done manually.",
|
||||||
|
"title": "Update nodes"
|
||||||
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"review": "Open node information",
|
"review": "Open node information",
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
"remove": "Remove"
|
"remove": "Remove",
|
||||||
|
"update": "Update"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,34 +48,42 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
var node = req.body;
|
var node = req.body;
|
||||||
var promise;
|
var promise;
|
||||||
|
var isUpgrade = false;
|
||||||
if (node.module) {
|
if (node.module) {
|
||||||
var module = redNodes.getModuleInfo(node.module);
|
var module = redNodes.getModuleInfo(node.module);
|
||||||
if (module) {
|
if (module) {
|
||||||
log.audit({event: "nodes.install",module:node.module,error:"module_already_loaded"},req);
|
if (!node.version || module.version === node.version) {
|
||||||
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
|
log.audit({event: "nodes.install",module:node.module, version:node.version, error:"module_already_loaded"},req);
|
||||||
return;
|
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isUpgrade = true;
|
||||||
}
|
}
|
||||||
promise = redNodes.installModule(node.module);
|
promise = redNodes.installModule(node.module,node.version);
|
||||||
} else {
|
} else {
|
||||||
log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
|
log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
|
||||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
promise.then(function(info) {
|
promise.then(function(info) {
|
||||||
comms.publish("node/added",info.nodes,false);
|
if (isUpgrade) {
|
||||||
|
comms.publish("node/upgraded",{module:node.module,version:node.version},false);
|
||||||
|
} else {
|
||||||
|
comms.publish("node/added",info.nodes,false);
|
||||||
|
}
|
||||||
if (node.module) {
|
if (node.module) {
|
||||||
log.audit({event: "nodes.install",module:node.module},req);
|
log.audit({event: "nodes.install",module:node.module,version:node.version},req);
|
||||||
res.json(info);
|
res.json(info);
|
||||||
}
|
}
|
||||||
}).otherwise(function(err) {
|
}).otherwise(function(err) {
|
||||||
if (err.code === 404) {
|
if (err.code === 404) {
|
||||||
log.audit({event: "nodes.install",module:node.module,error:"not_found"},req);
|
log.audit({event: "nodes.install",module:node.module,version:node.version,error:"not_found"},req);
|
||||||
res.status(404).end();
|
res.status(404).end();
|
||||||
} else if (err.code) {
|
} else if (err.code) {
|
||||||
log.audit({event: "nodes.install",module:node.module,error:err.code},req);
|
log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code},req);
|
||||||
res.status(400).json({error:err.code, message:err.message});
|
res.status(400).json({error:err.code, message:err.message});
|
||||||
} else {
|
} else {
|
||||||
log.audit({event: "nodes.install",module:node.module,error:err.code||"unexpected_error",message:err.toString()},req);
|
log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -24,12 +24,14 @@
|
|||||||
"removed-types": "Removed node types:",
|
"removed-types": "Removed node types:",
|
||||||
"install": {
|
"install": {
|
||||||
"invalid": "Invalid module name",
|
"invalid": "Invalid module name",
|
||||||
"installing": "Installing module: __name__",
|
"installing": "Installing module: __name__, version: __version__",
|
||||||
"installed": "Installed module: __name__",
|
"installed": "Installed module: __name__",
|
||||||
"install-failed": "Install failed",
|
"install-failed": "Install failed",
|
||||||
"install-failed-long": "Installation of module __name__ failed:",
|
"install-failed-long": "Installation of module __name__ failed:",
|
||||||
"install-failed-not-found": "$t(install-failed-long) module not found",
|
"install-failed-not-found": "$t(install-failed-long) module not found",
|
||||||
|
"upgrading": "Upgrading module: __name__ to version: __version__",
|
||||||
|
"upgraded": "Upgraded module: __name__. Restart Node-RED to use the new version",
|
||||||
|
"upgrade-failed-not-found": "$t(server.install.install-failed-long) version not found",
|
||||||
"uninstalling": "Uninstalling module: __name__",
|
"uninstalling": "Uninstalling module: __name__",
|
||||||
"uninstall-failed": "Uninstall failed",
|
"uninstall-failed": "Uninstall failed",
|
||||||
"uninstall-failed-long": "Uninstall of module __name__ failed:",
|
"uninstall-failed-long": "Uninstall of module __name__ failed:",
|
||||||
|
@ -76,7 +76,7 @@ module.exports = {
|
|||||||
|
|
||||||
addModule: addModule,
|
addModule: addModule,
|
||||||
removeModule: registry.removeModule,
|
removeModule: registry.removeModule,
|
||||||
|
|
||||||
installModule: installer.installModule,
|
installModule: installer.installModule,
|
||||||
uninstallModule: installer.uninstallModule,
|
uninstallModule: installer.uninstallModule,
|
||||||
|
|
||||||
|
@ -59,33 +59,44 @@ function checkModulePath(folder) {
|
|||||||
return moduleName;
|
return moduleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkExistingModule(module) {
|
function checkExistingModule(module,version) {
|
||||||
if (registry.getModuleInfo(module)) {
|
var info = registry.getModuleInfo(module);
|
||||||
// TODO: nls
|
if (info) {
|
||||||
var err = new Error("Module already loaded");
|
if (!version || info.version === version) {
|
||||||
err.code = "module_already_loaded";
|
var err = new Error("Module already loaded");
|
||||||
throw err;
|
err.code = "module_already_loaded";
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function installModule(module) {
|
function installModule(module,version) {
|
||||||
//TODO: ensure module is 'safe'
|
//TODO: ensure module is 'safe'
|
||||||
return when.promise(function(resolve,reject) {
|
return when.promise(function(resolve,reject) {
|
||||||
var installName = module;
|
var installName = module;
|
||||||
|
var isUpgrade = false;
|
||||||
try {
|
try {
|
||||||
if (moduleRe.test(module)) {
|
if (moduleRe.test(module)) {
|
||||||
// Simple module name - assume it can be npm installed
|
// Simple module name - assume it can be npm installed
|
||||||
|
if (version) {
|
||||||
|
installName += "@"+version;
|
||||||
|
}
|
||||||
} else if (slashRe.test(module)) {
|
} else if (slashRe.test(module)) {
|
||||||
// A path - check if there's a valid package.json
|
// A path - check if there's a valid package.json
|
||||||
installName = module;
|
installName = module;
|
||||||
module = checkModulePath(module);
|
module = checkModulePath(module);
|
||||||
}
|
}
|
||||||
checkExistingModule(module);
|
isUpgrade = checkExistingModule(module,version);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
log.info(log._("server.install.installing",{name: module}));
|
if (!isUpgrade) {
|
||||||
|
log.info(log._("server.install.installing",{name: module,version: version||"latest"}));
|
||||||
|
} else {
|
||||||
|
log.info(log._("server.install.upgrading",{name: module,version: version||"latest"}));
|
||||||
|
}
|
||||||
|
|
||||||
var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
|
var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
|
||||||
var child = child_process.execFile(npmCommand,['install','--production',installName],
|
var child = child_process.execFile(npmCommand,['install','--production',installName],
|
||||||
@ -94,10 +105,17 @@ function installModule(module) {
|
|||||||
},
|
},
|
||||||
function(err, stdin, stdout) {
|
function(err, stdin, stdout) {
|
||||||
if (err) {
|
if (err) {
|
||||||
var lookFor404 = new RegExp(" 404 .*"+installName+"$","m");
|
var e;
|
||||||
|
var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
|
||||||
|
var lookForVersionNotFound = new RegExp("version not found: "+module+"@"+version,"m");
|
||||||
if (lookFor404.test(stdout)) {
|
if (lookFor404.test(stdout)) {
|
||||||
log.warn(log._("server.install.install-failed-not-found",{name:module}));
|
log.warn(log._("server.install.install-failed-not-found",{name:module}));
|
||||||
var e = new Error("Module not found");
|
e = new Error("Module not found");
|
||||||
|
e.code = 404;
|
||||||
|
reject(e);
|
||||||
|
} else if (isUpgrade && lookForVersionNotFound.test(stdout)) {
|
||||||
|
log.warn(log._("server.install.upgrade-failed-not-found",{name:module}));
|
||||||
|
e = new Error("Module not found");
|
||||||
e.code = 404;
|
e.code = 404;
|
||||||
reject(e);
|
reject(e);
|
||||||
} else {
|
} else {
|
||||||
@ -108,8 +126,14 @@ function installModule(module) {
|
|||||||
reject(new Error(log._("server.install.install-failed")));
|
reject(new Error(log._("server.install.install-failed")));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info(log._("server.install.installed",{name:module}));
|
if (!isUpgrade) {
|
||||||
resolve(require("./index").addModule(module).then(reportAddedModules));
|
log.info(log._("server.install.installed",{name:module}));
|
||||||
|
resolve(require("./index").addModule(module).then(reportAddedModules));
|
||||||
|
} else {
|
||||||
|
log.info(log._("server.install.upgraded",{name:module, version:version}));
|
||||||
|
events.emit("runtime-event",{id:"restart-required",type:"warning",text:"notification.warnings.restartRequired"});
|
||||||
|
resolve(require("./registry").setModulePendingUpdated(module,version));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -82,7 +82,8 @@ function getNode(id) {
|
|||||||
|
|
||||||
function saveNodeList() {
|
function saveNodeList() {
|
||||||
var moduleList = {};
|
var moduleList = {};
|
||||||
|
var hadPending = false;
|
||||||
|
var hasPending = false;
|
||||||
for (var module in moduleConfigs) {
|
for (var module in moduleConfigs) {
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (moduleConfigs.hasOwnProperty(module)) {
|
if (moduleConfigs.hasOwnProperty(module)) {
|
||||||
@ -94,6 +95,15 @@ function saveNodeList() {
|
|||||||
local: moduleConfigs[module].local||false,
|
local: moduleConfigs[module].local||false,
|
||||||
nodes: {}
|
nodes: {}
|
||||||
};
|
};
|
||||||
|
if (moduleConfigs[module].hasOwnProperty('pending_version')) {
|
||||||
|
hadPending = true;
|
||||||
|
if (moduleConfigs[module].pending_version !== moduleConfigs[module].version) {
|
||||||
|
moduleList[module].pending_version = moduleConfigs[module].pending_version;
|
||||||
|
hasPending = true;
|
||||||
|
} else {
|
||||||
|
delete moduleConfigs[module].pending_version;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var nodes = moduleConfigs[module].nodes;
|
var nodes = moduleConfigs[module].nodes;
|
||||||
for(var node in nodes) {
|
for(var node in nodes) {
|
||||||
@ -111,6 +121,9 @@ function saveNodeList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hadPending && !hasPending) {
|
||||||
|
events.emit("runtime-event",{id:"restart-required"});
|
||||||
|
}
|
||||||
if (settings.available()) {
|
if (settings.available()) {
|
||||||
return settings.set("nodes",moduleList);
|
return settings.set("nodes",moduleList);
|
||||||
} else {
|
} else {
|
||||||
@ -280,6 +293,9 @@ function getNodeList(filter) {
|
|||||||
if (nodes.hasOwnProperty(node)) {
|
if (nodes.hasOwnProperty(node)) {
|
||||||
var nodeInfo = filterNodeInfo(nodes[node]);
|
var nodeInfo = filterNodeInfo(nodes[node]);
|
||||||
nodeInfo.version = moduleConfigs[module].version;
|
nodeInfo.version = moduleConfigs[module].version;
|
||||||
|
if (moduleConfigs[module].pending_version) {
|
||||||
|
nodeInfo.pending_version = moduleConfigs[module].pending_version;
|
||||||
|
}
|
||||||
if (!filter || filter(nodes[node])) {
|
if (!filter || filter(nodes[node])) {
|
||||||
list.push(nodeInfo);
|
list.push(nodeInfo);
|
||||||
}
|
}
|
||||||
@ -539,6 +555,12 @@ function cleanModuleList() {
|
|||||||
saveNodeList();
|
saveNodeList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function setModulePendingUpdated(module,version) {
|
||||||
|
moduleConfigs[module].pending_version = version;
|
||||||
|
return saveNodeList().then(function() {
|
||||||
|
return getModuleInfo(module);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var registry = module.exports = {
|
var registry = module.exports = {
|
||||||
init: init,
|
init: init,
|
||||||
@ -552,6 +574,7 @@ var registry = module.exports = {
|
|||||||
enableNodeSet: enableNodeSet,
|
enableNodeSet: enableNodeSet,
|
||||||
disableNodeSet: disableNodeSet,
|
disableNodeSet: disableNodeSet,
|
||||||
|
|
||||||
|
setModulePendingUpdated: setModulePendingUpdated,
|
||||||
removeModule: removeModule,
|
removeModule: removeModule,
|
||||||
|
|
||||||
getNodeInfo: getNodeInfo,
|
getNodeInfo: getNodeInfo,
|
||||||
|
@ -46,6 +46,9 @@ describe('nodes/registry/installer', function() {
|
|||||||
if (registry.getModuleInfo.restore) {
|
if (registry.getModuleInfo.restore) {
|
||||||
registry.getModuleInfo.restore();
|
registry.getModuleInfo.restore();
|
||||||
}
|
}
|
||||||
|
if (typeRegistry.getModuleInfo.restore) {
|
||||||
|
typeRegistry.getModuleInfo.restore();
|
||||||
|
}
|
||||||
|
|
||||||
if (require('fs').statSync.restore) {
|
if (require('fs').statSync.restore) {
|
||||||
require('fs').statSync.restore();
|
require('fs').statSync.restore();
|
||||||
@ -64,6 +67,32 @@ describe('nodes/registry/installer', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("rejects when npm does not find specified version", function(done) {
|
||||||
|
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
|
||||||
|
cb(new Error(),""," version not found: this_wont_exist@0.1.2");
|
||||||
|
});
|
||||||
|
sinon.stub(typeRegistry,"getModuleInfo", function() {
|
||||||
|
return {
|
||||||
|
version: "0.1.1"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
installer.installModule("this_wont_exist","0.1.2").otherwise(function(err) {
|
||||||
|
err.code.should.be.eql(404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("rejects when update requested to existing version", function(done) {
|
||||||
|
sinon.stub(typeRegistry,"getModuleInfo", function() {
|
||||||
|
return {
|
||||||
|
version: "0.1.1"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
installer.installModule("this_wont_exist","0.1.1").otherwise(function(err) {
|
||||||
|
err.code.should.be.eql('module_already_loaded');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
it("rejects with generic error", function(done) {
|
it("rejects with generic error", function(done) {
|
||||||
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
|
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
|
||||||
cb(new Error("test_error"),"","");
|
cb(new Error("test_error"),"","");
|
||||||
|
Loading…
Reference in New Issue
Block a user