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.1.3 to 1.2.0-beta.1

lib/flows/Flow.js

195

lib/api/flows.js

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

var runtime;
var Mutex = require('async-mutex').Mutex;
const mutex = new Mutex();

@@ -51,7 +53,5 @@ var api = module.exports = {

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

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

*/
setFlows: function(opts) {
return new Promise(function(resolve,reject) {
setFlows: async function(opts) {
return mutex.runExclusive(async function() {
var flows = opts.flows;

@@ -78,6 +77,6 @@ var deploymentType = opts.deploymentType||"full";

if (deploymentType === 'reload') {
apiPromise = runtime.nodes.loadFlows(true);
apiPromise = runtime.flows.loadFlows(true);
} else {
if (flows.hasOwnProperty('rev')) {
var currentVersion = runtime.nodes.getFlows().rev;
var currentVersion = runtime.flows.getFlows().rev;
if (currentVersion !== flows.rev) {

@@ -89,13 +88,13 @@ var err;

//TODO: log warning
return reject(err);
throw err;
}
}
apiPromise = runtime.nodes.setFlows(flows.flows,flows.credentials,deploymentType);
apiPromise = runtime.flows.setFlows(flows.flows,flows.credentials,deploymentType,null,null,opts.user);
}
apiPromise.then(function(flowId) {
return resolve({rev:flowId});
return apiPromise.then(function(flowId) {
return {rev:flowId};
}).catch(function(err) {
runtime.log.warn(runtime.log._("api.flows.error-"+(deploymentType === 'reload'?'reload':'save'),{message:err.message}));
runtime.log.warn(err.stack);
return reject(err);
throw err
});

@@ -114,16 +113,18 @@ });

*/
addFlow: function(opts) {
return new Promise(function(resolve,reject) {
addFlow: async function(opts) {
return mutex.runExclusive(async function() {
var flow = opts.flow;
runtime.nodes.addFlow(flow).then(function(id) {
runtime.log.audit({event: "flow.add",id:id}, opts.req);
return resolve(id);
}).catch(function(err) {
runtime.log.audit({event: "flow.add",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
return runtime.flows.addFlow(flow, opts.user).then(function (id) {
runtime.log.audit({event: "flow.add", id: id}, opts.req);
return id;
}).catch(function (err) {
runtime.log.audit({
event: "flow.add",
error: err.code || "unexpected_error",
message: err.toString()
}, opts.req);
err.status = 400;
return reject(err);
throw err;
})
})
});
},

@@ -140,17 +141,14 @@

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

@@ -167,30 +165,26 @@ /**

*/
updateFlow: function(opts) {
return new Promise(function (resolve,reject) {
updateFlow: async function(opts) {
return mutex.runExclusive(async function() {
var flow = opts.flow;
var id = opts.id;
try {
runtime.nodes.updateFlow(id,flow).then(function() {
runtime.log.audit({event: "flow.update",id:id}, opts.req);
return resolve(id);
}).catch(function(err) {
runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
return reject(err);
})
} catch(err) {
return runtime.flows.updateFlow(id, flow, opts.user).then(function () {
runtime.log.audit({event: "flow.update", id: id}, opts.req);
return id;
}).catch(function (err) {
if (err.code === 404) {
runtime.log.audit({event: "flow.update",id:id,error:"not_found"}, opts.req);
runtime.log.audit({event: "flow.update", id: id, error: "not_found"}, opts.req);
// TODO: this swap around of .code and .status isn't ideal
err.status = 404;
err.code = "not_found";
return reject(err);
} else {
runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
runtime.log.audit({
event: "flow.update",
error: err.code || "unexpected_error",
message: err.toString()
}, opts.req);
err.status = 400;
return reject(err);
}
}
throw err;
})
});
},

@@ -206,27 +200,25 @@ /**

*/
deleteFlow: function(opts) {
return new Promise(function (resolve,reject) {
deleteFlow: async function(opts) {
return mutex.runExclusive(function() {
var id = opts.id;
try {
runtime.nodes.removeFlow(id).then(function() {
runtime.log.audit({event: "flow.remove",id:id}, opts.req);
return resolve();
}).catch(function(err) {
runtime.log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
err.status = 400;
return reject(err);
});
} catch(err) {
return runtime.flows.removeFlow(id, opts.user).then(function () {
runtime.log.audit({event: "flow.remove", id: id}, opts.req);
return;
}).catch(function (err) {
if (err.code === 404) {
runtime.log.audit({event: "flow.remove",id:id,error:"not_found"}, opts.req);
runtime.log.audit({event: "flow.remove", id: id, error: "not_found"}, opts.req);
// TODO: this swap around of .code and .status isn't ideal
err.status = 404;
err.code = "not_found";
return reject(err);
} else {
runtime.log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
runtime.log.audit({
event: "flow.remove",
id: id,
error: err.code || "unexpected_error",
message: err.toString()
}, opts.req);
err.status = 400;
return reject(err);
}
}
throw err;
});
});

@@ -245,34 +237,31 @@ },

*/
getNodeCredentials: function(opts) {
return new Promise(function(resolve,reject) {
runtime.log.audit({event: "credentials.get",type:opts.type,id:opts.id}, opts.req);
var credentials = runtime.nodes.getCredentials(opts.id);
if (!credentials) {
return resolve({});
getNodeCredentials: async function(opts) {
runtime.log.audit({event: "credentials.get",type:opts.type,id:opts.id}, opts.req);
var credentials = runtime.nodes.getCredentials(opts.id);
if (!credentials) {
return {};
}
var sendCredentials = {};
var cred;
if (/^subflow(:|$)/.test(opts.type)) {
for (cred in credentials) {
if (credentials.hasOwnProperty(cred)) {
sendCredentials['has_'+cred] = credentials[cred] != null && credentials[cred] !== '';
}
}
var sendCredentials = {};
var cred;
if (/^subflow(:|$)/.test(opts.type)) {
for (cred in credentials) {
if (credentials.hasOwnProperty(cred)) {
sendCredentials['has_'+cred] = credentials[cred] != null && credentials[cred] !== '';
} else {
var definition = runtime.nodes.getCredentialDefinition(opts.type) || {};
for (cred in definition) {
if (definition.hasOwnProperty(cred)) {
if (definition[cred].type == "password") {
var key = 'has_' + cred;
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
continue;
}
sendCredentials[cred] = credentials[cred] || '';
}
} else {
var definition = runtime.nodes.getCredentialDefinition(opts.type) || {};
for (cred in definition) {
if (definition.hasOwnProperty(cred)) {
if (definition[cred].type == "password") {
var key = 'has_' + cred;
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
continue;
}
sendCredentials[cred] = credentials[cred] || '';
}
}
}
resolve(sendCredentials);
})
}
return sendCredentials;
}
}

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

* @param {String} opts.version - (optional) the version of the module to install
* @param {Object} opts.tarball - (optional) a tarball file to install. Object has properties `name`, `size` and `buffer`.
* @param {String} opts.url - (optional) url to install

@@ -177,2 +178,33 @@ * @param {Object} opts.req - the request to log (optional)

}
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";
err.status = 400;
return reject(err);
}
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;
return reject(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) {

@@ -179,0 +211,0 @@ var existingModule = runtime.nodes.getModuleInfo(opts.module);

@@ -17,6 +17,31 @@ /*!

var events = require("events");
const events = new (require("events")).EventEmitter();
module.exports = new events.EventEmitter();
const deprecatedEvents = {
"nodes-stopped": "flows:stopped",
"nodes-started": "flows:started"
}
function wrapEventFunction(obj,func) {
events["_"+func] = events[func];
return function(eventName, listener) {
if (deprecatedEvents.hasOwnProperty(eventName)) {
const log = require("@node-red/util").log;
const stack = (new Error().stack).split("\n")[2].split("(")[1].slice(0,-1);
log.warn(`[RED.events] Deprecated use of "${eventName}" event from "${stack}". Use "${deprecatedEvents[eventName]}" instead.`)
}
return events["_"+func].call(events,eventName,listener)
}
}
events.on = wrapEventFunction(events,"on");
events.once = wrapEventFunction(events,"once");
events.addListener = events.on;
module.exports = events;
/**

@@ -23,0 +48,0 @@ * Runtime events emitter

@@ -22,5 +22,7 @@ /*!

var redNodes = require("./nodes");
var flows = require("./flows");
var storage = require("./storage");
var library = require("./library");
var events = require("./events");
var hooks = require("./hooks");
var settings = require("./settings");

@@ -276,3 +278,5 @@ var exec = require("./exec");

events: events,
hooks: hooks,
nodes: redNodes,
flows: flows,
library: library,

@@ -360,2 +364,3 @@ exec: exec,

events: events,
hooks: hooks,
util: require("@node-red/util").util,

@@ -362,0 +367,0 @@ get httpNode() { return nodeApp },

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

var memory = require("./memory");
var flows;

@@ -52,3 +51,2 @@ var settings;

function init(_settings) {
flows = require("../flows");
settings = _settings;

@@ -518,35 +516,2 @@ contexts = {};

//
// function getContext(localId,flowId,parent) {
// var contextId = localId;
// if (flowId) {
// contextId = localId+":"+flowId;
// }
// console.log("getContext",localId,flowId,"known?",contexts.hasOwnProperty(contextId));
// if (contexts.hasOwnProperty(contextId)) {
// return contexts[contextId];
// }
// var newContext = createContext(contextId,undefined,parent);
// if (flowId) {
// var node = flows.get(flowId);
// console.log("flows,get",flowId,node&&node.type)
// var parent = undefined;
// if (node && node.type.startsWith("subflow:")) {
// parent = node.context().flow;
// }
// else {
// parent = createRootContext();
// }
// var flowContext = getContext(flowId,undefined,parent);
// Object.defineProperty(newContext, 'flow', {
// value: flowContext
// });
// }
// Object.defineProperty(newContext, 'global', {
// value: contexts['global']
// })
// contexts[contextId] = newContext;
// return newContext;
// }
function deleteContext(id,flowId) {

@@ -553,0 +518,0 @@ if(!hasConfiguredStore){

@@ -26,4 +26,4 @@ /**

var credentials = require("./credentials");
var flows = require("./flows");
var flowUtil = require("./flows/util")
var flows = require("../flows");
var flowUtil = require("../flows/util")
var context = require("./context");

@@ -155,7 +155,5 @@ var Node = require("./Node");

function installModule(module,version,url) {
var existingModule = registry.getModuleInfo(module);
var isUpgrade = !!existingModule;
return registry.installModule(module,version,url).then(function(info) {
if (isUpgrade) {
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:module,version:version}});
if (info.pending_version) {
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:info.name,version:info.pending_version}});
} else {

@@ -162,0 +160,0 @@ events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});

@@ -23,4 +23,6 @@ /**

var context = require("./context");
var flows = require("./flows");
var flows = require("../flows");
const hooks = require("../hooks");
const NOOP_SEND = function() {}

@@ -59,7 +61,3 @@

Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true })
this._asyncDelivery = n._flow.asyncMessageDelivery;
}
if (this._asyncDelivery === undefined) {
this._asyncDelivery = true;
}
this.updateWires(n.wires);

@@ -129,2 +127,8 @@ }

Node.prototype._complete = function(msg,error) {
this.metric("done",msg);
hooks.trigger("onComplete",{ msg: msg, error: error, node: { id: this.id, node: this }}, err => {
if (err) {
this.error(err);
}
})
if (error) {

@@ -178,11 +182,3 @@ // For now, delegate this to this.error

if (event === "input") {
// When Pluggable Message Routing arrives, this will be called from
// that and will already be sync/async depending on the router.
if (this._asyncDelivery) {
setImmediate(function() {
node._emitInput.apply(node,args);
});
} else {
this._emitInput.apply(this,args);
}
this._emitInput.apply(this,args);
} else {

@@ -201,37 +197,49 @@ this._emit.apply(this,arguments);

this.metric("receive", arg);
if (node._inputCallback) {
// Just one callback registered.
try {
node._inputCallback(
arg,
function() { node.send.apply(node,arguments) },
function(err) { node._complete(arg,err); }
);
} catch(err) {
node.error(err,arg);
}
} else if (node._inputCallbacks) {
// Multiple callbacks registered. Call each one, tracking eventual completion
var c = node._inputCallbacks.length;
for (var i=0;i<c;i++) {
var cb = node._inputCallbacks[i];
if (cb.length === 2) {
c++;
}
try {
node._inputCallbacks[i](
arg,
function() { node.send.apply(node,arguments) },
function(err) {
c--;
if (c === 0) {
node._complete(arg,err);
}
let receiveEvent = { msg:arg, destination: { id: this.id, node: this } }
// onReceive - a node is about to receive a message
hooks.trigger("onReceive",receiveEvent,(err) => {
if (err) {
node.error(err);
return
} else if (err !== false) {
if (node._inputCallback) {
// Just one callback registered.
try {
node._inputCallback(
arg,
function() { node.send.apply(node,arguments) },
function(err) { node._complete(arg,err); }
);
} catch(err) {
node.error(err,arg);
}
} else if (node._inputCallbacks) {
// Multiple callbacks registered. Call each one, tracking eventual completion
var c = node._inputCallbacks.length;
for (var i=0;i<c;i++) {
var cb = node._inputCallbacks[i];
if (cb.length === 2) {
c++;
}
);
} catch(err) {
node.error(err,arg);
try {
cb.call(
node,
arg,
function() { node.send.apply(node,arguments) },
function(err) {
c--;
if (c === 0) {
node._complete(arg,err);
}
}
);
} catch(err) {
node.error(err,arg);
}
}
}
// postReceive - the message has been passed to the node's input handler
hooks.trigger("postReceive",receiveEvent,(err) => {if (err) { node.error(err) }});
}
}
});
}

@@ -372,7 +380,15 @@

this.metric("send",msg);
node = this._flow.getNode(this._wire);
/* istanbul ignore else */
if (node) {
node.receive(msg);
}
this._flow.send([{
msg: msg,
source: {
id: this.id,
node: this,
port: 0
},
destination: {
id: this._wire,
node: undefined
},
cloneMessage: false
}]);
return;

@@ -391,3 +407,3 @@ } else {

var sentMessageId = null;
var hasMissingIds = false;
// for each output of node eg. [msgs to output 0, msgs to output 1, ...]

@@ -406,20 +422,27 @@ for (var i = 0; i < numOutputs; i++) {

for (var j = 0; j < wires.length; j++) {
node = this._flow.getNode(wires[j]); // node at end of wire j
if (node) {
// for each msg to send eg. [[m1, m2, ...], ...]
for (k = 0; k < msgs.length; k++) {
var m = msgs[k];
if (m !== null && m !== undefined) {
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
if (msgSent) {
var clonedmsg = redUtil.cloneMessage(m);
sendEvents.push({n:node,m:clonedmsg});
} else {
sendEvents.push({n:node,m:m});
msgSent = true;
}
// for each msg to send eg. [[m1, m2, ...], ...]
for (k = 0; k < msgs.length; k++) {
var m = msgs[k];
if (m !== null && m !== undefined) {
if (!m._msgid) {
hasMissingIds = true;
}
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
sendEvents.push({
msg: m,
source: {
id: this.id,
node: this,
port: i
},
destination: {
id: wires[j],
node: undefined
},
cloneMessage: msgSent
});
msgSent = true;
}

@@ -437,10 +460,12 @@ }

for (i=0;i<sendEvents.length;i++) {
var ev = sendEvents[i];
/* istanbul ignore else */
if (!ev.m._msgid) {
ev.m._msgid = sentMessageId;
if (hasMissingIds) {
for (i=0;i<sendEvents.length;i++) {
var ev = sendEvents[i];
/* istanbul ignore else */
if (!ev.msg._msgid) {
ev.msg._msgid = sentMessageId;
}
}
ev.n.receive(ev.m);
}
this._flow.send(sendEvents);
};

@@ -452,3 +477,2 @@

* This will emit the `input` event with the provided message.
* As of 1.0, this will return *before* any 'input' callback handler is invoked.
*/

@@ -455,0 +479,0 @@ Node.prototype.receive = function(msg) {

@@ -95,3 +95,3 @@ /**

} catch(err) {
return storage.saveSettings(globalSettings);
return storage.saveSettings(clone(globalSettings));
}

@@ -108,3 +108,3 @@ },

delete globalSettings[prop];
return storage.saveSettings(globalSettings);
return storage.saveSettings(clone(globalSettings));
}

@@ -188,3 +188,3 @@ return when.resolve();

globalSettings.users = userSettings;
return storage.saveSettings(globalSettings);
return storage.saveSettings(clone(globalSettings));
}

@@ -191,0 +191,0 @@ }

@@ -85,3 +85,3 @@ /**

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

@@ -98,3 +98,3 @@ var credentials = config.credentials;

return credentialSavePromise.then(function() {
return storageModule.saveFlows(flows).then(function() {
return storageModule.saveFlows(flows, user).then(function() {
return crypto.createHash('md5').update(JSON.stringify(config.flows)).digest("hex");

@@ -101,0 +101,0 @@ })

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

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

@@ -33,2 +32,7 @@

function checkForConfigFile(dir) {
return fs.existsSync(fspath.join(dir,".config.json")) ||
fs.existsSync(fspath.join(dir,".config.nodes.json"))
}
var localfilesystem = {

@@ -41,25 +45,15 @@ init: function(_settings, runtime) {

if (!settings.userDir) {
try {
fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json"));
settings.userDir = process.env.NODE_RED_HOME;
} catch(err) {
try {
// Consider compatibility for older versions
if (process.env.HOMEPATH) {
fs.statSync(fspath.join(process.env.HOMEPATH,".node-red",".config.json"));
settings.userDir = fspath.join(process.env.HOMEPATH,".node-red");
}
} catch(err) {
}
if (!settings.userDir) {
settings.userDir = fspath.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red");
if (!settings.readOnly) {
promises.push(fs.ensureDir(fspath.join(settings.userDir,"node_modules")));
}
}
if (checkForConfigFile(process.env.NODE_RED_HOME)) {
settings.userDir = process.env.NODE_RED_HOME
} else if (process.env.HOMEPATH && checkForConfigFile(fspath.join(process.env.HOMEPATH,".node-red"))) {
settings.userDir = fspath.join(process.env.HOMEPATH,".node-red");
} else {
settings.userDir = fspath.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red");
}
}
if (!settings.readOnly) {
promises.push(fs.ensureDir(fspath.join(settings.userDir,"node_modules")));
}
sessions.init(settings);
runtimeSettings.init(settings);
promises.push(runtimeSettings.init(settings));
promises.push(library.init(settings));

@@ -69,3 +63,3 @@ promises.push(projects.init(settings, runtime));

var packageFile = fspath.join(settings.userDir,"package.json");
var packagePromise = when.resolve();
var packagePromise = Promise.resolve();

@@ -88,3 +82,3 @@ if (!settings.readOnly) {

}
return when.all(promises).then(packagePromise);
return Promise.all(promises).then(packagePromise);
},

@@ -91,0 +85,0 @@

@@ -21,5 +21,6 @@ /**

var os = require("os");
const crypto = require("crypto");
function getListenPath() {
var seed = (0x100000+Math.random()*0x999999).toString(16);
var seed = crypto.randomBytes(8).toString('hex');
var fn = 'node-red-git-askpass-'+seed+'-sock';

@@ -26,0 +27,0 @@ var listenPath;

@@ -193,3 +193,9 @@ /**

function getUserGitSettings(user) {
var userSettings = settings.getUserSettings(user)||{};
var username;
if (!user) {
username = "_";
} else {
username = user.username;
}
var userSettings = settings.getUserSettings(username)||{};
return userSettings.git;

@@ -366,3 +372,2 @@ }

function createProject(user, metadata) {
// var userSettings = getUserGitSettings(user);
if (metadata.files && metadata.migrateFiles) {

@@ -554,3 +559,3 @@ // We expect there to be no active project in this scenario

function saveFlows(flows) {
function saveFlows(flows, user) {
if (settings.readOnly) {

@@ -569,3 +574,4 @@ return when.resolve();

if (settings.flowFilePretty) {
if (settings.flowFilePretty || (activeProject && settings.flowFilePretty !== false) ) {
// Pretty format if option enabled, or using Projects and not explicitly disabled
flowData = JSON.stringify(flows,null,4);

@@ -575,3 +581,11 @@ } else {

}
return util.writeFile(flowsFullPath, flowData, flowsFileBackup);
return util.writeFile(flowsFullPath, flowData, flowsFileBackup).then(() => {
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"})
})
}
});
}

@@ -589,3 +603,4 @@

var credentialData;
if (settings.flowFilePretty) {
if (settings.flowFilePretty || (activeProject && settings.flowFilePretty !== false) ) {
// Pretty format if option enabled, or using Projects and not explicitly disabled
credentialData = JSON.stringify(credentials,null,4);

@@ -592,0 +607,0 @@ } else {

@@ -43,3 +43,4 @@ /**

}
function getGitUser(user) {
function getUserGitSettings(user) {
var username;

@@ -51,5 +52,10 @@ if (!user) {

}
var userSettings = settings.getUserSettings(username);
if (userSettings && userSettings.git) {
return userSettings.git.user;
var userSettings = settings.getUserSettings(username)||{};
return userSettings.git;
}
function getGitUser(user) {
var gitSettings = getUserGitSettings(user);
if (gitSettings) {
return gitSettings.user;
}

@@ -177,3 +183,3 @@ return null;

return when.all(promises).then(function() {
return Promise.all(promises).then(function() {
return gitTools.stageFile(project.path,files);

@@ -349,2 +355,6 @@ }).then(function() {

}
if (data.hasOwnProperty('version')) {
savePackage = true;
this.package.version = data.version;
}

@@ -397,7 +407,11 @@ if (data.hasOwnProperty('git')) {

}
var modifiedFiles = [];
if (saveREADME) {
promises.push(util.writeFile(fspath.join(this.path,this.paths['README.md']), this.description));
modifiedFiles.push('README.md');
}
if (savePackage) {
promises.push(fs.readFile(fspath.join(project.path,project.paths['package.json']),"utf8").then(content => {
promises.push(fs.readFile(fspath.join(this.path,this.paths['package.json']),"utf8").then(content => {
var currentPackage = {};

@@ -411,8 +425,18 @@ try {

}));
modifiedFiles.push('package.json');
}
return when.settle(promises).then(res => {
return when.settle(promises).then(function(res) {
var gitSettings = getUserGitSettings(user) || {};
var workflowMode = (gitSettings.workflow||{}).mode || "manual";
if (workflowMode === 'auto') {
return project.stageFile(modifiedFiles.map(f => project.paths[f])).then(() => {
return project.commit(user,{message:"Update "+modifiedFiles.join(", ")})
})
}
}).then(res => {
if (reloadProject) {
return this.load()
}
}).then(() => { return {
}).then(function() {
return {
flowFilesChanged: flowFilesChanged,

@@ -818,2 +842,3 @@ credentialSecretChanged: credentialSecretChanged

summary: this.package.description,
version: this.package.version,
description: this.description,

@@ -920,3 +945,3 @@ dependencies: this.package.dependencies||{},

return when.all(promises).then(function() {
return Promise.all(promises).then(function() {
return gitTools.stageFile(projectPath,files);

@@ -923,0 +948,0 @@ }).then(function() {

@@ -17,9 +17,12 @@ /**

var when = require('when');
var fs = require('fs-extra');
var fspath = require("path");
const fs = require('fs-extra');
const fspath = require("path");
var log = require("@node-red/util").log; // TODO: separate module
var util = require("./util");
const log = require("@node-red/util").log;
const util = require("./util");
const configSections = ['nodes','users','projects'];
const settingsCache = {};
var globalSettingsFile;

@@ -29,2 +32,81 @@ var globalSettingsBackup;

async function migrateToMultipleConfigFiles() {
const nodesFilename = getSettingsFilename("nodes");
if (fs.existsSync(nodesFilename)) {
// We have both .config.json and .config.nodes.json
// Use the more recently modified. This handles users going back to pre1.2
// and up again.
// We can remove this logic in 1.3+ and remove the old .config.json file entirely
//
const fsStatNodes = await fs.stat(nodesFilename);
const fsStatGlobal = await fs.stat(globalSettingsFile);
if (fsStatNodes.mtimeMs > fsStatGlobal.mtimeMs) {
// .config.nodes.json is newer than .config.json - no migration needed
return;
}
}
const data = await util.readFile(globalSettingsFile,globalSettingsBackup,{});
// In a later release we should remove the old settings file. But don't do
// that *yet* otherwise users won't be able to downgrade easily.
return writeSettings(data) // .then( () => fs.remove(globalSettingsFile) );
}
/**
* Takes the single settings object and splits it into separate files. This makes
* it easier to backup selected parts of the settings and also helps reduce the blast
* radius if a file is lost.
*
* The settings are written to four files:
* - .config.nodes.json - the node registry
* - .config.users.json - user specific settings (eg editor settings)
* - .config.projects.json - project settings, including the active project
* - .config.runtime.json - everything else - most notable _credentialSecret
*/
function writeSettings(data) {
const configKeys = Object.keys(data);
const writePromises = [];
configSections.forEach(key => {
const sectionData = data[key] || {};
delete data[key];
const sectionFilename = getSettingsFilename(key);
const sectionContent = JSON.stringify(sectionData,null,4);
if (sectionContent !== settingsCache[key]) {
settingsCache[key] = sectionContent;
writePromises.push(util.writeFile(sectionFilename,sectionContent,sectionFilename+".backup"))
}
})
// Having extracted nodes/users/projects, write whatever is left to the runtime config
const sectionFilename = getSettingsFilename("runtime");
const sectionContent = JSON.stringify(data,null,4);
if (sectionContent !== settingsCache["runtime"]) {
settingsCache["runtime"] = sectionContent;
writePromises.push(util.writeFile(sectionFilename,sectionContent,sectionFilename+".backup"));
}
return Promise.all(writePromises);
}
async function readSettings() {
// Read the 'runtime' settings file first
const runtimeFilename = getSettingsFilename("runtime");
const result = await util.readFile(runtimeFilename,runtimeFilename+".backup",{});
settingsCache["runtime"] = JSON.stringify(result, null ,4);
const readPromises = [];
// Read the other settings files and add them into the runtime settings
configSections.forEach(key => {
const sectionFilename = getSettingsFilename(key);
readPromises.push(util.readFile(sectionFilename,sectionFilename+".backup",{}).then(sectionData => {
settingsCache[key] = JSON.stringify(sectionData, null ,4);
if (Object.keys(sectionData).length > 0) {
result[key] = sectionData;
}
}))
});
return Promise.all(readPromises).then(() => result);
}
function getSettingsFilename(section) {
return fspath.join(settings.userDir,`.config.${section}.json`);
}
module.exports = {

@@ -35,23 +117,18 @@ init: function(_settings) {

globalSettingsBackup = fspath.join(settings.userDir,".config.json.backup");
if (fs.existsSync(globalSettingsFile) && !settings.readOnly) {
return migrateToMultipleConfigFiles();
} else {
return Promise.resolve();
}
},
getSettings: function() {
return when.promise(function(resolve,reject) {
fs.readFile(globalSettingsFile,'utf8',function(err,data) {
if (!err) {
try {
return resolve(util.parseJSON(data));
} catch(err2) {
log.trace("Corrupted config detected - resetting");
}
}
return resolve({});
})
})
return readSettings()
},
saveSettings: function(newSettings) {
if (settings.readOnly) {
return when.resolve();
return Promise.resolve();
}
return util.writeFile(globalSettingsFile,JSON.stringify(newSettings,null,1),globalSettingsBackup);
return writeSettings(newSettings);
}
}

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

var fs = require('fs-extra');
var fspath = require('path');
var when = require('when');
var nodeFn = require('when/node/function');
const fs = require('fs-extra');
const fspath = require('path');
var log = require("@node-red/util").log; // TODO: separate module
const log = require("@node-red/util").log;

@@ -32,3 +30,3 @@ function parseJSON(data) {

function readFile(path,backupPath,emptyResponse,type) {
return when.promise(function(resolve) {
return new Promise(function(resolve) {
fs.readFile(path,'utf8',function(err,data) {

@@ -80,19 +78,25 @@ if (!err) {

/**
* Write content to a file using UTF8 encoding.
* This forces a fsync before completing to ensure
* the write hits disk.
*/
writeFile: function(path,content,backupPath) {
if (backupPath) {
if (fs.existsSync(path)) {
fs.renameSync(path,backupPath);
* Write content to a file using UTF8 encoding.
* This forces a fsync before completing to ensure
* the write hits disk.
*/
writeFile: function(path,content,backupPath) {
var backupPromise;
if (backupPath && fs.existsSync(path)) {
backupPromise = fs.copy(path,backupPath);
} else {
backupPromise = Promise.resolve();
}
const dirname = fspath.dirname(path);
const tempFile = `${path}.$$$`;
return backupPromise.then(() => {
if (backupPath) {
log.trace(`utils.writeFile - copied ${path} TO ${backupPath}`)
}
}
return when.promise(function(resolve,reject) {
fs.ensureDir(fspath.dirname(path), (err)=>{
if (err) {
reject(err);
return;
}
var stream = fs.createWriteStream(path);
return fs.ensureDir(dirname)
}).then(() => {
return new Promise(function(resolve,reject) {
var stream = fs.createWriteStream(tempFile);
stream.on('open',function(fd) {

@@ -102,3 +106,3 @@ stream.write(content,'utf8',function() {

if (err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()}));
log.warn(log._("storage.localfilesystem.fsync-fail",{path: tempFile, message: err.toString()}));
}

@@ -110,7 +114,20 @@ stream.end(resolve);

stream.on('error',function(err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: tempFile, message: err.toString()}));
reject(err);
});
});
}).then(() => {
log.trace(`utils.writeFile - written content to ${tempFile}`)
return new Promise(function(resolve,reject) {
fs.rename(tempFile,path,err => {
if (err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()}));
return reject(err);
}
log.trace(`utils.writeFile - renamed ${tempFile} to ${path}`)
resolve();
})
});
});
},
},
readFile: readFile,

@@ -117,0 +134,0 @@

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

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

"dependencies": {
"@node-red/registry": "1.1.3",
"@node-red/util": "1.1.3",
"@node-red/registry": "1.2.0-beta.1",
"@node-red/util": "1.2.0-beta.1",
"async-mutex": "0.2.4",
"clone": "2.1.2",

@@ -23,0 +24,0 @@ "express": "4.17.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