Socket
Socket
Sign inDemoInstall

@node-red/runtime

Package Overview
Dependencies
Maintainers
2
Versions
109
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@node-red/runtime - npm Package Compare versions

Comparing version 1.2.9 to 1.3.0-beta.1

lib/api/plugins.js

28

lib/api/comms.js

@@ -36,2 +36,3 @@ /**

const events = require("@node-red/util").events;

@@ -92,10 +93,10 @@ function handleCommsEvent(event) {

retained = {};
runtime.events.removeListener("node-status",handleStatusEvent);
runtime.events.on("node-status",handleStatusEvent);
runtime.events.removeListener("runtime-event",handleRuntimeEvent);
runtime.events.on("runtime-event",handleRuntimeEvent);
runtime.events.removeListener("comms",handleCommsEvent);
runtime.events.on("comms",handleCommsEvent);
runtime.events.removeListener("event-log",handleEventLog);
runtime.events.on("event-log",handleEventLog);
events.removeListener("node-status",handleStatusEvent);
events.on("node-status",handleStatusEvent);
events.removeListener("runtime-event",handleRuntimeEvent);
events.on("runtime-event",handleRuntimeEvent);
events.removeListener("comms",handleCommsEvent);
events.on("comms",handleCommsEvent);
events.removeListener("event-log",handleEventLog);
events.on("event-log",handleEventLog);
},

@@ -111,5 +112,4 @@

*/
addConnection: function(opts) {
addConnection: async function(opts) {
connections.push(opts.client);
return Promise.resolve();
},

@@ -125,3 +125,3 @@

*/
removeConnection: function(opts) {
removeConnection: async function(opts) {
for (var i=0;i<connections.length;i++) {

@@ -133,3 +133,2 @@ if (connections[i] === opts.client) {

}
return Promise.resolve();
},

@@ -148,3 +147,3 @@

*/
subscribe: function(opts) {
subscribe: async function(opts) {
var re = new RegExp("^"+opts.topic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");

@@ -156,3 +155,2 @@ for (var t in retained) {

}
return Promise.resolve();
},

@@ -169,3 +167,3 @@

*/
unsubscribe: function(opts) {}
unsubscribe: async function(opts) {}
};

@@ -74,3 +74,3 @@ /**

*/
getValue: function(opts) {
getValue: async function(opts) {
return new Promise(function(resolve,reject) {

@@ -169,3 +169,3 @@ var scope = opts.scope;

*/
delete: function(opts) {
delete: async function(opts) {
return new Promise(function(resolve,reject) {

@@ -172,0 +172,0 @@ var scope = opts.scope;

@@ -31,2 +31,3 @@ /*!

api.context.init(runtime);
api.plugins.init(runtime);
},

@@ -41,9 +42,10 @@

context: require("./context"),
plugins: require("./plugins"),
isStarted: function(opts) {
return Promise.resolve(runtime.isStarted());
isStarted: async function(opts) {
return runtime.isStarted();
},
version: function(opts) {
return Promise.resolve(runtime.version());
version: async function(opts) {
return runtime.version();
}
}

@@ -28,2 +28,23 @@ /**

// /* *
// * Gets the configuration of a library source.
// * @param {Object} opts
// * @param {User} opts.user - the user calling the api
// * @param {String} opts.library - the library
// * @param {Object} opts.req - the request to log (optional)
// * @return {Promise<String|Object>} - resolves when complete
// * @memberof @node-red/runtime_library
// */
// getConfig: async function(opts) {
// runtime.log.audit({event: "library.getConfig",library:opts.library}, opts.req);
// try {
// return runtime.library.getConfig(opts.library)
// } catch(err) {
// var error = new Error();
// error.code = "not_found";
// error.status = 404;
// throw error;
// }
// },
/**

@@ -40,28 +61,26 @@ * Gets an entry from the library.

*/
getEntry: function(opts) {
return new Promise(function(resolve,reject) {
runtime.library.getEntry(opts.library,opts.type,opts.path).then(function(result) {
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path}, opts.req);
return resolve(result);
}).catch(function(err) {
if (err) {
runtime.log.warn(runtime.log._("api.library.error-load-entry",{library:opts.library,type:opts.type,path:opts.path,message:err.toString()}));
if (err.code === 'forbidden') {
err.status = 403;
return reject(err);
} else if (err.code === "not_found") {
err.status = 404;
} else {
err.status = 400;
}
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path,error:err.code}, opts.req);
return reject(err);
getEntry: async function(opts) {
return runtime.library.getEntry(opts.library,opts.type,opts.path).then(function(result) {
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path}, opts.req);
return result;
}).catch(function(err) {
if (err) {
runtime.log.warn(runtime.log._("api.library.error-load-entry",{library:opts.library,type:opts.type,path:opts.path,message:err.toString()}));
if (err.code === 'forbidden') {
err.status = 403;
throw err;
} else if (err.code === "not_found") {
err.status = 404;
} else {
err.status = 400;
}
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,error:"not_found"}, opts.req);
var error = new Error();
error.code = "not_found";
error.status = 404;
return reject(error);
});
})
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path,error:err.code}, opts.req);
throw err;
}
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,error:"not_found"}, opts.req);
var error = new Error();
error.code = "not_found";
error.status = 404;
throw error;
});
},

@@ -82,21 +101,18 @@

*/
saveEntry: function(opts) {
return new Promise(function(resolve,reject) {
runtime.library.saveEntry(opts.library,opts.type,opts.path,opts.meta,opts.body).then(function() {
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path}, opts.req);
return resolve();
}).catch(function(err) {
runtime.log.warn(runtime.log._("api.library.error-save-entry",{path:opts.path,message:err.toString()}));
if (err.code === 'forbidden') {
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"forbidden"}, opts.req);
err.status = 403;
return reject(err);
}
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"unexpected_error",message:err.toString()}, opts.req);
var error = new Error();
error.status = 400;
return reject(error);
});
})
saveEntry: async function(opts) {
return runtime.library.saveEntry(opts.library,opts.type,opts.path,opts.meta,opts.body).then(function() {
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path}, opts.req);
}).catch(function(err) {
runtime.log.warn(runtime.log._("api.library.error-save-entry",{path:opts.path,message:err.toString()}));
if (err.code === 'forbidden') {
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"forbidden"}, opts.req);
err.status = 403;
throw err;
}
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"unexpected_error",message:err.toString()}, opts.req);
var error = new Error();
error.status = 400;
throw error;
});
}
}

@@ -21,3 +21,3 @@ /**

var fs = require("fs");
var fs = require("fs-extra");

@@ -56,18 +56,16 @@ var runtime;

*/
getNodeInfo: function(opts) {
return new Promise(function(resolve,reject) {
var id = opts.id;
var result = runtime.nodes.getNodeInfo(id);
if (result) {
runtime.log.audit({event: "nodes.info.get",id:id}, opts.req);
delete result.loaded;
return resolve(result);
} else {
runtime.log.audit({event: "nodes.info.get",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
})
getNodeInfo: async function(opts) {
var id = opts.id;
var result = runtime.nodes.getNodeInfo(id);
if (result) {
runtime.log.audit({event: "nodes.info.get",id:id}, opts.req);
delete result.loaded;
return result;
} else {
runtime.log.audit({event: "nodes.info.get",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
throw err;
}
},

@@ -83,7 +81,5 @@

*/
getNodeList: function(opts) {
return new Promise(function(resolve,reject) {
runtime.log.audit({event: "nodes.list.get"}, opts.req);
return resolve(runtime.nodes.getNodeList());
})
getNodeList: async function(opts) {
runtime.log.audit({event: "nodes.list.get"}, opts.req);
return runtime.nodes.getNodeList();
},

@@ -101,22 +97,20 @@

*/
getNodeConfig: function(opts) {
return new Promise(function(resolve,reject) {
var id = opts.id;
var lang = opts.lang;
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
reject(new Error("Invalid language: "+opts.lang));
return
}
var result = runtime.nodes.getNodeConfig(id,lang);
if (result) {
runtime.log.audit({event: "nodes.config.get",id:id}, opts.req);
return resolve(result);
} else {
runtime.log.audit({event: "nodes.config.get",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
});
getNodeConfig: async function(opts) {
var id = opts.id;
var lang = opts.lang;
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
reject(new Error("Invalid language: "+opts.lang));
return
}
var result = runtime.nodes.getNodeConfig(id,lang);
if (result) {
runtime.log.audit({event: "nodes.config.get",id:id}, opts.req);
return result;
} else {
runtime.log.audit({event: "nodes.config.get",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
throw err;
}
},

@@ -132,11 +126,9 @@ /**

*/
getNodeConfigs: function(opts) {
return new Promise(function(resolve,reject) {
runtime.log.audit({event: "nodes.configs.get"}, opts.req);
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
reject(new Error("Invalid language: "+opts.lang));
return
}
return resolve(runtime.nodes.getNodeConfigs(opts.lang));
});
getNodeConfigs: async function(opts) {
runtime.log.audit({event: "nodes.configs.get"}, opts.req);
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
reject(new Error("Invalid language: "+opts.lang));
return
}
return runtime.nodes.getNodeConfigs(opts.lang);
},

@@ -153,16 +145,14 @@

*/
getModuleInfo: function(opts) {
return new Promise(function(resolve,reject) {
var result = runtime.nodes.getModuleInfo(opts.module);
if (result) {
runtime.log.audit({event: "nodes.module.get",id:opts.module}, opts.req);
return resolve(result);
} else {
runtime.log.audit({event: "nodes.module.get",id:opts.module,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
})
getModuleInfo: async function(opts) {
var result = runtime.nodes.getModuleInfo(opts.module);
if (result) {
runtime.log.audit({event: "nodes.module.get",id:opts.module}, opts.req);
return result;
} else {
runtime.log.audit({event: "nodes.module.get",id:opts.module,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
throw err;
}
},

@@ -182,79 +172,74 @@

*/
addModule: function(opts) {
return new Promise(function(resolve,reject) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.install",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
addModule: async function(opts) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.install",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
throw err;
}
if (opts.tarball) {
if (runtime.settings.externalModules && runtime.settings.externalModules.palette && runtime.settings.externalModules.palette.upload === false) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
err.status = 400;
return reject(err);
throw err;
}
if (opts.tarball) {
if (runtime.settings.editorTheme && runtime.settings.editorTheme.palette && runtime.settings.editorTheme.palette.upload === false) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
if (opts.module || opts.version || opts.url) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,module:opts.module,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
err.status = 400;
throw err;
}
return runtime.nodes.installModule(opts.tarball.buffer).then(function(info) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,module:info.id}, opts.req);
return info;
}).catch(function(err) {
if (err.code) {
err.status = 400;
return reject(err);
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code}, opts.req);
} else {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
}
if (opts.module || opts.version || opts.url) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,module:opts.module,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
throw err;
})
}
if (opts.module) {
var existingModule = runtime.nodes.getModuleInfo(opts.module);
if (existingModule && existingModule.user) {
if (!opts.version || existingModule.version === opts.version) {
runtime.log.audit({event: "nodes.install",module:opts.module, version:opts.version, error:"module_already_loaded"}, opts.req);
var err = new Error("Module already loaded");
err.code = "module_already_loaded";
err.status = 400;
return reject(err);
throw err;
}
runtime.nodes.installModule(opts.tarball.buffer).then(function(info) {
runtime.log.audit({event: "nodes.install",tarball:opts.tarball.file,module:info.id}, opts.req);
return resolve(info);
}).catch(function(err) {
if (err.code) {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code}, opts.req);
} else {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
}
return reject(err);
})
return;
}
if (opts.module) {
var existingModule = runtime.nodes.getModuleInfo(opts.module);
if (existingModule) {
if (!opts.version || existingModule.version === opts.version) {
runtime.log.audit({event: "nodes.install",module:opts.module, version:opts.version, error:"module_already_loaded"}, opts.req);
var err = new Error("Module already loaded");
err.code = "module_already_loaded";
err.status = 400;
return reject(err);
}
return runtime.nodes.installModule(opts.module,opts.version,opts.url).then(function(info) {
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url}, opts.req);
return info;
}).catch(function(err) {
if (err.code === 404) {
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:"not_found"}, opts.req);
// TODO: code/status
err.status = 404;
} else if (err.code) {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code}, opts.req);
} else {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
}
runtime.nodes.installModule(opts.module,opts.version,opts.url).then(function(info) {
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url}, opts.req);
return resolve(info);
}).catch(function(err) {
if (err.code === 404) {
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:"not_found"}, opts.req);
// TODO: code/status
err.status = 404;
} else if (err.code) {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code}, opts.req);
} else {
err.status = 400;
runtime.log.audit({event: "nodes.install",module:opts.module,version:opts.version,url:opts.url,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
}
return reject(err);
})
} else {
runtime.log.audit({event: "nodes.install",module:opts.module,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
err.status = 400;
return reject(err);
}
});
throw err;
})
} else {
runtime.log.audit({event: "nodes.install",module:opts.module,error:"invalid_request"}, opts.req);
var err = new Error("Invalid request");
err.code = "invalid_request";
err.status = 400;
throw err;
}
},

