mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
implement flows runtime stop/start API and UI
This commit is contained in:
@@ -73,6 +73,10 @@ 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) {
|
||||
@@ -255,5 +259,83 @@ var api = module.exports = {
|
||||
}
|
||||
}
|
||||
return sendCredentials;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Gets running state of runtime flows
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {{state:string, started:boolean}} - the current run state of the flows
|
||||
* @memberof @node-red/runtime_flows
|
||||
*/
|
||||
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
|
||||
}
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Sets running state of runtime flows
|
||||
* @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".
|
||||
* @return {Promise<Flow>} - the active flow configuration
|
||||
* @memberof @node-red/runtime_flows
|
||||
*/
|
||||
setState: async function(opts) {
|
||||
opts = opts || {};
|
||||
const makeError = (error, errcode, statusCode) => {
|
||||
const message = typeof error == "object" ? error.message : error
|
||||
const err = typeof error == "object" ? error : new Error(message||"Unexpected Error")
|
||||
err.status = err.status || statusCode || 400;
|
||||
err.code = err.code || errcode || "unexpected_error"
|
||||
runtime.log.audit({
|
||||
event: "flows.setState",
|
||||
state: opts.requestedState || "",
|
||||
error: errcode || "unexpected_error",
|
||||
message: err.code
|
||||
}, opts.req);
|
||||
return err
|
||||
}
|
||||
|
||||
const getState = () => {
|
||||
return {
|
||||
state: runtime.flows.started ? "started" : "stopped",
|
||||
started: !!runtime.flows.started,
|
||||
rev: runtime.flows.getFlows().rev,
|
||||
}
|
||||
}
|
||||
|
||||
if(runtime.settings.runtimeState ? runtime.settings.runtimeState.enabled === false : false) {
|
||||
throw (makeError("Method Not Allowed", "not_allowed", 405))
|
||||
}
|
||||
switch (opts.requestedState) {
|
||||
case "start":
|
||||
try {
|
||||
try {
|
||||
runtime.settings.set('flowsRunStateRequested', opts.requestedState);
|
||||
} catch(err) { }
|
||||
await runtime.flows.startFlows("full")
|
||||
return getState()
|
||||
} catch (err) {
|
||||
throw (makeError(err, err.code, 500))
|
||||
}
|
||||
case "stop":
|
||||
try {
|
||||
try {
|
||||
runtime.settings.set('flowsRunStateRequested', opts.requestedState);
|
||||
} catch(err) { }
|
||||
await runtime.flows.stopFlows("full")
|
||||
return getState()
|
||||
} catch (err) {
|
||||
throw (makeError(err, err.code, 500))
|
||||
}
|
||||
default:
|
||||
throw (makeError("Cannot set runtime state. Invalid state", "invalid_run_state", 400))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@@ -148,6 +148,18 @@ var api = module.exports = {
|
||||
enabled: (runtime.settings.diagnostics && runtime.settings.diagnostics.enabled === false) ? false : true,
|
||||
ui: (runtime.settings.diagnostics && runtime.settings.diagnostics.ui === false) ? false : true
|
||||
}
|
||||
if(safeSettings.diagnostics.enabled === false) {
|
||||
safeSettings.diagnostics.ui = false; // cannot have UI without endpoint
|
||||
}
|
||||
|
||||
safeSettings.runtimeState = {
|
||||
//unless runtimeState.ui and runtimeState.enabled are explicitly false, they will default to true.
|
||||
enabled: (runtime.settings.runtimeState && runtime.settings.runtimeState.enabled === false) ? false : true,
|
||||
ui: (runtime.settings.runtimeState && runtime.settings.runtimeState.ui === false) ? false : true
|
||||
}
|
||||
if(safeSettings.runtimeState.enabled === false) {
|
||||
safeSettings.runtimeState.ui = false; // cannot have UI without endpoint
|
||||
}
|
||||
|
||||
runtime.settings.exportNodeSettings(safeSettings);
|
||||
runtime.plugins.exportPluginSettings(safeSettings);
|
||||
|
@@ -261,6 +261,7 @@ function getFlows() {
|
||||
|
||||
async function start(type,diff,muteLog) {
|
||||
type = type||"full";
|
||||
let reallyStarted = started
|
||||
started = true;
|
||||
var i;
|
||||
// If there are missing types, report them, emit the necessary runtime event and return
|
||||
@@ -365,24 +366,42 @@ async function start(type,diff,muteLog) {
|
||||
}
|
||||
}
|
||||
// Having created or updated all flows, now start them.
|
||||
for (id in activeFlows) {
|
||||
if (activeFlows.hasOwnProperty(id)) {
|
||||
try {
|
||||
activeFlows[id].start(diff);
|
||||
let startFlows = true
|
||||
try {
|
||||
startFlows = settings.get('flowsRunStateRequested');
|
||||
} catch(err) {
|
||||
}
|
||||
startFlows = (startFlows !== "stop");
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
reallyStarted = true;
|
||||
events.emit("flows:started", {config: activeConfig, type: type, diff: diff});
|
||||
// Deprecated event
|
||||
events.emit("nodes-started");
|
||||
} else {
|
||||
started = false;
|
||||
}
|
||||
events.emit("flows:started", {config: activeConfig, type: type, diff: diff});
|
||||
// Deprecated event
|
||||
events.emit("nodes-started");
|
||||
|
||||
const state = {
|
||||
started: reallyStarted,
|
||||
state: reallyStarted ? "started" : "stopped",
|
||||
}
|
||||
events.emit("runtime-event",{id:"flows-run-state", payload: state, retain:true});
|
||||
|
||||
|
||||
if (credentialsPendingReset === true) {
|
||||
credentialsPendingReset = false;
|
||||
@@ -390,7 +409,7 @@ async function start(type,diff,muteLog) {
|
||||
events.emit("runtime-event",{id:"runtime-state",retain:true});
|
||||
}
|
||||
|
||||
if (!muteLog) {
|
||||
if (!muteLog && reallyStarted) {
|
||||
if (type !== "full") {
|
||||
log.info(log._("nodes.flows.started-modified-"+type));
|
||||
} else {
|
||||
@@ -471,6 +490,7 @@ 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});
|
||||
// Deprecated event
|
||||
events.emit("nodes-stopped");
|
||||
});
|
||||
|
Reference in New Issue
Block a user