mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Rework start/stop api to use runtime-event notification message
This commit is contained in:
		@@ -83,7 +83,7 @@ module.exports = {
 | 
				
			|||||||
    postState: function(req,res) {
 | 
					    postState: function(req,res) {
 | 
				
			||||||
        const opts = {
 | 
					        const opts = {
 | 
				
			||||||
            user: req.user,
 | 
					            user: req.user,
 | 
				
			||||||
            requestedState: req.body.state||"",
 | 
					            state: req.body.state || "",
 | 
				
			||||||
            req: apiUtils.getRequestLogObject(req)
 | 
					            req: apiUtils.getRequestLogObject(req)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        runtimeAPI.flows.setState(opts).then(function(result) {
 | 
					        runtimeAPI.flows.setState(opts).then(function(result) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,6 +169,10 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "notification": {
 | 
					    "notification": {
 | 
				
			||||||
 | 
					        "state": {
 | 
				
			||||||
 | 
					            "flowsStopped": "Flows stopped",
 | 
				
			||||||
 | 
					            "flowsStarted": "Flows started"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "warning": "<strong>Warning</strong>: __message__",
 | 
					        "warning": "<strong>Warning</strong>: __message__",
 | 
				
			||||||
        "warnings": {
 | 
					        "warnings": {
 | 
				
			||||||
            "undeployedChanges": "node has undeployed changes",
 | 
					            "undeployedChanges": "node has undeployed changes",
 | 
				
			||||||
@@ -291,12 +295,6 @@
 | 
				
			|||||||
    "stopstart":{
 | 
					    "stopstart":{
 | 
				
			||||||
        "status": {
 | 
					        "status": {
 | 
				
			||||||
            "state_changed": "Flows runtime has been changed to '__state__' state"
 | 
					            "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": {
 | 
					    "deploy": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
 * limitations under the License.
 | 
					 * limitations under the License.
 | 
				
			||||||
 **/
 | 
					 **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 
 | 
					/**
 | 
				
			||||||
 * An Interface to nodes and utility functions for creating/adding/deleting nodes and links
 | 
					 * An Interface to nodes and utility functions for creating/adding/deleting nodes and links
 | 
				
			||||||
 * @namespace RED.nodes
 | 
					 * @namespace RED.nodes
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -336,7 +336,6 @@ var RED = (function() {
 | 
				
			|||||||
                    id: notificationId
 | 
					                    id: notificationId
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (notificationId === "runtime-state") {
 | 
					                if (notificationId === "runtime-state") {
 | 
				
			||||||
                    RED.events.emit("runtime-state",msg);
 | 
					 | 
				
			||||||
                    if (msg.error === "safe-mode") {
 | 
					                    if (msg.error === "safe-mode") {
 | 
				
			||||||
                        options.buttons = [
 | 
					                        options.buttons = [
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
@@ -477,9 +476,9 @@ var RED = (function() {
 | 
				
			|||||||
            } else if (persistentNotifications.hasOwnProperty(notificationId)) {
 | 
					            } else if (persistentNotifications.hasOwnProperty(notificationId)) {
 | 
				
			||||||
                persistentNotifications[notificationId].close();
 | 
					                persistentNotifications[notificationId].close();
 | 
				
			||||||
                delete persistentNotifications[notificationId];
 | 
					                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) {
 | 
					        RED.comms.subscribe("status/#",function(topic,msg) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,53 +1,36 @@
 | 
				
			|||||||
RED.runtime = (function() {
 | 
					RED.runtime = (function() {
 | 
				
			||||||
    let state = ""
 | 
					    let state = ""
 | 
				
			||||||
    let settings = {ui: false, enabled: false};
 | 
					    let settings = { ui: false, enabled: false };
 | 
				
			||||||
    const STOPPED = "stopped"
 | 
					    const STOPPED = "stop"
 | 
				
			||||||
    const STARTED = "started"
 | 
					    const STARTED = "start"
 | 
				
			||||||
 | 
					    const SAFE = "safe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        init: function() {
 | 
					        init: function() {
 | 
				
			||||||
            // refresh the current runtime status from server
 | 
					            // refresh the current runtime status from server
 | 
				
			||||||
            settings = Object.assign({}, settings, RED.settings.runtimeState);
 | 
					            settings = Object.assign({}, settings, RED.settings.runtimeState);
 | 
				
			||||||
            RED.runtime.requestState()
 | 
					            RED.events.on("runtime-state", function(msg) {
 | 
				
			||||||
 | 
					                if (msg.state) {
 | 
				
			||||||
            // {id:"flows-run-state", started: false, state: "stopped", retain:true}
 | 
					                    const currentState = state
 | 
				
			||||||
            RED.comms.subscribe("notification/flows-run-state",function(topic,msg) {
 | 
					                    state = msg.state
 | 
				
			||||||
                RED.events.emit("flows-run-state",msg);
 | 
					                    $(".red-ui-flow-node-button").toggleClass("red-ui-flow-node-button-stopped", state !== STARTED)
 | 
				
			||||||
                RED.runtime.updateState(msg.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 !== STARTED)
 | 
				
			||||||
        get state() {
 | 
					                    }
 | 
				
			||||||
            return state
 | 
					                    // Do not notify the user about this event if:
 | 
				
			||||||
        },
 | 
					                    // - This is the very first event we've received after loading the editor (currentState = '')
 | 
				
			||||||
        get started() {
 | 
					                    // - The state matches what we already thought was the case (state === currentState)
 | 
				
			||||||
            return state === STARTED
 | 
					                    // - The event was triggered by a deploy (msg.deploy === true)
 | 
				
			||||||
        },
 | 
					                    // - The event is a safe mode event - that gets notified separately
 | 
				
			||||||
        get states() {
 | 
					                    if (currentState !== '' && state !== currentState && !msg.deploy && state !== SAFE) {
 | 
				
			||||||
            return { STOPPED, STARTED }
 | 
					                        RED.notify(RED._("notification.state.flows"+(state === STOPPED?'Stopped':'Started'), msg), "success")
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        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)
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        get started() {
 | 
				
			||||||
 | 
					            return state === STARTED
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
})()
 | 
					})()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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")}}},
 | 
					                    {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
 | 
					                    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-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})
 | 
					                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
 | 
					        deployInflight = true
 | 
				
			||||||
        deployButtonSetBusy()
 | 
					        deployButtonSetBusy()
 | 
				
			||||||
        shadeShow()
 | 
					        shadeShow()
 | 
				
			||||||
        RED.runtime.updateState(state)
 | 
					 | 
				
			||||||
        $.ajax({
 | 
					        $.ajax({
 | 
				
			||||||
            url:"flows/state",
 | 
					            url:"flows/state",
 | 
				
			||||||
            type: "POST",
 | 
					            type: "POST",
 | 
				
			||||||
@@ -311,30 +310,23 @@ RED.deploy = (function() {
 | 
				
			|||||||
            if (deployWasEnabled) {
 | 
					            if (deployWasEnabled) {
 | 
				
			||||||
                $("#red-ui-header-button-deploy").removeClass("disabled")
 | 
					                $("#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) {
 | 
					        }).fail(function(xhr,textStatus,err) {
 | 
				
			||||||
            if (deployWasEnabled) {
 | 
					            if (deployWasEnabled) {
 | 
				
			||||||
                $("#red-ui-header-button-deploy").removeClass("disabled")
 | 
					                $("#red-ui-header-button-deploy").removeClass("disabled")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (xhr.status === 401) {
 | 
					            if (xhr.status === 401) {
 | 
				
			||||||
                RED.notify(RED._("notification.error", { message: RED._("stopstart.errors.notAuthorized") }), "error")
 | 
					                RED.notify(RED._("notification.error", { message: RED._("user.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")
 | 
					 | 
				
			||||||
            } else if (xhr.responseText) {
 | 
					            } else if (xhr.responseText) {
 | 
				
			||||||
                const errorDetail = { message: err ? (err + "") : "" }
 | 
					                const errorDetail = { message: err ? (err + "") : "" }
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    errorDetail.message = JSON.parse(xhr.responseText).message
 | 
					                    errorDetail.message = JSON.parse(xhr.responseText).message
 | 
				
			||||||
                } finally { 
 | 
					                } finally {
 | 
				
			||||||
                    errorDetail.message = errorDetail.message || xhr.responseText
 | 
					                    errorDetail.message = errorDetail.message || xhr.responseText
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                RED.notify(RED._("notification.error", errorDetail), "error")
 | 
					                RED.notify(RED._("notification.error", errorDetail), "error")
 | 
				
			||||||
            } else {
 | 
					            } 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() {
 | 
					        }).always(function() {
 | 
				
			||||||
            const delta = Math.max(0, 300 - (Date.now() - startTime))
 | 
					            const delta = Math.max(0, 300 - (Date.now() - startTime))
 | 
				
			||||||
            setTimeout(function () {
 | 
					            setTimeout(function () {
 | 
				
			||||||
@@ -514,13 +506,10 @@ RED.deploy = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        deployButtonSetBusy();
 | 
					        deployButtonSetBusy();
 | 
				
			||||||
        const data = { flows: nns };
 | 
					        const data = { flows: nns };
 | 
				
			||||||
        data.runtimeState = RED.runtime.state;
 | 
					        if (!force) {
 | 
				
			||||||
        if (data.runtimeState === RED.runtime.states.STOPPED || force) {
 | 
					 | 
				
			||||||
            data._rev = RED.nodes.version();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            data.rev = RED.nodes.version();
 | 
					            data.rev = RED.nodes.version();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        deployInflight = true;
 | 
					        deployInflight = true;
 | 
				
			||||||
        shadeShow();
 | 
					        shadeShow();
 | 
				
			||||||
        $.ajax({
 | 
					        $.ajax({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4877,7 +4877,7 @@ RED.view = (function() {
 | 
				
			|||||||
                        if (d._def.button) {
 | 
					                        if (d._def.button) {
 | 
				
			||||||
                            var buttonEnabled = isButtonEnabled(d);
 | 
					                            var buttonEnabled = isButtonEnabled(d);
 | 
				
			||||||
                            this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
 | 
					                            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);
 | 
					                                this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,10 +73,6 @@ var api = module.exports = {
 | 
				
			|||||||
            if (deploymentType === 'reload') {
 | 
					            if (deploymentType === 'reload') {
 | 
				
			||||||
                apiPromise = runtime.flows.loadFlows(true);
 | 
					                apiPromise = runtime.flows.loadFlows(true);
 | 
				
			||||||
            } else {
 | 
					            } 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')) {
 | 
					                if (flows.hasOwnProperty('rev')) {
 | 
				
			||||||
                    var currentVersion = runtime.flows.getFlows().rev;
 | 
					                    var currentVersion = runtime.flows.getFlows().rev;
 | 
				
			||||||
                    if (currentVersion !== flows.rev) {
 | 
					                    if (currentVersion !== flows.rev) {
 | 
				
			||||||
@@ -271,9 +267,7 @@ var api = module.exports = {
 | 
				
			|||||||
    getState: async function(opts) {
 | 
					    getState: async function(opts) {
 | 
				
			||||||
        runtime.log.audit({event: "flows.getState"}, opts.req);
 | 
					        runtime.log.audit({event: "flows.getState"}, opts.req);
 | 
				
			||||||
        const result = {
 | 
					        const result = {
 | 
				
			||||||
            state: runtime.flows.started ? "started" : "stopped",
 | 
					            state: runtime.flows.state()
 | 
				
			||||||
            started: !!runtime.flows.started,
 | 
					 | 
				
			||||||
            rev: runtime.flows.getFlows().rev
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return result;
 | 
					        return result;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -282,7 +276,7 @@ var api = module.exports = {
 | 
				
			|||||||
    * @param {Object} opts
 | 
					    * @param {Object} opts
 | 
				
			||||||
    * @param {Object} opts.req - the request to log (optional)
 | 
					    * @param {Object} opts.req - the request to log (optional)
 | 
				
			||||||
    * @param {User} opts.user - the user calling the api
 | 
					    * @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<Flow>} - the active flow configuration
 | 
					    * @return {Promise<Flow>} - the active flow configuration
 | 
				
			||||||
    * @memberof @node-red/runtime_flows
 | 
					    * @memberof @node-red/runtime_flows
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
@@ -295,7 +289,7 @@ var api = module.exports = {
 | 
				
			|||||||
            err.code = err.code || errcode || "unexpected_error"
 | 
					            err.code = err.code || errcode || "unexpected_error"
 | 
				
			||||||
            runtime.log.audit({
 | 
					            runtime.log.audit({
 | 
				
			||||||
                event: "flows.setState",
 | 
					                event: "flows.setState",
 | 
				
			||||||
                state: opts.requestedState || "",
 | 
					                state: opts.state || "",
 | 
				
			||||||
                error: errcode || "unexpected_error",
 | 
					                error: errcode || "unexpected_error",
 | 
				
			||||||
                message: err.code
 | 
					                message: err.code
 | 
				
			||||||
            }, opts.req);
 | 
					            }, opts.req);
 | 
				
			||||||
@@ -304,21 +298,22 @@ var api = module.exports = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const getState = () => {
 | 
					        const getState = () => {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                state: runtime.flows.started ? "started" : "stopped",
 | 
					                state: runtime.flows.state()
 | 
				
			||||||
                started: !!runtime.flows.started,
 | 
					 | 
				
			||||||
                rev: runtime.flows.getFlows().rev,
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!runtime.settings.runtimeState || runtime.settings.runtimeState.enabled !== true) {
 | 
					        if(!runtime.settings.runtimeState || runtime.settings.runtimeState.enabled !== true) {
 | 
				
			||||||
            throw (makeError("Method Not Allowed", "not_allowed", 405))
 | 
					            throw (makeError("Method Not Allowed", "not_allowed", 405))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        switch (opts.requestedState) {
 | 
					        switch (opts.state) {
 | 
				
			||||||
            case "start":
 | 
					            case "start":
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
                        runtime.settings.set('flowsRunStateRequested', opts.requestedState);
 | 
					                        runtime.settings.set('runtimeFlowState', opts.state);
 | 
				
			||||||
                    } catch(err) { }
 | 
					                    } catch(err) {}
 | 
				
			||||||
 | 
					                    if (runtime.settings.safeMode) {
 | 
				
			||||||
 | 
					                        delete runtime.settings.safeMode
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    await runtime.flows.startFlows("full")
 | 
					                    await runtime.flows.startFlows("full")
 | 
				
			||||||
                    return getState()
 | 
					                    return getState()
 | 
				
			||||||
                } catch (err) {
 | 
					                } catch (err) {
 | 
				
			||||||
@@ -327,15 +322,15 @@ var api = module.exports = {
 | 
				
			|||||||
            case "stop":
 | 
					            case "stop":
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
                        runtime.settings.set('flowsRunStateRequested', opts.requestedState);
 | 
					                        runtime.settings.set('runtimeFlowState', opts.state);
 | 
				
			||||||
                    } catch(err) { }
 | 
					                    } catch(err) {}
 | 
				
			||||||
                    await runtime.flows.stopFlows("full")
 | 
					                    await runtime.flows.stopFlows("full")
 | 
				
			||||||
                    return getState()
 | 
					                    return getState()
 | 
				
			||||||
                } catch (err) {
 | 
					                } catch (err) {
 | 
				
			||||||
                    throw (makeError(err, err.code, 500))
 | 
					                    throw (makeError(err, err.code, 500))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            default:
 | 
					            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))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,8 @@ var activeFlowConfig = null;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var activeFlows = {};
 | 
					var activeFlows = {};
 | 
				
			||||||
var started = false;
 | 
					var started = false;
 | 
				
			||||||
 | 
					var state = 'stop'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var credentialsPendingReset = false;
 | 
					var credentialsPendingReset = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var activeNodesToFlow = {};
 | 
					var activeNodesToFlow = {};
 | 
				
			||||||
@@ -50,6 +52,7 @@ function init(runtime) {
 | 
				
			|||||||
    storage = runtime.storage;
 | 
					    storage = runtime.storage;
 | 
				
			||||||
    log = runtime.log;
 | 
					    log = runtime.log;
 | 
				
			||||||
    started = false;
 | 
					    started = false;
 | 
				
			||||||
 | 
					    state = 'stop';
 | 
				
			||||||
    if (!typeEventRegistered) {
 | 
					    if (!typeEventRegistered) {
 | 
				
			||||||
        events.on('type-registered',function(type) {
 | 
					        events.on('type-registered',function(type) {
 | 
				
			||||||
            if (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
 | 
					            if (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
 | 
				
			||||||
@@ -214,19 +217,26 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
 | 
				
			|||||||
            // Flows are running (or should be)
 | 
					            // Flows are running (or should be)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Stop the active flows (according to deploy type and the diff)
 | 
					            // 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
 | 
					                // Once stopped, allow context to remove anything no longer needed
 | 
				
			||||||
                return context.clean(activeFlowConfig)
 | 
					                return context.clean(activeFlowConfig)
 | 
				
			||||||
            }).then(() => {
 | 
					            }).then(() => {
 | 
				
			||||||
 | 
					                if (!isLoad) {
 | 
				
			||||||
 | 
					                    log.info(log._("nodes.flows.updated-flows"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                // Start the active 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});
 | 
					                    events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
                // Return the new revision asynchronously to the actual start
 | 
					                // Return the new revision asynchronously to the actual start
 | 
				
			||||||
                return flowRevision;
 | 
					                return flowRevision;
 | 
				
			||||||
            }).catch(function(err) { })
 | 
					            }).catch(function(err) { })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (!isLoad) {
 | 
				
			||||||
 | 
					                log.info(log._("nodes.flows.updated-flows"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
 | 
					            events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
 | 
				
			||||||
 | 
					            return flowRevision;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -259,10 +269,10 @@ function getFlows() {
 | 
				
			|||||||
    return activeConfig;
 | 
					    return activeConfig;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function start(type,diff,muteLog) {
 | 
					async function start(type,diff,muteLog,isDeploy) {
 | 
				
			||||||
    type = type||"full";
 | 
					    type = type || "full";
 | 
				
			||||||
    let reallyStarted = started
 | 
					 | 
				
			||||||
    started = true;
 | 
					    started = true;
 | 
				
			||||||
 | 
					    state = 'start'
 | 
				
			||||||
    var i;
 | 
					    var i;
 | 
				
			||||||
    // If there are missing types, report them, emit the necessary runtime event and return
 | 
					    // If there are missing types, report them, emit the necessary runtime event and return
 | 
				
			||||||
    if (activeFlowConfig.missingTypes.length > 0) {
 | 
					    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(log._("nodes.flows.missing-type-install-2"));
 | 
				
			||||||
            log.info("  "+settings.userDir);
 | 
					            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;
 | 
					        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()})
 | 
					            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"}]`);
 | 
					            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;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -307,10 +317,20 @@ async function start(type,diff,muteLog) {
 | 
				
			|||||||
        log.info("*****************************************************************")
 | 
					        log.info("*****************************************************************")
 | 
				
			||||||
        log.info(log._("nodes.flows.safe-mode"));
 | 
					        log.info(log._("nodes.flows.safe-mode"));
 | 
				
			||||||
        log.info("*****************************************************************")
 | 
					        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;
 | 
					        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 (!muteLog) {
 | 
				
			||||||
        if (type !== "full") {
 | 
					        if (type !== "full") {
 | 
				
			||||||
            log.info(log._("nodes.flows.starting-modified-"+type));
 | 
					            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.
 | 
					    for (id in activeFlows) {
 | 
				
			||||||
    let startFlows = true
 | 
					        if (activeFlows.hasOwnProperty(id)) {
 | 
				
			||||||
    try {
 | 
					            try {
 | 
				
			||||||
        startFlows = settings.get('flowsRunStateRequested');
 | 
					                activeFlows[id].start(diff);
 | 
				
			||||||
    } catch(err) {
 | 
					                // Create a map of node id to flow id and also a subflowInstance lookup map
 | 
				
			||||||
    }
 | 
					                var activeNodes = activeFlows[id].getActiveNodes();
 | 
				
			||||||
    startFlows = (startFlows !== "stop");
 | 
					                Object.keys(activeNodes).forEach(function(nid) {
 | 
				
			||||||
 | 
					                    activeNodesToFlow[nid] = id;
 | 
				
			||||||
    if (startFlows) {
 | 
					                });
 | 
				
			||||||
        for (id in activeFlows) {
 | 
					            } catch(err) {
 | 
				
			||||||
            if (activeFlows.hasOwnProperty(id)) {
 | 
					                console.log(err.stack);
 | 
				
			||||||
                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});
 | 
				
			||||||
    const state = {
 | 
					    // Deprecated event
 | 
				
			||||||
        started: reallyStarted,
 | 
					    events.emit("nodes-started");
 | 
				
			||||||
        state: reallyStarted ? "started" : "stopped",
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    events.emit("runtime-event",{id:"flows-run-state", payload: state, retain:true});
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (credentialsPendingReset === true) {
 | 
					    if (credentialsPendingReset === true) {
 | 
				
			||||||
        credentialsPendingReset = false;
 | 
					        credentialsPendingReset = false;
 | 
				
			||||||
    } else {
 | 
					    } 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") {
 | 
					        if (type !== "full") {
 | 
				
			||||||
            log.info(log._("nodes.flows.started-modified-"+type));
 | 
					            log.info(log._("nodes.flows.started-modified-"+type));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -419,7 +419,7 @@ async function start(type,diff,muteLog) {
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function stop(type,diff,muteLog) {
 | 
					function stop(type,diff,muteLog,isDeploy) {
 | 
				
			||||||
    if (!started) {
 | 
					    if (!started) {
 | 
				
			||||||
        return Promise.resolve();
 | 
					        return Promise.resolve();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -439,6 +439,7 @@ function stop(type,diff,muteLog) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    started = false;
 | 
					    started = false;
 | 
				
			||||||
 | 
					    state = 'stop'
 | 
				
			||||||
    var promises = [];
 | 
					    var promises = [];
 | 
				
			||||||
    var stopList;
 | 
					    var stopList;
 | 
				
			||||||
    var removedList = diff.removed;
 | 
					    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("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
 | 
					        // Deprecated event
 | 
				
			||||||
        events.emit("nodes-stopped");
 | 
					        events.emit("nodes-stopped");
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -810,7 +812,7 @@ module.exports = {
 | 
				
			|||||||
    stopFlows: stop,
 | 
					    stopFlows: stop,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get started() { return started },
 | 
					    get started() { return started },
 | 
				
			||||||
 | 
					    state: () => { return state },
 | 
				
			||||||
    // handleError: handleError,
 | 
					    // handleError: handleError,
 | 
				
			||||||
    // handleStatus: handleStatus,
 | 
					    // handleStatus: handleStatus,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,7 +215,7 @@ function start() {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return redNodes.loadContextsPlugin().then(function () {
 | 
					                return redNodes.loadContextsPlugin().then(function () {
 | 
				
			||||||
                    redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
 | 
					                    redNodes.loadFlows().then(() => { redNodes.startFlows() }).catch(function(err) {});
 | 
				
			||||||
                    started = true;
 | 
					                    started = true;
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@@ -399,12 +399,12 @@ module.exports = {
 | 
				
			|||||||
    * @memberof @node-red/runtime
 | 
					    * @memberof @node-red/runtime
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
    version: externalAPI.version,
 | 
					    version: externalAPI.version,
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
    * @memberof @node-red/diagnostics
 | 
					    * @memberof @node-red/diagnostics
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
    diagnostics:externalAPI.diagnostics,
 | 
					    diagnostics:externalAPI.diagnostics,
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    storage: storage,
 | 
					    storage: storage,
 | 
				
			||||||
    events: events,
 | 
					    events: events,
 | 
				
			||||||
    hooks: hooks,
 | 
					    hooks: hooks,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,6 +122,7 @@
 | 
				
			|||||||
            "stopped-flows": "Stopped flows",
 | 
					            "stopped-flows": "Stopped flows",
 | 
				
			||||||
            "stopped": "Stopped",
 | 
					            "stopped": "Stopped",
 | 
				
			||||||
            "stopping-error": "Error stopping node: __message__",
 | 
					            "stopping-error": "Error stopping node: __message__",
 | 
				
			||||||
 | 
					            "updated-flows": "Updated flows",
 | 
				
			||||||
            "added-flow": "Adding flow: __label__",
 | 
					            "added-flow": "Adding flow: __label__",
 | 
				
			||||||
            "updated-flow": "Updated flow: __label__",
 | 
					            "updated-flow": "Updated flow: __label__",
 | 
				
			||||||
            "removed-flow": "Removed flow: __label__",
 | 
					            "removed-flow": "Removed flow: __label__",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user