@@ -270,34 +255,31 @@ /**

*/
removeModule: function(opts) {
return new Promise(function(resolve,reject) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.install",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
removeModule: async function(opts) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.install",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
throw err;
}
var module = runtime.nodes.getModuleInfo(opts.module);
if (!module) {
runtime.log.audit({event: "nodes.remove",module:opts.module,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
throw err;
}
try {
return runtime.nodes.uninstallModule(opts.module).then(function() {
runtime.log.audit({event: "nodes.remove",module:opts.module}, opts.req);
}).catch(function(err) {
err.status = 400;
return reject(err);
}
var module = runtime.nodes.getModuleInfo(opts.module);
if (!module) {
runtime.log.audit({event: "nodes.remove",module:opts.module,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
try {
runtime.nodes.uninstallModule(opts.module).then(function() {
runtime.log.audit({event: "nodes.remove",module:opts.module}, opts.req);
resolve();
}).catch(function(err) {
err.status = 400;
runtime.log.audit({event: "nodes.remove",module:opts.module,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
return reject(err);
})
} catch(error) {
runtime.log.audit({event: "nodes.remove",module:opts.module,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
return reject(error);
}
});
runtime.log.audit({event: "nodes.remove",module:opts.module,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
throw err;
})
} catch(error) {
runtime.log.audit({event: "nodes.remove",module:opts.module,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
throw err;
}
},

@@ -315,39 +297,37 @@

*/
setModuleState: function(opts) {
setModuleState: async function(opts) {
var mod = opts.module;
return new Promise(function(resolve,reject) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.module.set",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
return reject(err);
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.module.set",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
throw err;
}
try {
var module = runtime.nodes.getModuleInfo(mod);
if (!module) {
runtime.log.audit({event: "nodes.module.set",module:mod,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
throw err;
}
try {
var module = runtime.nodes.getModuleInfo(mod);
if (!module) {
runtime.log.audit({event: "nodes.module.set",module:mod,error:"not_found"}, opts.req);
var err = new Error("Module not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
var nodes = module.nodes;
var promises = [];
for (var i = 0; i < nodes.length; ++i) {
promises.push(putNode(nodes[i],opts.enabled));
}
Promise.all(promises).then(function() {
return resolve(runtime.nodes.getModuleInfo(mod));
}).catch(function(err) {
err.status = 400;
return reject(err);
});
} catch(error) {
runtime.log.audit({event: "nodes.module.set",module:mod,enabled:opts.enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
return reject(error);
var nodes = module.nodes;
var promises = [];
for (var i = 0; i < nodes.length; ++i) {
promises.push(putNode(nodes[i],opts.enabled));
}
});
return Promise.all(promises).then(function() {
return runtime.nodes.getModuleInfo(mod);
}).catch(function(err) {
err.status = 400;
throw err;
});
} catch(error) {
runtime.log.audit({event: "nodes.module.set",module:mod,enabled:opts.enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
throw err;
}
},

@@ -365,39 +345,37 @@

*/
setNodeSetState: function(opts) {
return new Promise(function(resolve,reject) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.info.set",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
return reject(err);
}
setNodeSetState: async function(opts) {
if (!runtime.settings.available()) {
runtime.log.audit({event: "nodes.info.set",error:"settings_unavailable"}, opts.req);
var err = new Error("Settings unavailable");
err.code = "settings_unavailable";
err.status = 400;
throw err;
}
var id = opts.id;
var enabled = opts.enabled;
try {
var node = runtime.nodes.getNodeInfo(id);
if (!node) {
runtime.log.audit({event: "nodes.info.set",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
return reject(err);
} else {
delete node.loaded;
putNode(node,enabled).then(function(result) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled}, opts.req);
return resolve(result);
}).catch(function(err) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
return reject(err);
});
}
} catch(error) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
return reject(error);
var id = opts.id;
var enabled = opts.enabled;
try {
var node = runtime.nodes.getNodeInfo(id);
if (!node) {
runtime.log.audit({event: "nodes.info.set",id:id,error:"not_found"}, opts.req);
var err = new Error("Node not found");
err.code = "not_found";
err.status = 404;
throw err;
} else {
delete node.loaded;
return putNode(node,enabled).then(function(result) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled}, opts.req);
return result;
}).catch(function(err) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
throw err;
});
}
});
} catch(error) {
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
error.status = 400;
throw err;
}
},

@@ -414,12 +392,12 @@

*/
getModuleCatalogs: function(opts) {
return new Promise(function(resolve,reject) {
var namespace = opts.module;
var lang = opts.lang;
var prevLang = runtime.i18n.i.language;
if (/[^0-9a-z=\-\*]/i.test(lang)) {
reject(new Error("Invalid language: "+lang));
return
}
// Trigger a load from disk of the language if it is not the default
getModuleCatalogs: async function(opts) {
var namespace = opts.module;
var lang = opts.lang;
if (/[^0-9a-z=\-\*]/i.test(lang)) {
reject(new Error("Invalid language: "+lang));
return
}
var prevLang = runtime.i18n.i.language;
// Trigger a load from disk of the language if it is not the default
return new Promise( (resolve,reject) => {
runtime.i18n.i.changeLanguage(lang, function(){

@@ -433,5 +411,5 @@ var nodeList = runtime.nodes.getNodeList();

});
runtime.i18n.i.changeLanguage(prevLang);
resolve(result);
});
runtime.i18n.i.changeLanguage(prevLang);
});

@@ -450,17 +428,17 @@ },

*/
getModuleCatalog: function(opts) {
return new Promise(function(resolve,reject) {
var namespace = opts.module;
var lang = opts.lang;
if (/[^0-9a-z=\-\*]/i.test(lang)) {
reject(new Error("Invalid language: "+lang));
return
}
var prevLang = runtime.i18n.i.language;
// Trigger a load from disk of the language if it is not the default
runtime.i18n.i.changeLanguage(lang, function(){
getModuleCatalog: async function(opts) {
var namespace = opts.module;
var lang = opts.lang;
if (/[^0-9a-z=\-\*]/i.test(lang)) {
reject(new Error("Invalid language: "+lang));
return
}
var prevLang = runtime.i18n.i.language;
// Trigger a load from disk of the language if it is not the default
return new Promise(resolve => {
runtime.i18n.i.changeLanguage(lang, function() {
var catalog = runtime.i18n.i.getResourceBundle(lang, namespace);
runtime.i18n.i.changeLanguage(prevLang);
resolve(catalog||{});
});
runtime.i18n.i.changeLanguage(prevLang);
});

@@ -477,8 +455,5 @@ },

*/
getIconList: function(opts) {
return new Promise(function(resolve,reject) {
runtime.log.audit({event: "nodes.icons.get"}, opts.req);
return resolve(runtime.nodes.getNodeIcons());
});
getIconList: async function(opts) {
runtime.log.audit({event: "nodes.icons.get"}, opts.req);
return runtime.nodes.getNodeIcons();
},

@@ -495,18 +470,13 @@ /**

*/
getIcon: function(opts) {
return new Promise(function(resolve,reject) {
var iconPath = runtime.nodes.getNodeIconPath(opts.module,opts.icon);
if (iconPath) {
fs.readFile(iconPath,function(err,data) {
if (err) {
err.status = 400;
return reject(err);
}
return resolve(data)
});
} else {
resolve(null);
}
});
getIcon: async function(opts) {
var iconPath = runtime.nodes.getNodeIconPath(opts.module,opts.icon);
if (iconPath) {
return fs.readFile(iconPath).catch(err => {
err.status = 400;
throw err;
});
} else {
return null
}
}
}

@@ -27,4 +27,4 @@ /**

},
available: function(opts) {
return Promise.resolve(!!runtime.storage.projects);
available: async function(opts) {
return !!runtime.storage.projects;
},

@@ -40,3 +40,3 @@ /**

*/
listProjects: function(opts) {
listProjects: async function(opts) {
return runtime.storage.projects.listProjects(opts.user).then(function(list) {

@@ -66,3 +66,3 @@ var active = runtime.storage.projects.getActiveProject(opts.user);

*/
createProject: function(opts) {
createProject: async function(opts) {
runtime.log.audit({event: "projects.create",name:opts.project?opts.project.name:"missing-name"}, opts.req);

@@ -82,3 +82,3 @@ return runtime.storage.projects.createProject(opts.user, opts.project)

*/
initialiseProject: function(opts) {
initialiseProject: async function(opts) {
// Initialised set when creating default files for an empty repo

@@ -97,4 +97,4 @@ runtime.log.audit({event: "projects.initialise",id:opts.id}, opts.req);

*/
getActiveProject: function(opts) {
return Promise.resolve(runtime.storage.projects.getActiveProject(opts.user));
getActiveProject: async function(opts) {
return runtime.storage.projects.getActiveProject(opts.user);
},

@@ -111,3 +111,3 @@

*/
setActiveProject: function(opts) {
setActiveProject: async function(opts) {
var currentProject = runtime.storage.projects.getActiveProject(opts.user);

@@ -117,4 +117,2 @@ runtime.log.audit({event: "projects.set",id:opts.id}, opts.req);

return runtime.storage.projects.setActiveProject(opts.user, opts.id);
} else {
return Promise.resolve();
}

@@ -132,3 +130,3 @@ },

*/
getProject: function(opts) {
getProject: async function(opts) {
return runtime.storage.projects.getProject(opts.user, opts.id)

@@ -147,3 +145,3 @@ },

*/
updateProject: function(opts) {
updateProject: async function(opts) {
runtime.log.audit({event: "projects.update",id:opts.id}, opts.req);

@@ -162,3 +160,3 @@ return runtime.storage.projects.updateProject(opts.user, opts.id, opts.project);

*/
deleteProject: function(opts) {
deleteProject: async function(opts) {
runtime.log.audit({event: "projects.delete",id:opts.id}, opts.req);

@@ -178,3 +176,3 @@ return runtime.storage.projects.deleteProject(opts.user, opts.id);

*/
getStatus: function(opts) {
getStatus: async function(opts) {
return runtime.storage.projects.getStatus(opts.user, opts.id, opts.remote)

@@ -193,3 +191,3 @@ },

*/
getBranches: function(opts) {
getBranches: async function(opts) {
return runtime.storage.projects.getBranches(opts.user, opts.id, opts.remote);

@@ -208,3 +206,3 @@ },

*/
getBranchStatus: function(opts) {
getBranchStatus: async function(opts) {
return runtime.storage.projects.getBranchStatus(opts.user, opts.id, opts.branch);

@@ -224,3 +222,3 @@ },

*/
setBranch: function(opts) {
setBranch: async function(opts) {
runtime.log.audit({event: "projects.branch.set",id:opts.id, branch: opts.branch, create:opts.create}, opts.req);

@@ -241,3 +239,3 @@ return runtime.storage.projects.setBranch(opts.user, opts.id, opts.branch, opts.create)

*/
deleteBranch: function(opts) {
deleteBranch: async function(opts) {
runtime.log.audit({event: "projects.branch.delete",id:opts.id, branch: opts.branch, force:opts.force}, opts.req);

@@ -257,3 +255,3 @@ return runtime.storage.projects.deleteBranch(opts.user, opts.id, opts.branch, false, opts.force);

*/
commit: function(opts) {
commit: async function(opts) {
runtime.log.audit({event: "projects.commit",id:opts.id}, opts.req);

@@ -273,3 +271,3 @@ return runtime.storage.projects.commit(opts.user, opts.id,{message: opts.message});

*/
getCommit: function(opts) {
getCommit: async function(opts) {
return runtime.storage.projects.getCommit(opts.user, opts.id, opts.sha);

@@ -289,3 +287,3 @@ },

*/
getCommits: function(opts) {
getCommits: async function(opts) {
return runtime.storage.projects.getCommits(opts.user, opts.id, {

@@ -306,3 +304,3 @@ limit: opts.limit || 20,

*/
abortMerge: function(opts) {
abortMerge: async function(opts) {
runtime.log.audit({event: "projects.merge.abort",id:opts.id}, opts.req);

@@ -323,3 +321,3 @@ return runtime.storage.projects.abortMerge(opts.user, opts.id);

*/
resolveMerge: function(opts) {
resolveMerge: async function(opts) {
runtime.log.audit({event: "projects.merge.resolve",id:opts.id, file:opts.path}, opts.req);

@@ -338,3 +336,3 @@ return runtime.storage.projects.resolveMerge(opts.user, opts.id, opts.path, opts.resolution);

*/
getFiles: function(opts) {
getFiles: async function(opts) {
return runtime.storage.projects.getFiles(opts.user, opts.id);

@@ -354,3 +352,3 @@ },

*/
getFile: function(opts) {
getFile: async function(opts) {
return runtime.storage.projects.getFile(opts.user, opts.id,opts.path,opts.tree);

@@ -369,3 +367,3 @@ },

*/
stageFile: function(opts) {
stageFile: async function(opts) {
runtime.log.audit({event: "projects.file.stage",id:opts.id, file:opts.path}, opts.req);

@@ -385,3 +383,3 @@ return runtime.storage.projects.stageFile(opts.user, opts.id, opts.path);

*/
unstageFile: function(opts) {
unstageFile: async function(opts) {
runtime.log.audit({event: "projects.file.unstage",id:opts.id, file:opts.path}, opts.req);

@@ -401,3 +399,3 @@ return runtime.storage.projects.unstageFile(opts.user, opts.id, opts.path);

*/
revertFile: function(opts) {
revertFile: async function(opts) {
runtime.log.audit({event: "projects.file.revert",id:opts.id, file:opts.path}, opts.req);

@@ -418,3 +416,3 @@ return runtime.storage.projects.revertFile(opts.user, opts.id,opts.path)

*/
getFileDiff: function(opts) {
getFileDiff: async function(opts) {
return runtime.storage.projects.getFileDiff(opts.user, opts.id, opts.path, opts.type);

@@ -432,3 +430,3 @@ },

*/
getRemotes: function(opts) {
getRemotes: async function(opts) {
return runtime.storage.projects.getRemotes(opts.user, opts.id);

@@ -450,3 +448,3 @@

*/
addRemote: function(opts) {
addRemote: async function(opts) {
runtime.log.audit({event: "projects.remote.add",id:opts.id, remote:opts.remote.name}, opts.req);

@@ -466,3 +464,3 @@ return runtime.storage.projects.addRemote(opts.user, opts.id, opts.remote)

*/
removeRemote: function(opts) {
removeRemote: async function(opts) {
runtime.log.audit({event: "projects.remote.delete",id:opts.id, remote:opts.remote}, opts.req);

@@ -483,3 +481,3 @@ return runtime.storage.projects.removeRemote(opts.user, opts.id, opts.remote);

*/
updateRemote: function(opts) {
updateRemote: async function(opts) {
runtime.log.audit({event: "projects.remote.update",id:opts.id, remote:opts.remote.name}, opts.req);

@@ -500,3 +498,3 @@ return runtime.storage.projects.updateRemote(opts.user, opts.id, opts.remote.name, opts.remote)

*/
pull: function(opts) {
pull: async function(opts) {
runtime.log.audit({event: "projects.pull",id:opts.id, remote: opts.remote, track:opts.track}, opts.req);

@@ -517,3 +515,3 @@ return runtime.storage.projects.pull(opts.user, opts.id, opts.remote, opts.track, opts.allowUnrelatedHistories);

*/
push: function(opts) {
push: async function(opts) {
runtime.log.audit({event: "projects.push",id:opts.id, remote: opts.remote, track:opts.track}, opts.req);

@@ -520,0 +518,0 @@ return runtime.storage.projects.push(opts.user, opts.id, opts.remote, opts.track);

@@ -67,60 +67,72 @@ /**

*/
getRuntimeSettings: function(opts) {
return new Promise(function(resolve,reject) {
try {
var safeSettings = {
httpNodeRoot: runtime.settings.httpNodeRoot||"/",
version: runtime.settings.version
getRuntimeSettings: async function(opts) {
var safeSettings = {
httpNodeRoot: runtime.settings.httpNodeRoot||"/",
version: runtime.settings.version
}
if (opts.user) {
safeSettings.user = {}
var props = ["anonymous","username","image","permissions"];
props.forEach(prop => {
if (opts.user.hasOwnProperty(prop)) {
safeSettings.user[prop] = opts.user[prop];
}
if (opts.user) {
safeSettings.user = {}
var props = ["anonymous","username","image","permissions"];
props.forEach(prop => {
if (opts.user.hasOwnProperty(prop)) {
safeSettings.user[prop] = opts.user[prop];
}
})
}
})
}
if (!runtime.settings.disableEditor) {
safeSettings.context = runtime.nodes.listContextStores();
if (!runtime.settings.disableEditor) {
safeSettings.context = runtime.nodes.listContextStores();
safeSettings.libraries = runtime.library.getLibraries();
if (util.isArray(runtime.settings.paletteCategories)) {
safeSettings.paletteCategories = runtime.settings.paletteCategories;
}
if (util.isArray(runtime.settings.paletteCategories)) {
safeSettings.paletteCategories = runtime.settings.paletteCategories;
}
if (runtime.settings.flowFilePretty) {
safeSettings.flowFilePretty = runtime.settings.flowFilePretty;
}
if (runtime.settings.flowFilePretty) {
safeSettings.flowFilePretty = runtime.settings.flowFilePretty;
}
if (runtime.settings.editorTheme && runtime.settings.editorTheme.palette) {
if (runtime.settings.editorTheme.palette.upload === false || runtime.settings.editorTheme.palette.editable === false) {
safeSettings.externalModules = {palette: { } }
}
if (runtime.settings.editorTheme.palette.upload === false) {
safeSettings.externalModules.palette.allowUpload = false;
}
if (runtime.settings.editorTheme.palette.editable === false) {
safeSettings.externalModules.palette.allowInstall = false;
safeSettings.externalModules.palette.allowUpload = false;
}
}
if (!runtime.nodes.paletteEditorEnabled()) {
safeSettings.editorTheme = safeSettings.editorTheme || {};
safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {};
safeSettings.editorTheme.palette.editable = false;
if (runtime.settings.externalModules) {
safeSettings.externalModules = extend(safeSettings.externalModules||{},runtime.settings.externalModules);
}
if (!runtime.nodes.installerEnabled()) {
safeSettings.externalModules = safeSettings.externalModules || {};
safeSettings.externalModules.palette = safeSettings.externalModules.palette || {};
safeSettings.externalModules.palette.allowInstall = false;
safeSettings.externalModules.palette.allowUpload = false;
}
if (runtime.storage.projects) {
var activeProject = runtime.storage.projects.getActiveProject();
if (activeProject) {
safeSettings.project = activeProject;
} else if (runtime.storage.projects.flowFileExists()) {
safeSettings.files = {
flow: runtime.storage.projects.getFlowFilename(),
credentials: runtime.storage.projects.getCredentialsFilename()
}
if (runtime.storage.projects) {
var activeProject = runtime.storage.projects.getActiveProject();
if (activeProject) {
safeSettings.project = activeProject;
} else if (runtime.storage.projects.flowFileExists()) {
safeSettings.files = {
flow: runtime.storage.projects.getFlowFilename(),
credentials: runtime.storage.projects.getCredentialsFilename()
}
}
safeSettings.git = {
globalUser: runtime.storage.projects.getGlobalGitUser()
}
}
safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType();
runtime.settings.exportNodeSettings(safeSettings);
}
safeSettings.git = {
globalUser: runtime.storage.projects.getGlobalGitUser()
}
}
safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType();
runtime.settings.exportNodeSettings(safeSettings);
runtime.plugins.exportPluginSettings(safeSettings);
}
resolve(safeSettings);
}catch(err) {
console.log(err);
}
});
return safeSettings;
},

@@ -136,3 +148,3 @@

*/
getUserSettings: function(opts) {
getUserSettings: async function(opts) {
var username;

@@ -144,3 +156,3 @@ if (!opts.user || opts.user.anonymous) {

}
return Promise.resolve(runtime.settings.getUserSettings(username)||{});
return runtime.settings.getUserSettings(username)||{};
},

@@ -157,3 +169,3 @@

*/
updateUserSettings: function(opts) {
updateUserSettings: async function(opts) {
var username;

@@ -165,21 +177,19 @@ if (!opts.user || opts.user.anonymous) {

}
return new Promise(function(resolve,reject) {
var currentSettings = runtime.settings.getUserSettings(username)||{};
currentSettings = extend(currentSettings, opts.settings);
try {
runtime.settings.setUserSettings(username, currentSettings).then(function() {
runtime.log.audit({event: "settings.update",username:username}, opts.req);
return resolve();
}).catch(function(err) {
runtime.log.audit({event: "settings.update",username:username,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
return reject(err);
});
} catch(err) {
runtime.log.warn(runtime.log._("settings.user-not-available",{message:runtime.log._("settings.not-available")}));
var currentSettings = runtime.settings.getUserSettings(username)||{};
currentSettings = extend(currentSettings, opts.settings);
try {
return runtime.settings.setUserSettings(username, currentSettings).then(function() {
runtime.log.audit({event: "settings.update",username:username}, opts.req);
return;
}).catch(function(err) {
runtime.log.audit({event: "settings.update",username:username,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
return reject(err);
}
});
throw err;
});
} catch(err) {
runtime.log.warn(runtime.log._("settings.user-not-available",{message:runtime.log._("settings.not-available")}));
runtime.log.audit({event: "settings.update",username:username,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
throw err;
}
},

@@ -195,11 +205,8 @@

*/
getUserKeys: function(opts) {
return new Promise(function(resolve,reject) {
var username = getSSHKeyUsername(opts.user);
runtime.storage.projects.ssh.listSSHKeys(username).then(function(list) {
return resolve(list);
}).catch(function(err) {
err.status = 400;
return reject(err);
});
getUserKeys: async function(opts) {
var username = getSSHKeyUsername(opts.user);
return runtime.storage.projects.ssh.listSSHKeys(username).catch(function(err) {
err.status = 400;
throw err;
return reject(err);
});

@@ -217,19 +224,19 @@ },

*/
getUserKey: function(opts) {
return new Promise(function(resolve,reject) {
var username = getSSHKeyUsername(opts.user);
// console.log('username:', username);
runtime.storage.projects.ssh.getSSHKey(username, opts.id).then(function(data) {
if (data) {
return resolve(data);
} else {
var err = new Error("Key not found");
err.code = "not_found";
err.status = 404;
return reject(err);
}
}).catch(function(err) {
getUserKey: async function(opts) {
var username = getSSHKeyUsername(opts.user);
// console.log('username:', username);
return runtime.storage.projects.ssh.getSSHKey(username, opts.id).then(function(data) {
if (data) {
return data;
} else {
var err = new Error("Key not found");
err.code = "not_found";
err.status = 404;
throw err;
}
}).catch(function(err) {
if (!err.status) {
err.status = 400;
return reject(err);
});
}
throw err;
});

@@ -250,11 +257,7 @@ },

*/
generateUserKey: function(opts) {
return new Promise(function(resolve,reject) {
var username = getSSHKeyUsername(opts.user);
runtime.storage.projects.ssh.generateSSHKey(username, opts).then(function(name) {
return resolve(name);
}).catch(function(err) {
err.status = 400;
return reject(err);
});
generateUserKey: async function(opts) {
var username = getSSHKeyUsername(opts.user);
return runtime.storage.projects.ssh.generateSSHKey(username, opts).catch(function(err) {
err.status = 400;
throw err;
});

@@ -272,14 +275,9 @@ },

*/
removeUserKey: function(opts) {
return new Promise(function(resolve,reject) {
var username = getSSHKeyUsername(opts.user);
runtime.storage.projects.ssh.deleteSSHKey(username, opts.id).then(function() {
return resolve();
}).catch(function(err) {
err.status = 400;
return reject(err);
});
removeUserKey: async function(opts) {
var username = getSSHKeyUsername(opts.user);
return runtime.storage.projects.ssh.deleteSSHKey(username, opts.id).catch(function(err) {
err.status = 400;
throw err;
});
}
}

@@ -19,4 +19,4 @@ /**

var redUtil = require("@node-red/util").util;
const events = require("@node-red/util").events;
var flowUtil = require("./util");
var events = require("../events");
const context = require('../nodes/context');

@@ -97,3 +97,3 @@ const hooks = require("../hooks");

*/
log(msg) {
info(msg) {
Log.log({

@@ -121,2 +121,13 @@ id: this.id||"global",

/**
* [log description]
* @param {[type]} msg [description]
* @return {[type]} [description]
*/
log(msg) {
if (!msg.path) {
msg.path = this.path;
}
this.parent.log(msg);
}

@@ -303,2 +314,13 @@ /**

let nodesToStop = [];
let configsToStop = [];
stopList.forEach(id => {
if (this.flow.configs[id]) {
configsToStop.push(id);
} else {
nodesToStop.push(id);
}
});
stopList = nodesToStop.concat(configsToStop);
var promises = [];

@@ -310,18 +332,10 @@ for (i=0;i<stopList.length;i++) {

if (this.subflowInstanceNodes[stopList[i]]) {
try {
(function(subflow) {
promises.push(stopNode(node,false).then(() => subflow.stop()));
})(this.subflowInstanceNodes[stopList[i]]);
} catch(err) {
node.error(err);
}
delete this.subflowInstanceNodes[stopList[i]];
} else {
try {
var removed = removedMap[stopList[i]];
promises.push(stopNode(node,removed).catch(()=>{}));
} catch(err) {
node.error(err);
}
}
try {
var removed = removedMap[stopList[i]];
promises.push(stopNode(node,removed).catch(()=>{}));
} catch(err) {
node.error(err);
}
if (removedMap[stopList[i]]) {

@@ -422,2 +436,8 @@ events.emit("node-status",{

if (!muteStatusEvent) {
if (statusMessage.hasOwnProperty("text") && typeof(statusMessage.text !== "string")) {
try {
statusMessage.text = statusMessage.text.toString();
}
catch(e) {}
}
events.emit("node-status",{

@@ -688,3 +708,2 @@ id: node.id,

Subflow = require("./Subflow");
Subflow.init(runtime);
},

@@ -691,0 +710,0 @@ create: function(parent,global,conf) {

@@ -28,5 +28,4 @@ /**

var log;
var events = require("../events");
const events = require("@node-red/util").events;
var redUtil = require("@node-red/util").util;
const hooks = require("../hooks");

@@ -192,31 +191,31 @@ var storage = null;

return configSavePromise
.then(function(flowRevision) {
if (!isLoad) {
log.debug("saved flow revision: "+flowRevision);
}
activeConfig = {
flows:config,
rev:flowRevision
};
activeFlowConfig = newFlowConfig;
if (forceStart || started) {
// Flows are running (or should be)
// Stop the active flows (according to deploy type and the diff)
return stop(type,diff,muteLog).then(() => {
// Once stopped, allow context to remove anything no longer needed
return context.clean(activeFlowConfig)
}).then(() => {
// Start the active flows
start(type,diff,muteLog).then(() => {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
});
// Return the new revision asynchronously to the actual start
return flowRevision;
}).catch(function(err) { })
} else {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
}
});
return configSavePromise.then(flowRevision => {
if (!isLoad) {
log.debug("saved flow revision: "+flowRevision);
}
activeConfig = {
flows:config,
rev:flowRevision
};
activeFlowConfig = newFlowConfig;
if (forceStart || started) {
// Flows are running (or should be)
// Stop the active flows (according to deploy type and the diff)
return stop(type,diff,muteLog).then(() => {
// Once stopped, allow context to remove anything no longer needed
return context.clean(activeFlowConfig)
}).then(() => {
// Start the active flows
start(type,diff,muteLog).then(() => {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
});
// Return the new revision asynchronously to the actual start
return flowRevision;
}).catch(function(err) { })
} else {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
}
});
}

@@ -252,3 +251,3 @@

function start(type,diff,muteLog) {
async function start(type,diff,muteLog) {
type = type||"full";

@@ -278,5 +277,19 @@ started = true;

events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-types", type:"warning",text:"notification.warnings.missing-types",types:activeFlowConfig.missingTypes},retain:true});
return Promise.resolve();
return;
}
try {
await typeRegistry.checkFlowDependencies(activeConfig.flows);
} catch(err) {
log.info("Failed to load external modules required by this flow:");
const missingModules = [];
for (i=0;i<err.length;i++) {
let errMessage = err[i].error.toString()
missingModules.push({module:err[i].module.module, error: err[i].error.code || err[i].error.toString()})
log.info(` - ${err[i].module.spec} [${err[i].error.code || "unknown_error"}]`);
}
events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});
return;
}
// In safe mode, don't actually start anything, emit the necessary runtime event and return

@@ -288,3 +301,3 @@ if (settings.safeMode) {

events.emit("runtime-event",{id:"runtime-state",payload:{error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
return Promise.resolve();
return;
}

@@ -379,3 +392,3 @@

}
return Promise.resolve();
return;
}

@@ -414,3 +427,11 @@

for (var id in activeFlows) {
// Stop the global flow object last
let activeFlowIds = Object.keys(activeFlows);
let globalIndex = activeFlowIds.indexOf("global");
if (globalIndex !== -1) {
activeFlowIds.splice(globalIndex,1);
activeFlowIds.push("global");
}
activeFlowIds.forEach(id => {
if (activeFlows.hasOwnProperty(id)) {

@@ -424,6 +445,6 @@ var flowStateChanged = diff && (diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1);

}
}
});
return Promise.all(promises).then(function() {
for (id in activeNodesToFlow) {
for (let id in activeNodesToFlow) {
if (activeNodesToFlow.hasOwnProperty(id)) {

@@ -710,3 +731,4 @@ if (!activeFlows[activeNodesToFlow[id]]) {

handleStatus: () => false,
getSetting: k => flowUtil.getEnvVar(k)
getSetting: k => flowUtil.getEnvVar(k),
log: m => log.log(m)
}

@@ -724,3 +746,3 @@

loadFlows: load,
get:getNode,

@@ -727,0 +749,0 @@ eachNode: eachNode,

@@ -21,12 +21,7 @@ /**

const util = require("util");
const events = require("../events");
const redUtil = require("@node-red/util").util;
const events = require("@node-red/util").events;
const flowUtil = require("./util");
const credentials = require("../nodes/credentials");
var Log;
/**

@@ -217,3 +212,8 @@ * Create deep copy of object

this.node.on("input", function(msg) { this.send(msg);});
this.node.on("close", function() { this.status({}); })
// Called when the subflow instance node is being stopped
this.node.on("close", function(done) {
this.status({});
// Stop the complete subflow
self.stop().finally(done)
})
this.node.status = status => this.parent.handleStatus(this.node,status);

@@ -509,2 +509,34 @@ // Create a context instance

class SubflowModule extends Subflow {
/**
* Create a Subflow Module object.
* This is a node that has been published as a subflow.
* @param {[type]} parent [description]
* @param {[type]} globalFlow [description]
* @param {[type]} subflowDef [description]
* @param {[type]} subflowInstance [description]
*/
constructor(type, parent,globalFlow,subflowDef,subflowInstance) {
super(parent,globalFlow,subflowDef,subflowInstance);
this.TYPE = `module:${type}`;
this.subflowType = type;
}
/**
* [log description]
* @param {[type]} msg [description]
* @return {[type]} [description]
*/
log(msg) {
if (msg.id) {
msg.id = this.id
}
if (msg.type) {
msg.type = this.subflowType
}
super.log(msg);
}
}
function createSubflow(parent,globalFlow,subflowDef,subflowInstance) {

@@ -514,7 +546,11 @@ return new Subflow(parent,globalFlow,subflowDef,subflowInstance)

function createModuleInstance(type, parent,globalFlow,subflowDef,subflowInstance) {
return new SubflowModule(type, parent,globalFlow,subflowDef,subflowInstance);
}
module.exports = {
init: function(runtime) {
Log = runtime.log;
},
create: createSubflow
init: function(runtime) {},
create: createSubflow,
createModuleInstance: createModuleInstance
}

@@ -22,2 +22,3 @@ /**

var envVarExcludes = {};

@@ -70,2 +71,191 @@

function createNode(flow,config) {
var newNode = null;
var type = config.type;
try {
var nodeTypeConstructor = typeRegistry.get(type);
if (typeof nodeTypeConstructor === "function") {
var conf = clone(config);
delete conf.credentials;
for (var p in conf) {
if (conf.hasOwnProperty(p)) {
mapEnvVarProperties(conf,p,flow);
}
}
try {
Object.defineProperty(conf,'_flow', {value: flow, enumerable: false, writable: true })
newNode = new nodeTypeConstructor(conf);
} catch (err) {
Log.log({
level: Log.ERROR,
id:conf.id,
type: type,
msg: err
});
}
} else if (nodeTypeConstructor) {
// console.log(nodeTypeConstructor)
var subflowConfig = parseConfig([nodeTypeConstructor.subflow].concat(nodeTypeConstructor.subflow.flow));
var instanceConfig = clone(config);
instanceConfig.env = clone(nodeTypeConstructor.subflow.env);
instanceConfig.env = nodeTypeConstructor.subflow.env.map(nodeProp => {
var nodePropType;
var nodePropValue = config[nodeProp.name];
if (nodeProp.type === "cred") {
nodePropType = "cred";
} else {
switch(typeof config[nodeProp.name]) {
case "string": nodePropType = "str"; break;
case "number": nodePropType = "num"; break;
case "boolean": nodePropType = "bool"; nodePropValue = nodeProp?"true":"false"; break;
default:
nodePropType = config[nodeProp.name].type;
nodePropValue = config[nodeProp.name].value;
}
}
return {
name: nodeProp.name,
type: nodePropType,
value: nodePropValue
}
})
var subflow = require("./Subflow").createModuleInstance(
nodeTypeConstructor.type,
flow,
flow.global,
subflowConfig.subflows[nodeTypeConstructor.subflow.id],
instanceConfig
);
subflow.start();
return subflow.node;
Log.error(Log._("nodes.flow.unknown-type", {type:type}));
}
} catch(err) {
Log.error(err);
}
return newNode;
}
function parseConfig(config) {
var flow = {};
flow.allNodes = {};
flow.subflows = {};
flow.configs = {};
flow.flows = {};
flow.groups = {};
flow.missingTypes = [];
config.forEach(function(n) {
flow.allNodes[n.id] = clone(n);
if (n.type === 'tab') {
flow.flows[n.id] = n;
flow.flows[n.id].subflows = {};
flow.flows[n.id].configs = {};
flow.flows[n.id].nodes = {};
}
if (n.type === 'group') {
flow.groups[n.id] = n;
}
});
// TODO: why a separate forEach? this can be merged with above
config.forEach(function(n) {
if (n.type === 'subflow') {
flow.subflows[n.id] = n;
flow.subflows[n.id].configs = {};
flow.subflows[n.id].nodes = {};
flow.subflows[n.id].instances = [];
}
});
var linkWires = {};
var linkOutNodes = [];
config.forEach(function(n) {
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
var subflowDetails = subflowInstanceRE.exec(n.type);
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
if (flow.missingTypes.indexOf(n.type) === -1) {
flow.missingTypes.push(n.type);
}
}
var container = null;
if (flow.flows[n.z]) {
container = flow.flows[n.z];
} else if (flow.subflows[n.z]) {
container = flow.subflows[n.z];
}
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
if (subflowDetails) {
var subflowType = subflowDetails[1]
n.subflow = subflowType;
flow.subflows[subflowType].instances.push(n)
}
if (container) {
container.nodes[n.id] = n;
}
} else {
if (container) {
container.configs[n.id] = n;
} else {
flow.configs[n.id] = n;
flow.configs[n.id]._users = [];
}
}
if (n.type === 'link in' && n.links) {
// Ensure wires are present in corresponding link out nodes
n.links.forEach(function(id) {
linkWires[id] = linkWires[id]||{};
linkWires[id][n.id] = true;
})
} else if (n.type === 'link out' && n.links) {
linkWires[n.id] = linkWires[n.id]||{};
n.links.forEach(function(id) {
linkWires[n.id][id] = true;
})
linkOutNodes.push(n);
}
}
});
linkOutNodes.forEach(function(n) {
var links = linkWires[n.id];
var targets = Object.keys(links);
n.wires = [targets];
});
var addedTabs = {};
config.forEach(function(n) {
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
for (var prop in n) {
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
// This property references a global config node
flow.configs[n[prop]]._users.push(n.id)
}
}
if (n.z && !flow.subflows[n.z]) {
if (!flow.flows[n.z]) {
flow.flows[n.z] = {type:'tab',id:n.z};
flow.flows[n.z].subflows = {};
flow.flows[n.z].configs = {};
flow.flows[n.z].nodes = {};
addedTabs[n.z] = flow.flows[n.z];
}
if (addedTabs[n.z]) {
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
addedTabs[n.z].nodes[n.id] = n;
} else {
addedTabs[n.z].configs[n.id] = n;
}
}
}
}
});
return flow;
}
module.exports = {

@@ -84,120 +274,4 @@ init: function(runtime) {

parseConfig: function(config) {
var flow = {};
flow.allNodes = {};
flow.subflows = {};
flow.configs = {};
flow.flows = {};
flow.groups = {};
flow.missingTypes = [];
parseConfig: parseConfig,
config.forEach(function(n) {
flow.allNodes[n.id] = clone(n);
if (n.type === 'tab') {
flow.flows[n.id] = n;
flow.flows[n.id].subflows = {};
flow.flows[n.id].configs = {};
flow.flows[n.id].nodes = {};
}
if (n.type === 'group') {
flow.groups[n.id] = n;
}
});
// TODO: why a separate forEach? this can be merged with above
config.forEach(function(n) {
if (n.type === 'subflow') {
flow.subflows[n.id] = n;
flow.subflows[n.id].configs = {};
flow.subflows[n.id].nodes = {};
flow.subflows[n.id].instances = [];
}
});
var linkWires = {};
var linkOutNodes = [];
config.forEach(function(n) {
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
var subflowDetails = subflowInstanceRE.exec(n.type);
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
if (flow.missingTypes.indexOf(n.type) === -1) {
flow.missingTypes.push(n.type);
}
}
var container = null;
if (flow.flows[n.z]) {
container = flow.flows[n.z];
} else if (flow.subflows[n.z]) {
container = flow.subflows[n.z];
}
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
if (subflowDetails) {
var subflowType = subflowDetails[1]
n.subflow = subflowType;
flow.subflows[subflowType].instances.push(n)
}
if (container) {
container.nodes[n.id] = n;
}
} else {
if (container) {
container.configs[n.id] = n;
} else {
flow.configs[n.id] = n;
flow.configs[n.id]._users = [];
}
}
if (n.type === 'link in' && n.links) {
// Ensure wires are present in corresponding link out nodes
n.links.forEach(function(id) {
linkWires[id] = linkWires[id]||{};
linkWires[id][n.id] = true;
})
} else if (n.type === 'link out' && n.links) {
linkWires[n.id] = linkWires[n.id]||{};
n.links.forEach(function(id) {
linkWires[n.id][id] = true;
})
linkOutNodes.push(n);
}
}
});
linkOutNodes.forEach(function(n) {
var links = linkWires[n.id];
var targets = Object.keys(links);
n.wires = [targets];
});
var addedTabs = {};
config.forEach(function(n) {
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
for (var prop in n) {
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
// This property references a global config node
flow.configs[n[prop]]._users.push(n.id)
}
}
if (n.z && !flow.subflows[n.z]) {
if (!flow.flows[n.z]) {
flow.flows[n.z] = {type:'tab',id:n.z};
flow.flows[n.z].subflows = {};
flow.flows[n.z].configs = {};
flow.flows[n.z].nodes = {};
addedTabs[n.z] = flow.flows[n.z];
}
if (addedTabs[n.z]) {
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
addedTabs[n.z].nodes[n.id] = n;
} else {
addedTabs[n.z].configs[n.id] = n;
}
}
}
}
});
return flow;
},
diffConfigs: function(oldConfig, newConfig) {

@@ -374,4 +448,17 @@ var id;

// changed.
if (changed[node[prop]] || removed[node[prop]]) {
var changeOrigin = changed[node[prop]];
if (changeOrigin || removed[node[prop]]) {
if (!changed[node.id]) {
if (changeOrigin &&
(prop === "g") &&
(changeOrigin.type === "group")) {
var oldNode = oldConfig.allNodes[node.id];
// ignore change of group node
// if group of this node not changed
if (oldNode &&
(node.g === oldNode.g)) {
continue;
}
}
madeChange = true;

@@ -482,34 +569,3 @@ changed[node.id] = node;

*/
createNode: function(flow,config) {
var newNode = null;
var type = config.type;
try {
var nodeTypeConstructor = typeRegistry.get(type);
if (nodeTypeConstructor) {
var conf = clone(config);
delete conf.credentials;
for (var p in conf) {
if (conf.hasOwnProperty(p)) {
mapEnvVarProperties(conf,p,flow);
}
}
try {
Object.defineProperty(conf,'_flow', {value: flow, enumerable: false, writable: true })
newNode = new nodeTypeConstructor(conf);
} catch (err) {
Log.log({
level: Log.ERROR,
id:conf.id,
type: type,
msg: err
});
}
} else {
Log.error(Log._("nodes.flow.unknown-type", {type:type}));
}
} catch(err) {
Log.error(err);
}
return newNode;
}
createNode: createNode
}

@@ -17,4 +17,2 @@ /*!

var when = require('when');
var externalAPI = require("./api");

@@ -26,6 +24,5 @@

var library = require("./library");
var events = require("./events");
var hooks = require("./hooks");
var plugins = require("./plugins");
var settings = require("./settings");
var exec = require("./exec");

@@ -37,5 +34,3 @@ var express = require("express");

var redUtil = require("@node-red/util");
var log = redUtil.log;
var i18n = redUtil.i18n;
const {log,i18n,events,exec,util} = require("@node-red/util");

@@ -73,3 +68,3 @@ var runtimeMetricInterval = null;

*/
function init(userSettings,httpServer,_adminApi,__util) {
function init(userSettings,httpServer,_adminApi) {
server = httpServer;

@@ -86,12 +81,3 @@ userSettings.version = getVersion();

redNodes.init(runtime);
library.init(runtime);
externalAPI.init(runtime);
exec.init(runtime);
if (__util) {
log = __util.log;
i18n = __util.i18n;
} else {
log = redUtil.log;
i18n = redUtil.i18n;
}
}

@@ -125,2 +111,3 @@

.then(function() { return settings.load(storage)})
.then(function() { return library.init(runtime)})
.then(function() {

@@ -146,3 +133,11 @@

return redNodes.load().then(function() {
let autoInstallModules = false;
if (settings.hasOwnProperty('autoInstallModules')) {
log.warn(log._("server.deprecatedOption",{old:"autoInstallModules", new:"externalModules.autoInstall"}));
autoInstallModules = true;
}
if (settings.externalModules) {
// autoInstallModules = autoInstall enabled && (no palette setting || palette install not disabled)
autoInstallModules = settings.externalModules.autoInstall && (!settings.externalModules.palette || settings.externalModules.palette.allowInstall !== false) ;
}
var i;

@@ -180,3 +175,3 @@ var nodeErrors = redNodes.getNodeList(function(n) { return n.err!=null;});

log.warn(" - "+i+" ("+missingModules[i].version+"): "+missingModules[i].types.join(", "));
if (settings.autoInstallModules && i != "node-red") {
if (autoInstallModules && i != "node-red") {
installingModules.push({id:i,version:missingModules[i].version});

@@ -186,3 +181,3 @@ }

}
if (!settings.autoInstallModules) {
if (!autoInstallModules) {
log.info(log._("server.removing-modules"));

@@ -209,25 +204,31 @@ redNodes.cleanModuleList();

var reinstallAttempts;
var reinstallAttempts = 0;
var reinstallTimeout;
function reinstallModules(moduleList) {
var promises = [];
var failedModules = [];
const promises = [];
const reinstallList = [];
const installRetry = 30000;
if (settings.hasOwnProperty('autoInstallModulesRetry')) {
log.warn(log._("server.deprecatedOption",{old:"autoInstallModulesRetry", new:"externalModules.autoInstallRetry"}));
installRetry = settings.autoInstallModulesRetry;
}
if (settings.externalModules && settings.externalModules.hasOwnProperty('autoInstallRetry')) {
installRetry = settings.externalModules.autoInstallRetry * 1000;
}
for (var i=0;i<moduleList.length;i++) {
if (settings.autoInstallModules && i != "node-red") {
promises.push(redNodes.installModule(moduleList[i].id,moduleList[i].version));
if (moduleList[i].id != "node-red") {
(function(mod) {
promises.push(redNodes.installModule(mod.id,mod.version).then(m => {
events.emit("runtime-event",{id:"node/added",retain:false,payload:m.nodes});
}).catch(err => {
reinstallList.push(mod);
}));
})(moduleList[i])
}
}
when.settle(promises).then(function(results) {
var reinstallList = [];
for (var i=0;i<results.length;i++) {
if (results[i].state === 'rejected') {
reinstallList.push(moduleList[i]);
} else {
events.emit("runtime-event",{id:"node/added",retain:false,payload:results[i].value.nodes});
}
}
Promise.all(promises).then(function(results) {
if (reinstallList.length > 0) {
reinstallAttempts++;
// First 5 at 1x timeout, next 5 at 2x, next 5 at 4x, then 8x
var timeout = (settings.autoInstallModulesRetry||30000) * Math.pow(2,Math.min(Math.floor(reinstallAttempts/5),3));
var timeout = installRetry * Math.pow(2,Math.min(Math.floor(reinstallAttempts/5),3));
reinstallTimeout = setTimeout(function() {

@@ -262,2 +263,6 @@ reinstallModules(reinstallList);

* Stops the runtime.
*
* Once called, Node-RED should not be restarted until the Node.JS process is
* restarted.
*
* @return {Promise} - resolves when the runtime is stopped.

@@ -280,16 +285,18 @@ * @memberof @node-red/runtime

// This is the internal api
var runtime = {
version: getVersion,
get log() { return log },
get i18n() { return i18n },
log: log,
i18n: i18n,
events: events,
settings: settings,
storage: storage,
events: events,
hooks: hooks,
nodes: redNodes,
plugins: plugins,
flows: flows,
library: library,
exec: exec,
util: require("@node-red/util").util,
util: util,
get adminApi() { return adminApi },

@@ -353,2 +360,8 @@ get adminApp() { return adminApp },

/**
* @memberof @node-red/runtime
* @mixes @node-red/runtime_plugins
*/
plugins: externalAPI.plugins,
/**
* Returns whether the runtime is started

@@ -355,0 +368,0 @@ * @param {Object} opts

@@ -98,5 +98,9 @@ /**

module.exports = {
name: '_examples_',
id: "examples",
label: "editor:library.types.examples",
icon: "font-awesome/fa-life-ring",
types: ["flows"],
readOnly: true,
init: init,
getEntry: getEntry
}

@@ -18,19 +18,77 @@ /**

var knownTypes = {};
const {events,log} = require("@node-red/util")
const knownTypes = {};
const libraries = {};
const libraryConfigs = {};
const libraryPlugins = {};
var libraries = {};
// Libraries defined in the settings file. Their configurations
// cannot be modified in the editor.
let runtimeLibraries = [];
// Libraries defined by the user in the editor.
let userLibraries = [];
function init(runtime) {
knownTypes = {
'flows': 'node-red'
};
let runtime;
libraries["_examples_"] = require("./examples");
libraries["_examples_"].init(runtime);
function init(_runtime) {
runtime = _runtime;
events.removeListener("registry:plugin-added",onPluginAdded);
events.on("registry:plugin-added",onPluginAdded);
knownTypes.flows = 'node-red';
libraries["local"] = require("./local");
libraries["local"].init(runtime);
libraryConfigs["local"] = libraries["local"]
libraries["examples"] = require("./examples");
libraries["examples"].init(runtime);
libraryConfigs["examples"] = libraries["examples"]
try {
runtimeLibraries = runtime.settings.editorTheme.library.sources;
} catch(err) {
runtimeLibraries = [];
}
// userLibraries = runtime.settings.get("library")
}
function onPluginAdded(id) {
const plugin = runtime.plugins.getPlugin(id);
if (plugin.type === "node-red-library-source") {
libraryPlugins[plugin.id] = plugin;
runtimeLibraries.forEach(library => {
if (library.type === id) {
library.local = false;
if (!/^[a-z0-9-_]+$/.test(library.id)) {
log.warn(log._("library.failedToInit",{error:log._("library.invalidProperty",{prop:"id",value:library.id})}));
return;
}
try {
libraries[library.id] = new plugin.class(library)
libraryConfigs[library.id] = library;
libraryConfigs[library.id].type = id;
if (libraries[library.id].init) {
libraries[library.id].init().catch(err => {
delete libraries[library.id];
delete libraryConfigs[library.id];
log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()}));
});
}
} catch(err) {
log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()}));
}
}
})
}
}
function registerType(id,type) {

@@ -47,3 +105,3 @@ // TODO: would like to enforce this, but currently the tests register the same type multiple

if (!knownTypes.hasOwnProperty(type)) {
throw new Error(`Unknown library type '${type}'`);
throw new Error(log._("library.unknownType",{type: type}))
}

@@ -53,3 +111,3 @@ if (libraries.hasOwnProperty(library)) {

} else {
throw new Error(`Unknown library '${library}'`);
throw new Error(log._("library.unknownLibrary",{library: library}))
}

@@ -59,17 +117,68 @@ }

if (!knownTypes.hasOwnProperty(type)) {
throw new Error(`Unknown library type '${type}'`);
throw new Error(log._("library.unknownType",{type: type}))
}
if (libraries.hasOwnProperty(library)) {
if (libraries[library].hasOwnProperty("saveEntry")) {
if (libraries[library].saveEntry) {
return libraries[library].saveEntry(type,path,meta,body);
} else {
throw new Error(`Library '${library}' is read-only`);
throw new Error(log._("library.readOnly",{library: library}))
}
} else {
throw new Error(`Unknown library '${library}'`);
throw new Error(log._("library.unknownLibrary",{library: library}))
}
}
function getLibraries() {
const libraryList = []
for (let id in libraries) {
if (libraries.hasOwnProperty(id)) {
var config = getConfig(id);
// Don't include the full configuration of each library when providing
// the list of all libraries
delete config.config;
libraryList.push(config);
}
}
return libraryList;
}
function getConfig(id) {
var lib = {
id: id,
label: libraryConfigs[id].label || id,
user: false,
icon: libraryConfigs[id].icon
}
if (libraryConfigs[id].types) {
lib.types = libraryConfigs[id].types
}
if (libraryConfigs[id].readOnly) {
lib.readOnly = libraryConfigs[id].readOnly
}
if (libraryConfigs[id].type) {
lib.type = libraryConfigs[id].type;
var def = libraryPlugins[lib.type];
if (def && def.defaults) {
lib.config = {};
for (var d in def.defaults) {
if (def.defaults.hasOwnProperty(d)) {
if (def.defaults[d].type !== 'password') {
lib.config[d] = libraryConfigs[id][d];
} else if (!!libraryConfigs[id][d]) {
lib.config["has_"+d] = true;
}
}
}
}
}
return lib;
}
module.exports = {
init: init,
getLibraries: getLibraries,
// getConfig: getConfig,
register: registerType,

@@ -76,0 +185,0 @@ getEntry: getEntry,

@@ -33,3 +33,5 @@ /**

module.exports = {
name: 'local',
id: "local",
label: "editor:library.types.local",
icon: "font-awesome/fa-hdd-o",
init: init,

@@ -36,0 +38,0 @@ getEntry: getEntry,

@@ -17,3 +17,2 @@ /**

var when = require("when");
var crypto = require('crypto');

@@ -64,3 +63,3 @@ var runtime;

*/
load: function (credentials) {
load: async function (credentials) {
dirty = false;

@@ -82,3 +81,3 @@ var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;

var setupEncryptionPromise = when.resolve();
var setupEncryptionPromise = Promise.resolve();

@@ -140,3 +139,3 @@ var projectKey = false;

error.code = "credentials_load_failed";
return when.reject(error);
throw error;
}

@@ -170,3 +169,3 @@ }

error.code = "credentials_load_failed";
return when.reject(error);
throw error;
}

@@ -225,5 +224,5 @@ }

activeProject.credentialSecretInvalid = true;
return when.reject(error);
throw error;
}
return when.reject(error);
throw error;
}

@@ -244,5 +243,5 @@ // These are encrypted credentials

activeProject.credentialSecretInvalid = true;
return when.reject(error);
throw error;
}
return when.reject(error);
throw error;
}

@@ -267,3 +266,3 @@ } else {

*/
add: function (id, creds) {
add: async function (id, creds) {
if (!credentialCache.hasOwnProperty(id) || JSON.stringify(creds) !== JSON.stringify(credentialCache[id])) {

@@ -273,3 +272,2 @@ credentialCache[id] = creds;

}
return when.resolve();
},

@@ -305,3 +303,3 @@

*/
clean: function (config) {
clean: async function (config) {
var existingIds = {};

@@ -326,3 +324,2 @@ config.forEach(function(n) {

}
return when.resolve();
},

@@ -446,3 +443,3 @@

},
export: function() {
export: async function() {
var result = credentialCache;

@@ -470,5 +467,5 @@

} else {
return when.resolve(result);
return result;
}
}
}

@@ -17,3 +17,2 @@ /**

var when = require("when");
var path = require("path");

@@ -33,3 +32,3 @@ var fs = require("fs");

var events = require("../events");
const events = require("@node-red/util").events;

@@ -87,3 +86,3 @@ var settings;

}
registry.registerType(nodeSet,type,constructor);
registry.registerType(nodeSet,type,constructor,opts);
}

@@ -119,2 +118,21 @@

function registerSubflow(nodeSet, subflow) {
// TODO: extract credentials definition from subflow properties
var registeredType = registry.registerSubflow(nodeSet,subflow);
if (subflow.env) {
var creds = {};
var hasCreds = false;
subflow.env.forEach(e => {
if (e.type === "cred") {
creds[e.name] = {type: "password"};
hasCreds = true;
}
})
if (hasCreds) {
credentials.register(registeredType.type,creds);
}
}
}
function init(runtime) {

@@ -170,7 +188,8 @@ settings = runtime.settings;

var info = registry.getModuleInfo(module);
if (!info) {
if (!info || !info.user) {
throw new Error(log._("nodes.index.unrecognised-module", {module:module}));
} else {
for (var i=0;i<info.nodes.length;i++) {
flows.checkTypeInUse(module+"/"+info.nodes[i].name);
var nodeTypesToCheck = info.nodes.map(n => `${module}/${n.name}`);
for (var i=0;i<nodeTypesToCheck.length;i++) {
flows.checkTypeInUse(nodeTypesToCheck[i]);
}

@@ -196,3 +215,3 @@ return registry.uninstallModule(module).then(function(list) {

paletteEditorEnabled: registry.paletteEditorEnabled,
installerEnabled: registry.installerEnabled,
installModule: installModule,

@@ -206,2 +225,3 @@ uninstallModule: uninstallModule,

registerType: registerType,
registerSubflow: registerSubflow,
getType: registry.get,

@@ -251,3 +271,3 @@

closeContextsPlugin: context.close,
listContextStores: context.listStores
listContextStores: context.listStores,
};

@@ -489,5 +489,3 @@ /**

}
if (self._flow) {
o.path = self._flow.path;
}
if (self.z) {

@@ -499,3 +497,3 @@ o.z = self.z;

}
Log.log(o);
self._flow.log(o);
}

@@ -502,0 +500,0 @@ /**

@@ -17,3 +17,2 @@ /**

var when = require("when");
var clone = require("clone");

@@ -94,6 +93,6 @@ var assert = require("assert");

assert.deepEqual(current,value);
return when.resolve();
} catch(err) {
return storage.saveSettings(clone(globalSettings));
}
return Promise.resolve();
},

@@ -111,3 +110,3 @@ delete: function(prop) {

}
return when.resolve();
return Promise.resolve();
},

@@ -186,3 +185,3 @@

assert.deepEqual(current,settings);
return when.resolve();
return Promise.resolve();
} catch(err) {

@@ -189,0 +188,0 @@ globalSettings.users = userSettings;

@@ -17,7 +17,6 @@ /**

var when = require('when');
var Path = require('path');
var crypto = require('crypto');
var log = require("@node-red/util").log; // TODO: separate module
var log = require("@node-red/util").log;

@@ -54,11 +53,9 @@ var runtime;

var storageModuleInterface = {
init: function(_runtime) {
init: async function(_runtime) {
runtime = _runtime;
try {
storageModule = moduleSelector(runtime.settings);
settingsAvailable = storageModule.hasOwnProperty("getSettings") && storageModule.hasOwnProperty("saveSettings");
sessionsAvailable = storageModule.hasOwnProperty("getSessions") && storageModule.hasOwnProperty("saveSessions");
} catch (e) {
return when.reject(e);
}
// Any errors thrown by the module will get passed up to the called
// as a rejected promise
storageModule = moduleSelector(runtime.settings);
settingsAvailable = storageModule.hasOwnProperty("getSettings") && storageModule.hasOwnProperty("saveSettings");
sessionsAvailable = storageModule.hasOwnProperty("getSessions") && storageModule.hasOwnProperty("saveSessions");
if (!!storageModule.projects) {

@@ -78,3 +75,3 @@ var projectsEnabled = false;

},
getFlows: function() {
getFlows: async function() {
return storageModule.getFlows().then(function(flows) {

@@ -91,3 +88,3 @@ return storageModule.getCredentials().then(function(creds) {

},
saveFlows: function(config, user) {
saveFlows: async function(config, user) {
var flows = config.flows;

@@ -99,3 +96,3 @@ var credentials = config.credentials;

} else {
credentialSavePromise = when.resolve();
credentialSavePromise = Promise.resolve();
}

@@ -113,31 +110,27 @@ delete config.credentialsDirty;

// },
saveCredentials: function(credentials) {
saveCredentials: async function(credentials) {
return storageModule.saveCredentials(credentials);
},
getSettings: function() {
getSettings: async function() {
if (settingsAvailable) {
return storageModule.getSettings();
} else {
return when.resolve(null);
return null
}
},
saveSettings: function(settings) {
saveSettings: async function(settings) {
if (settingsAvailable) {
return settingsSaveMutex.runExclusive(() => storageModule.saveSettings(settings))
} else {
return when.resolve();
}
},
getSessions: function() {
getSessions: async function() {
if (sessionsAvailable) {
return storageModule.getSessions();
} else {
return when.resolve(null);
return null
}
},
saveSessions: function(sessions) {
saveSessions: async function(sessions) {
if (sessionsAvailable) {
return storageModule.saveSessions(sessions);
} else {
return when.resolve();
}

@@ -148,15 +141,15 @@ },

getLibraryEntry: function(type, path) {
getLibraryEntry: async function(type, path) {
if (is_malicious(path)) {
var err = new Error();
err.code = "forbidden";
return when.reject(err);
throw err;
}
return storageModule.getLibraryEntry(type, path);
},
saveLibraryEntry: function(type, path, meta, body) {
saveLibraryEntry: async function(type, path, meta, body) {
if (is_malicious(path)) {
var err = new Error();
err.code = "forbidden";
return when.reject(err);
throw err;
}

@@ -167,3 +160,3 @@ return storageModule.saveLibraryEntry(type, path, meta, body);

/* Deprecated functions */
getAllFlows: function() {
getAllFlows: async function() {
if (storageModule.hasOwnProperty("getAllFlows")) {

@@ -173,3 +166,3 @@ return storageModule.getAllFlows();

if (libraryFlowsCachedResult) {
return Promise.resolve(libraryFlowsCachedResult);
return libraryFlowsCachedResult;
} else {

@@ -187,3 +180,3 @@ return listFlows("/").then(function(result) {

err.code = "forbidden";
return when.reject(err);
throw err;
}

@@ -201,3 +194,3 @@ if (storageModule.hasOwnProperty("getFlow")) {

err.code = "forbidden";
return when.reject(err);
throw err;
}

@@ -218,32 +211,30 @@ libraryFlowsCachedResult = null;

return storageModule.getLibraryEntry("flows",path).then(function(res) {
return when.promise(function(resolve) {
var promises = [];
res.forEach(function(r) {
if (typeof r === "string") {
promises.push(listFlows(Path.join(path,r)));
const promises = [];
res.forEach(function(r) {
if (typeof r === "string") {
promises.push(listFlows(Path.join(path,r)));
} else {
promises.push(Promise.resolve(r));
}
});
return Promise.all(promises).then(res2 => {
let i = 0;
const result = {};
res2.forEach(function(r) {
// TODO: name||fn
if (r.fn) {
var name = r.name;
if (!name) {
name = r.fn.replace(/\.json$/, "");
}
result.f = result.f || [];
result.f.push(name);
} else {
promises.push(when.resolve(r));
result.d = result.d || {};
result.d[res[i]] = r;
//console.log(">",r.value);
}
i++;
});
var i=0;
when.settle(promises).then(function(res2) {
var result = {};
res2.forEach(function(r) {
// TODO: name||fn
if (r.value.fn) {
var name = r.value.name;
if (!name) {
name = r.value.fn.replace(/\.json$/, "");
}
result.f = result.f || [];
result.f.push(name);
} else {
result.d = result.d || {};
result.d[res[i]] = r.value;
//console.log(">",r.value);
}
i++;
});
resolve(result);
});
return result;
});

@@ -253,4 +244,2 @@ });

module.exports = storageModuleInterface;

@@ -18,5 +18,3 @@ /**

var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");
var nodeFn = require('when/node/function');

@@ -94,3 +92,3 @@ var util = require("./util");

// don't create the folder if it does not exist - we are only reading....
return nodeFn.call(fs.lstat, rootPath).then(function(stats) {
return fs.lstat(rootPath).then(function(stats) {
if (stats.isFile()) {

@@ -102,3 +100,3 @@ return getFileBody(root,path);

}
return nodeFn.call(fs.readdir, rootPath).then(function(fns) {
return fs.readdir(rootPath).then(function(fns) {
var dirs = [];

@@ -149,3 +147,3 @@ var files = [];

module.exports = {
init: function(_settings) {
init: async function(_settings) {
settings = _settings;

@@ -156,4 +154,2 @@ libDir = fspath.join(settings.userDir,"lib");

return fs.ensureDir(libFlowsDir);
} else {
return when.resolve();
}

@@ -163,5 +159,5 @@ },

saveLibraryEntry: function(type,path,meta,body) {
saveLibraryEntry: async function(type,path,meta,body) {
if (settings.readOnly) {
return when.resolve();
return;
}

@@ -168,0 +164,0 @@ if (type === "flows" && !path.endsWith(".json")) {

@@ -17,4 +17,2 @@ /**

var exec = require("../../../../exec");
var authResponseServer = require('./authServer').ResponseServer;

@@ -27,3 +25,3 @@ var sshResponseServer = require('./authServer').ResponseSSHServer;

var gitVersion;
var log = require("@node-red/util").log;
const {log,exec} = require("@node-red/util");

@@ -30,0 +28,0 @@ function runGitCommand(args,cwd,env,emit) {

@@ -18,5 +18,3 @@ /**

var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");
var nodeFn = require('when/node/function');
var crypto = require('crypto');

@@ -34,2 +32,3 @@

var log = require("@node-red/util").log;
const events = require("@node-red/util").events;

@@ -40,3 +39,3 @@ var projectsEnabled = false;

var projectsDir;
var activeProject
var activeProject;

@@ -113,6 +112,16 @@ var globalGitUser = false;

} else {
// Ensure there's a default workflow mode set
settings.editorTheme.projects.workflow = {
mode: (settings.editorTheme.projects.workflow || {}).mode || "manual"
}
globalGitUser = gitConfig.user;
Projects.init(settings,runtime);
sshTools.init(settings);
projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
if(settings.editorTheme.projects.path) {
projectsDir = settings.editorTheme.projects.path;
}
if (!settings.readOnly) {

@@ -237,3 +246,2 @@ return fs.ensureDir(projectsDir)

checkActiveProject(name);
//return when.resolve(activeProject.info);
return Promise.resolve(activeProject.export());

@@ -377,7 +385,7 @@ }

return runtime.nodes.loadFlows(true).then(function() {
runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
}).catch(function(err) {
// We're committed to the project change now, so notify editors
// that it has changed.
runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
throw err;

@@ -519,6 +527,11 @@ });

function getFlows() {
async function getFlows() {
if (!initialFlowLoadComplete) {
initialFlowLoadComplete = true;
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
if (projectsEnabled) {
log.info(log._("storage.localfilesystem.projects.projects-directory", {projectsDirectory: projectsDir}));
}
if (activeProject) {

@@ -547,3 +560,3 @@ // At this point activeProject will be a string, so go load it and

error.code = "project_empty";
return when.reject(error);
throw error;
}

@@ -554,3 +567,3 @@ if (activeProject.missingFiles && activeProject.missingFiles.indexOf('package.json') !== -1) {

error.code = "missing_package_file";
return when.reject(error);
throw error;
}

@@ -561,3 +574,3 @@ if (!activeProject.getFlowFile()) {

error.code = "missing_flow_file";
return when.reject(error);
throw error;
}

@@ -568,3 +581,3 @@ if (activeProject.isMerging()) {

error.code = "git_merge_conflict";
return when.reject(error);
throw error;
}

@@ -583,5 +596,5 @@

function saveFlows(flows, user) {
async function saveFlows(flows, user) {
if (settings.readOnly) {
return when.resolve();
return
}

@@ -591,3 +604,3 @@ if (activeProject && activeProject.isMerging()) {

error.code = "git_merge_conflict";
return when.reject(error);
throw error;
}

@@ -607,7 +620,9 @@

var gitSettings = getUserGitSettings(user) || {};
var workflowMode = (gitSettings.workflow||{}).mode || "manual";
if (activeProject && workflowMode === 'auto') {
return activeProject.stageFile([flowsFullPath, credentialsFile]).then(() => {
return activeProject.commit(user,{message:"Update flow files"})
})
if (activeProject) {
var workflowMode = (gitSettings.workflow||{}).mode || settings.editorTheme.projects.workflow.mode;
if (workflowMode === 'auto') {
return activeProject.stageFile([flowsFullPath, credentialsFile]).then(() => {
return activeProject.commit(user,{message:"Update flow files"})
})
}
}

@@ -621,5 +636,5 @@ });

function saveCredentials(credentials) {
async function saveCredentials(credentials) {
if (settings.readOnly) {
return when.resolve();
return;
}

@@ -626,0 +641,0 @@

@@ -19,3 +19,2 @@ /**

var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");

@@ -31,2 +30,3 @@ var os = require('os');

var log = require("@node-red/util").log;
const events = require("@node-red/util").events;

@@ -113,3 +113,3 @@ var projectsDir;

}
}));
}).catch(err => {})); //
if (missingFiles.indexOf('README.md') === -1) {

@@ -119,3 +119,3 @@ project.paths['README.md'] = fspath.join(project.paths.root,"README.md");

project.description = content;
}));
}).catch(err => {}));
} else {

@@ -140,5 +140,5 @@ project.description = "";

promises.push(project.loadRemotes());
promises.push(project.loadRemotes().catch(err => {}));
return when.settle(promises).then(function(results) {
return Promise.all(promises).then(function(results) {
return project;

@@ -247,3 +247,3 @@ })

Project.prototype.update = function (user, data) {
Project.prototype.update = async function (user, data) {
var username;

@@ -288,3 +288,3 @@ if (!user) {

e.code = "missing_current_credential_key";
return when.reject(e);
throw e;
}

@@ -302,3 +302,3 @@ this.credentialSecret = secret;

// Cannot update a project that doesn't have a known package.json
return Promise.reject("Cannot update project with missing package.json");
throw new Error("Cannot update project with missing package.json");
}

@@ -313,3 +313,3 @@ }

if (!/package\.json$/.test(data.files.package)) {
return Promise.reject("Invalid package file: "+data.files.package)
return new Error("Invalid package file: "+data.files.package)
}

@@ -412,3 +412,3 @@ var root = data.files.package.substring(0,data.files.package.length-12);

});
promises.push(modifyRemotesPromise);
promises.push(modifyRemotesPromise.catch(err => {}));
}

@@ -420,3 +420,3 @@ }

if (saveSettings) {
promises.push(settings.set("projects",globalProjectSettings));
promises.push(settings.set("projects",globalProjectSettings).catch(err => {}));
}

@@ -427,3 +427,3 @@

if (saveREADME) {
promises.push(util.writeFile(fspath.join(this.path,this.paths['README.md']), this.description));
promises.push(util.writeFile(fspath.join(this.path,this.paths['README.md']), this.description).catch(err => {}));
modifiedFiles.push('README.md');

@@ -440,8 +440,8 @@ }

return util.writeFile(fspath.join(project.path,this.paths['package.json']), JSON.stringify(this.package,"",4));
}));
}).catch(err => {}));
modifiedFiles.push('package.json');
}
return when.settle(promises).then(function(res) {
return Promise.all(promises).then(function(res) {
var gitSettings = getUserGitSettings(user) || {};
var workflowMode = (gitSettings.workflow||{}).mode || "manual";
var workflowMode = (gitSettings.workflow||{}).mode || settings.editorTheme.projects.workflow.mode;
if (workflowMode === 'auto') {

@@ -562,3 +562,3 @@ return project.stageFile(modifiedFiles.map(f => project.paths[f])).then(() => {

self.merging = true;
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",

@@ -587,3 +587,3 @@ payload:{

if (!self.empty) {
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",

@@ -602,5 +602,5 @@ payload:{

if (self.paths.flowFile) {
runtime.events.emit("runtime-event",{id:"runtime-state",retain:true});
events.emit("runtime-event",{id:"runtime-state",retain:true});
} else {
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",

@@ -981,28 +981,13 @@ payload:{

var promises = [];
var paths = [];
var missing = [];
for (var file in defaultFileSet) {
if (defaultFileSet.hasOwnProperty(file)) {
paths.push(file);
promises.push(fs.stat(fspath.join(project.path,project.paths.root,file)));
(function(f) {
promises.push(fs.stat(fspath.join(project.path,project.paths.root,f)).catch(err => {
missing.push(f);
}));
})(file);
}
}
return when.settle(promises).then(function(results) {
var missing = [];
results.forEach(function(result,i) {
if (result.state === 'rejected') {
missing.push(paths[i]);
}
});
return missing;
}).then(function(missing) {
// if (createMissing) {
// var promises = [];
// missing.forEach(function(file) {
// promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
// });
// return promises;
// } else {
return missing;
// }
});
return Promise.all(promises).then(() => missing);
}

@@ -1101,2 +1086,5 @@ function createProject(user, metadata) {

projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
if(settings.editorTheme.projects.path) {
projectsDir = settings.editorTheme.projects.path;
}
authCache.init();

@@ -1103,0 +1091,0 @@ }

@@ -18,3 +18,2 @@ /**

var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");

@@ -21,0 +20,0 @@ var keygen = require("./keygen");

@@ -17,3 +17,2 @@ /**

var when = require('when');
var fs = require('fs-extra');

@@ -34,4 +33,4 @@ var fspath = require("path");

},
getSessions: function() {
return when.promise(function(resolve,reject) {
getSessions: async function() {
return new Promise(function(resolve,reject) {
fs.readFile(sessionsFile,'utf8',function(err,data){

@@ -49,5 +48,5 @@ if (!err) {

},
saveSessions: function(sessions) {
saveSessions: async function(sessions) {
if (settings.readOnly) {
return when.resolve();
return;
}

@@ -54,0 +53,0 @@ return util.writeFile(sessionsFile,JSON.stringify(sessions));

@@ -45,2 +45,3 @@ {

},
"deprecatedOption": "Use of __old__ is deprecated. Use __new__ instead",
"unable-to-listen": "Unable to listen on __listenpath__",

@@ -92,3 +93,9 @@ "port-in-use": "Error: port in use",

},
"library": {
"unknownLibrary": "Unknown library: __library__",
"unknownType": "Unknown library type: __type__",
"readOnly": "Library __library__ is read-only",
"failedToInit": "Failed to initialise library __library__: __error__",
"invalidProperty": "Invalid property __prop__: '__value__'"
},
"nodes": {

@@ -160,2 +167,3 @@ "credentials": {

"active-project": "Active project : __project__",
"projects-directory": "Projects directory: __projectsDirectory__",
"project-not-found": "Project not found : __project__",

@@ -162,0 +170,0 @@ "no-active-project": "No active project : using default flows file",

@@ -44,2 +44,3 @@ {

},
"deprecatedOption": "__old__ の利用は非推奨です。代わりに __new__ を使用してください",
"unable-to-listen": "__listenpath__ に対してlistenできません",

@@ -153,2 +154,3 @@ "port-in-use": "エラー: ポートが使用中です",

"active-project": "選択中のプロジェクト : __project__",
"projects-directory": "プロジェクトディレクトリ: __projectsDirectory__",
"project-not-found": "プロジェクトが見つかりません : __project__",

@@ -155,0 +157,0 @@ "no-active-project": "プロジェクトが選択されていません : デフォルトのフローファイルを使用します",

{
"name": "@node-red/runtime",
"version": "1.2.9",
"version": "1.3.0-beta.1",
"license": "Apache-2.0",

@@ -19,4 +19,4 @@ "main": "./lib/index.js",

"dependencies": {
"@node-red/registry": "1.2.9",
"@node-red/util": "1.2.9",
"@node-red/registry": "1.3.0-beta.1",
"@node-red/util": "1.3.0-beta.1",
"async-mutex": "0.2.6",

@@ -26,5 +26,4 @@ "clone": "2.1.2",

"fs-extra": "8.1.0",
"json-stringify-safe": "5.0.1",
"when": "3.7.8"
"json-stringify-safe": "5.0.1"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc