diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/flows.js b/packages/node_modules/@node-red/editor-api/lib/admin/flows.js
index 2ad233f8f..4d8679aac 100644
--- a/packages/node_modules/@node-red/editor-api/lib/admin/flows.js
+++ b/packages/node_modules/@node-red/editor-api/lib/admin/flows.js
@@ -83,7 +83,7 @@ module.exports = {
postState: function(req,res) {
const opts = {
user: req.user,
- requestedState: req.body.state||"",
+ state: req.body.state || "",
req: apiUtils.getRequestLogObject(req)
}
runtimeAPI.flows.setState(opts).then(function(result) {
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
index b25885e21..ab2fba982 100755
--- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
@@ -169,6 +169,10 @@
}
},
"notification": {
+ "state": {
+ "flowsStopped": "Flows stopped",
+ "flowsStarted": "Flows started"
+ },
"warning": "Warning: __message__",
"warnings": {
"undeployedChanges": "node has undeployed changes",
@@ -291,12 +295,6 @@
"stopstart":{
"status": {
"state_changed": "Flows runtime has been changed to '__state__' state"
- },
- "errors": {
- "notAllowed": "Method not allowed",
- "notAuthorized": "Not authorized",
- "notFound": "Not found",
- "noResponse": "No response from server"
}
},
"deploy": {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
index 052fad558..b390dab53 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
@@ -14,7 +14,7 @@
* limitations under the License.
**/
-/**
+/**
* An Interface to nodes and utility functions for creating/adding/deleting nodes and links
* @namespace RED.nodes
*/
diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js
index 939c32f2e..55446418b 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/red.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/red.js
@@ -336,7 +336,6 @@ var RED = (function() {
id: notificationId
}
if (notificationId === "runtime-state") {
- RED.events.emit("runtime-state",msg);
if (msg.error === "safe-mode") {
options.buttons = [
{
@@ -477,9 +476,9 @@ var RED = (function() {
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
persistentNotifications[notificationId].close();
delete persistentNotifications[notificationId];
- if (notificationId === 'runtime-state') {
- RED.events.emit("runtime-state",msg);
- }
+ }
+ if (notificationId === 'runtime-state') {
+ RED.events.emit("runtime-state",msg);
}
});
RED.comms.subscribe("status/#",function(topic,msg) {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/runtime.js b/packages/node_modules/@node-red/editor-client/src/js/runtime.js
index 49960e382..eac985467 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/runtime.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/runtime.js
@@ -1,53 +1,36 @@
RED.runtime = (function() {
let state = ""
- let settings = {ui: false, enabled: false};
- const STOPPED = "stopped"
- const STARTED = "started"
+ let settings = { ui: false, enabled: false };
+ const STOPPED = "stop"
+ const STARTED = "start"
+ const SAFE = "safe"
+
return {
init: function() {
// refresh the current runtime status from server
settings = Object.assign({}, settings, RED.settings.runtimeState);
- RED.runtime.requestState()
-
- // {id:"flows-run-state", started: false, state: "stopped", retain:true}
- RED.comms.subscribe("notification/flows-run-state",function(topic,msg) {
- RED.events.emit("flows-run-state",msg);
- RED.runtime.updateState(msg.state);
- });
- },
- get state() {
- return state
- },
- get started() {
- return state === STARTED
- },
- get states() {
- return { STOPPED, STARTED }
- },
- updateState: function(newState) {
- state = newState;
- // disable pointer events on node buttons (e.g. inject/debug nodes)
- $(".red-ui-flow-node-button").toggleClass("red-ui-flow-node-button-stopped", state === STOPPED)
- // show/hide Start/Stop based on current state
- if(settings.enabled === true && settings.ui === true) {
- RED.menu.setVisible("deploymenu-item-runtime-stop", state === STARTED)
- RED.menu.setVisible("deploymenu-item-runtime-start", state === STOPPED)
- }
- },
- requestState: function(callback) {
- $.ajax({
- headers: {
- "Accept":"application/json"
- },
- cache: false,
- url: 'flows/state',
- success: function(data) {
- RED.runtime.updateState(data.state)
- if(callback) {
- callback(data.state)
+ RED.events.on("runtime-state", function(msg) {
+ if (msg.state) {
+ const currentState = state
+ state = msg.state
+ $(".red-ui-flow-node-button").toggleClass("red-ui-flow-node-button-stopped", state !== STARTED)
+ if(settings.enabled === true && settings.ui === true) {
+ RED.menu.setVisible("deploymenu-item-runtime-stop", state === STARTED)
+ RED.menu.setVisible("deploymenu-item-runtime-start", state !== STARTED)
+ }
+ // Do not notify the user about this event if:
+ // - This is the very first event we've received after loading the editor (currentState = '')
+ // - The state matches what we already thought was the case (state === currentState)
+ // - The event was triggered by a deploy (msg.deploy === true)
+ // - The event is a safe mode event - that gets notified separately
+ if (currentState !== '' && state !== currentState && !msg.deploy && state !== SAFE) {
+ RED.notify(RED._("notification.state.flows"+(state === STOPPED?'Stopped':'Started'), msg), "success")
}
}
});
+ },
+ get started() {
+ return state === STARTED
}
}
-})()
\ No newline at end of file
+})()
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
index 46408cb6c..809202c99 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
@@ -69,7 +69,7 @@ RED.deploy = (function() {
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.svg",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}},
null
]
- if(RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
+ if (RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
mainMenuItems.push({id:"deploymenu-item-runtime-start", icon:"red/images/start.svg",label:"Start"/*RED._("deploy.startFlows")*/,sublabel:"Start Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:start-flows", visible:false})
mainMenuItems.push({id:"deploymenu-item-runtime-stop", icon:"red/images/stop.svg",label:"Stop"/*RED._("deploy.startFlows")*/,sublabel:"Stop Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:stop-flows", visible:false})
}
@@ -302,7 +302,6 @@ RED.deploy = (function() {
deployInflight = true
deployButtonSetBusy()
shadeShow()
- RED.runtime.updateState(state)
$.ajax({
url:"flows/state",
type: "POST",
@@ -311,30 +310,23 @@ RED.deploy = (function() {
if (deployWasEnabled) {
$("#red-ui-header-button-deploy").removeClass("disabled")
}
- RED.runtime.updateState((data && data.state) || "unknown")
- RED.notify(RED._("stopstart.status.state_changed", data), "success")
}).fail(function(xhr,textStatus,err) {
if (deployWasEnabled) {
$("#red-ui-header-button-deploy").removeClass("disabled")
}
if (xhr.status === 401) {
- RED.notify(RED._("notification.error", { message: RED._("stopstart.errors.notAuthorized") }), "error")
- } else if (xhr.status === 404) {
- RED.notify(RED._("notification.error", { message: RED._("stopstart.errors.notFound") }), "error")
- } else if (xhr.status === 405) {
- RED.notify(RED._("notification.error", { message: RED._("stopstart.errors.notAllowed") }), "error")
+ RED.notify(RED._("notification.error", { message: RED._("user.notAuthorized") }), "error")
} else if (xhr.responseText) {
const errorDetail = { message: err ? (err + "") : "" }
try {
errorDetail.message = JSON.parse(xhr.responseText).message
- } finally {
+ } finally {
errorDetail.message = errorDetail.message || xhr.responseText
}
RED.notify(RED._("notification.error", errorDetail), "error")
} else {
- RED.notify(RED._("notification.error", { message: RED._("stopstart.errors.noResponse") }), "error")
+ RED.notify(RED._("notification.error", { message: RED._("deploy.errors.noResponse") }), "error")
}
- RED.runtime.requestState()
}).always(function() {
const delta = Math.max(0, 300 - (Date.now() - startTime))
setTimeout(function () {
@@ -514,13 +506,10 @@ RED.deploy = (function() {
deployButtonSetBusy();
const data = { flows: nns };
- data.runtimeState = RED.runtime.state;
- if (data.runtimeState === RED.runtime.states.STOPPED || force) {
- data._rev = RED.nodes.version();
- } else {
+ if (!force) {
data.rev = RED.nodes.version();
}
-
+
deployInflight = true;
shadeShow();
$.ajax({
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
index eca5c01ef..02b8df5d8 100755
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
@@ -4877,7 +4877,7 @@ RED.view = (function() {
if (d._def.button) {
var buttonEnabled = isButtonEnabled(d);
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
- if(RED.runtime && Object.hasOwn(RED.runtime,'started')) {
+ if (RED.runtime && Object.hasOwn(RED.runtime,'started')) {
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
}
diff --git a/packages/node_modules/@node-red/runtime/lib/api/flows.js b/packages/node_modules/@node-red/runtime/lib/api/flows.js
index 2e71cbdca..b91635201 100644
--- a/packages/node_modules/@node-red/runtime/lib/api/flows.js
+++ b/packages/node_modules/@node-red/runtime/lib/api/flows.js
@@ -73,10 +73,6 @@ var api = module.exports = {
if (deploymentType === 'reload') {
apiPromise = runtime.flows.loadFlows(true);
} else {
- //ensure the runtime running/stopped state matches the deploying editor. If not, then copy the _rev number to flows.rev
- if(flows.hasOwnProperty('_rev') && !flows.hasOwnProperty('rev') && (flows.runtimeState !== "stopped" || runtime.flows.started)) {
- flows.rev = flows._rev
- }
if (flows.hasOwnProperty('rev')) {
var currentVersion = runtime.flows.getFlows().rev;
if (currentVersion !== flows.rev) {
@@ -271,9 +267,7 @@ var api = module.exports = {
getState: async function(opts) {
runtime.log.audit({event: "flows.getState"}, opts.req);
const result = {
- state: runtime.flows.started ? "started" : "stopped",
- started: !!runtime.flows.started,
- rev: runtime.flows.getFlows().rev
+ state: runtime.flows.state()
}
return result;
},
@@ -282,7 +276,7 @@ var api = module.exports = {
* @param {Object} opts
* @param {Object} opts.req - the request to log (optional)
* @param {User} opts.user - the user calling the api
- * @param {string} opts.requestedState - the requested state. Valid values are "start" and "stop".
+ * @param {string} opts.state - the requested state. Valid values are "start" and "stop".
* @return {Promise} - the active flow configuration
* @memberof @node-red/runtime_flows
*/
@@ -295,7 +289,7 @@ var api = module.exports = {
err.code = err.code || errcode || "unexpected_error"
runtime.log.audit({
event: "flows.setState",
- state: opts.requestedState || "",
+ state: opts.state || "",
error: errcode || "unexpected_error",
message: err.code
}, opts.req);
@@ -304,21 +298,22 @@ var api = module.exports = {
const getState = () => {
return {
- state: runtime.flows.started ? "started" : "stopped",
- started: !!runtime.flows.started,
- rev: runtime.flows.getFlows().rev,
+ state: runtime.flows.state()
}
}
if(!runtime.settings.runtimeState || runtime.settings.runtimeState.enabled !== true) {
throw (makeError("Method Not Allowed", "not_allowed", 405))
}
- switch (opts.requestedState) {
+ switch (opts.state) {
case "start":
try {
try {
- runtime.settings.set('flowsRunStateRequested', opts.requestedState);
- } catch(err) { }
+ runtime.settings.set('runtimeFlowState', opts.state);
+ } catch(err) {}
+ if (runtime.settings.safeMode) {
+ delete runtime.settings.safeMode
+ }
await runtime.flows.startFlows("full")
return getState()
} catch (err) {
@@ -327,15 +322,15 @@ var api = module.exports = {
case "stop":
try {
try {
- runtime.settings.set('flowsRunStateRequested', opts.requestedState);
- } catch(err) { }
+ runtime.settings.set('runtimeFlowState', opts.state);
+ } catch(err) {}
await runtime.flows.stopFlows("full")
return getState()
} catch (err) {
throw (makeError(err, err.code, 500))
}
default:
- throw (makeError(`Cannot change flows runtime state to '${opts.requestedState}'}`, "invalid_run_state", 400))
+ throw (makeError(`Cannot change flows runtime state to '${opts.state}'}`, "invalid_run_state", 400))
}
},
}
diff --git a/packages/node_modules/@node-red/runtime/lib/flows/index.js b/packages/node_modules/@node-red/runtime/lib/flows/index.js
index b707b4aaf..b2de8ea73 100644
--- a/packages/node_modules/@node-red/runtime/lib/flows/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/flows/index.js
@@ -36,6 +36,8 @@ var activeFlowConfig = null;
var activeFlows = {};
var started = false;
+var state = 'stop'
+
var credentialsPendingReset = false;
var activeNodesToFlow = {};
@@ -50,6 +52,7 @@ function init(runtime) {
storage = runtime.storage;
log = runtime.log;
started = false;
+ state = 'stop';
if (!typeEventRegistered) {
events.on('type-registered',function(type) {
if (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
@@ -214,19 +217,26 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
// Flows are running (or should be)
// Stop the active flows (according to deploy type and the diff)
- return stop(type,diff,muteLog).then(() => {
+ return stop(type,diff,muteLog,true).then(() => {
// Once stopped, allow context to remove anything no longer needed
return context.clean(activeFlowConfig)
}).then(() => {
+ if (!isLoad) {
+ log.info(log._("nodes.flows.updated-flows"));
+ }
// Start the active flows
- start(type,diff,muteLog).then(() => {
+ start(type,diff,muteLog,true).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 {
+ if (!isLoad) {
+ log.info(log._("nodes.flows.updated-flows"));
+ }
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
+ return flowRevision;
}
});
}
@@ -259,10 +269,10 @@ function getFlows() {
return activeConfig;
}
-async function start(type,diff,muteLog) {
- type = type||"full";
- let reallyStarted = started
+async function start(type,diff,muteLog,isDeploy) {
+ type = type || "full";
started = true;
+ state = 'start'
var i;
// If there are missing types, report them, emit the necessary runtime event and return
if (activeFlowConfig.missingTypes.length > 0) {
@@ -284,7 +294,7 @@ async function start(type,diff,muteLog) {
log.info(log._("nodes.flows.missing-type-install-2"));
log.info(" "+settings.userDir);
}
- events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-types", type:"warning",text:"notification.warnings.missing-types",types:activeFlowConfig.missingTypes},retain:true});
+ events.emit("runtime-event",{id:"runtime-state",payload:{state: 'stop', error:"missing-types", type:"warning",text:"notification.warnings.missing-types",types:activeFlowConfig.missingTypes},retain:true});
return;
}
@@ -298,7 +308,7 @@ async function start(type,diff,muteLog) {
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});
+ events.emit("runtime-event",{id:"runtime-state",payload:{state: 'stop', error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});
return;
}
@@ -307,10 +317,20 @@ async function start(type,diff,muteLog) {
log.info("*****************************************************************")
log.info(log._("nodes.flows.safe-mode"));
log.info("*****************************************************************")
- events.emit("runtime-event",{id:"runtime-state",payload:{error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
+ state = 'safe'
+ events.emit("runtime-event",{id:"runtime-state",payload:{state: 'safe', error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
return;
}
+ const runtimeState = settings.get('runtimeFlowState') || 'start'
+ if (runtimeState === 'stop') {
+ log.info(log._("nodes.flows.stopped-flows"));
+ events.emit("runtime-event",{id:"runtime-state",payload:{ state: 'stop', deploy:isDeploy },retain:true});
+ state = 'stop'
+ started = false
+ return
+ }
+
if (!muteLog) {
if (type !== "full") {
log.info(log._("nodes.flows.starting-modified-"+type));
@@ -365,51 +385,31 @@ async function start(type,diff,muteLog) {
}
}
}
- // Having created or updated all flows, now start them.
- let startFlows = true
- try {
- startFlows = settings.get('flowsRunStateRequested');
- } catch(err) {
- }
- startFlows = (startFlows !== "stop");
-
- if (startFlows) {
- for (id in activeFlows) {
- if (activeFlows.hasOwnProperty(id)) {
- try {
- activeFlows[id].start(diff);
- // Create a map of node id to flow id and also a subflowInstance lookup map
- var activeNodes = activeFlows[id].getActiveNodes();
- Object.keys(activeNodes).forEach(function(nid) {
- activeNodesToFlow[nid] = id;
- });
- } catch(err) {
- console.log(err.stack);
- }
+ for (id in activeFlows) {
+ if (activeFlows.hasOwnProperty(id)) {
+ try {
+ activeFlows[id].start(diff);
+ // Create a map of node id to flow id and also a subflowInstance lookup map
+ var activeNodes = activeFlows[id].getActiveNodes();
+ Object.keys(activeNodes).forEach(function(nid) {
+ activeNodesToFlow[nid] = id;
+ });
+ } catch(err) {
+ console.log(err.stack);
}
}
- reallyStarted = true;
- events.emit("flows:started", {config: activeConfig, type: type, diff: diff});
- // Deprecated event
- events.emit("nodes-started");
- } else {
- started = false;
}
-
- const state = {
- started: reallyStarted,
- state: reallyStarted ? "started" : "stopped",
- }
- events.emit("runtime-event",{id:"flows-run-state", payload: state, retain:true});
-
+ events.emit("flows:started", {config: activeConfig, type: type, diff: diff});
+ // Deprecated event
+ events.emit("nodes-started");
if (credentialsPendingReset === true) {
credentialsPendingReset = false;
} else {
- events.emit("runtime-event",{id:"runtime-state",retain:true});
+ events.emit("runtime-event",{id:"runtime-state", payload:{ state: 'start', deploy:isDeploy}, retain:true});
}
- if (!muteLog && reallyStarted) {
+ if (!muteLog) {
if (type !== "full") {
log.info(log._("nodes.flows.started-modified-"+type));
} else {
@@ -419,7 +419,7 @@ async function start(type,diff,muteLog) {
return;
}
-function stop(type,diff,muteLog) {
+function stop(type,diff,muteLog,isDeploy) {
if (!started) {
return Promise.resolve();
}
@@ -439,6 +439,7 @@ function stop(type,diff,muteLog) {
}
}
started = false;
+ state = 'stop'
var promises = [];
var stopList;
var removedList = diff.removed;
@@ -490,7 +491,8 @@ function stop(type,diff,muteLog) {
}
}
events.emit("flows:stopped",{config: activeConfig, type: type, diff: diff});
- events.emit("runtime-event",{id:"flows-run-state", payload: {started: false, state: "stopped"}, retain:true});
+
+ events.emit("runtime-event",{ id:"runtime-state", payload:{ state: 'stop', deploy:isDeploy }, retain:true });
// Deprecated event
events.emit("nodes-stopped");
});
@@ -810,7 +812,7 @@ module.exports = {
stopFlows: stop,
get started() { return started },
-
+ state: () => { return state },
// handleError: handleError,
// handleStatus: handleStatus,
diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js
index 8e1d2b487..88b3b8293 100644
--- a/packages/node_modules/@node-red/runtime/lib/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/index.js
@@ -215,7 +215,7 @@ function start() {
}
}
return redNodes.loadContextsPlugin().then(function () {
- redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
+ redNodes.loadFlows().then(() => { redNodes.startFlows() }).catch(function(err) {});
started = true;
});
});
@@ -399,12 +399,12 @@ module.exports = {
* @memberof @node-red/runtime
*/
version: externalAPI.version,
-
+
/**
* @memberof @node-red/diagnostics
*/
diagnostics:externalAPI.diagnostics,
-
+
storage: storage,
events: events,
hooks: hooks,
diff --git a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
index 3b46d0c9f..ecd010abb 100644
--- a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
+++ b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
@@ -122,6 +122,7 @@
"stopped-flows": "Stopped flows",
"stopped": "Stopped",
"stopping-error": "Error stopping node: __message__",
+ "updated-flows": "Updated flows",
"added-flow": "Adding flow: __label__",
"updated-flow": "Updated flow: __label__",
"removed-flow": "Removed flow: __label__",