mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'master' into dev
This commit is contained in:
		| @@ -27,6 +27,7 @@ | |||||||
|     ], |     ], | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "ajv": "6.12.3", |         "ajv": "6.12.3", | ||||||
|  |         "async-mutex": "0.2.4", | ||||||
|         "basic-auth": "2.0.1", |         "basic-auth": "2.0.1", | ||||||
|         "bcryptjs": "2.4.3", |         "bcryptjs": "2.4.3", | ||||||
|         "body-parser": "1.19.0", |         "body-parser": "1.19.0", | ||||||
|   | |||||||
| @@ -119,6 +119,9 @@ | |||||||
|                 if (evt.keyCode === 27) { |                 if (evt.keyCode === 27) { | ||||||
|                     that.element.val(""); |                     that.element.val(""); | ||||||
|                 } |                 } | ||||||
|  |                 if (evt.keyCode === 13) { | ||||||
|  |                     evt.preventDefault(); | ||||||
|  |                 } | ||||||
|             }) |             }) | ||||||
|             this.element.on("keyup",function(evt) { |             this.element.on("keyup",function(evt) { | ||||||
|                 that._change($(this).val()); |                 that._change($(this).val()); | ||||||
|   | |||||||
| @@ -380,11 +380,16 @@ RED.group = (function() { | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |             var existingGroup; | ||||||
|  |  | ||||||
|             // Second pass, ungroup any groups in the selection and add their contents |             // Second pass, ungroup any groups in the selection and add their contents | ||||||
|             // to the selection |             // to the selection | ||||||
|             for (var i=0; i<selection.nodes.length; i++) { |             for (var i=0; i<selection.nodes.length; i++) { | ||||||
|                 n = selection.nodes[i]; |                 n = selection.nodes[i]; | ||||||
|                 if (n.type === "group") { |                 if (n.type === "group") { | ||||||
|  |                     if (!existingGroup) { | ||||||
|  |                         existingGroup = n; | ||||||
|  |                     } | ||||||
|                     ungroupHistoryEvent.groups.push(n); |                     ungroupHistoryEvent.groups.push(n); | ||||||
|                     nodes = nodes.concat(ungroup(n)); |                     nodes = nodes.concat(ungroup(n)); | ||||||
|                 } else { |                 } else { | ||||||
| @@ -398,6 +403,10 @@ RED.group = (function() { | |||||||
|             // Finally, create the new group |             // Finally, create the new group | ||||||
|             var group = createGroup(nodes); |             var group = createGroup(nodes); | ||||||
|             if (group) { |             if (group) { | ||||||
|  |                 if (existingGroup) { | ||||||
|  |                     group.style = existingGroup.style; | ||||||
|  |                     group.name = existingGroup.name; | ||||||
|  |                 } | ||||||
|                 RED.view.select({nodes:[group]}) |                 RED.view.select({nodes:[group]}) | ||||||
|             } |             } | ||||||
|             historyEvent.events.push({ |             historyEvent.events.push({ | ||||||
|   | |||||||
| @@ -1678,7 +1678,7 @@ RED.view = (function() { | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (ns.length > 0) { |                 if (ns.length > 0 && mouse_mode == RED.state.MOVING_ACTIVE) { | ||||||
|                     historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}; |                     historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}; | ||||||
|                     if (activeSpliceLink) { |                     if (activeSpliceLink) { | ||||||
|                         // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp |                         // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp | ||||||
| @@ -2354,6 +2354,10 @@ RED.view = (function() { | |||||||
|         mousedown_port_type = null; |         mousedown_port_type = null; | ||||||
|         activeSpliceLink = null; |         activeSpliceLink = null; | ||||||
|         spliceActive = false; |         spliceActive = false; | ||||||
|  |         if (activeHoverGroup) { | ||||||
|  |             activeHoverGroup.hovered = false; | ||||||
|  |             activeHoverGroup = null; | ||||||
|  |         } | ||||||
|         d3.select(".red-ui-flow-link-splice").classed("red-ui-flow-link-splice",false); |         d3.select(".red-ui-flow-link-splice").classed("red-ui-flow-link-splice",false); | ||||||
|         if (spliceTimer) { |         if (spliceTimer) { | ||||||
|             clearTimeout(spliceTimer); |             clearTimeout(spliceTimer); | ||||||
| @@ -2869,7 +2873,7 @@ RED.view = (function() { | |||||||
|         //RED.touch.radialMenu.show(d3.select(this),pos); |         //RED.touch.radialMenu.show(d3.select(this),pos); | ||||||
|         if (mouse_mode == RED.state.IMPORT_DRAGGING) { |         if (mouse_mode == RED.state.IMPORT_DRAGGING) { | ||||||
|             RED.keyboard.remove("escape"); |             RED.keyboard.remove("escape"); | ||||||
|  |             var historyEvent = RED.history.peek(); | ||||||
|             if (activeSpliceLink) { |             if (activeSpliceLink) { | ||||||
|                 // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp |                 // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp | ||||||
|                 var spliceLink = d3.select(activeSpliceLink).data()[0]; |                 var spliceLink = d3.select(activeSpliceLink).data()[0]; | ||||||
| @@ -2886,12 +2890,27 @@ RED.view = (function() { | |||||||
|                 }; |                 }; | ||||||
|                 RED.nodes.addLink(link1); |                 RED.nodes.addLink(link1); | ||||||
|                 RED.nodes.addLink(link2); |                 RED.nodes.addLink(link2); | ||||||
|                 var historyEvent = RED.history.peek(); |  | ||||||
|                 historyEvent.links = [link1,link2]; |                 historyEvent.links = [link1,link2]; | ||||||
|                 historyEvent.removedLinks = [spliceLink]; |                 historyEvent.removedLinks = [spliceLink]; | ||||||
|                 updateActiveNodes(); |                 updateActiveNodes(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             if (activeHoverGroup) { | ||||||
|  |                 for (var j=0;j<movingSet.length();j++) { | ||||||
|  |                     var n = movingSet.get(j); | ||||||
|  |                     RED.group.addToGroup(activeHoverGroup,n.n); | ||||||
|  |                 } | ||||||
|  |                 historyEvent.addedToGroup = activeHoverGroup; | ||||||
|  |  | ||||||
|  |                 activeHoverGroup.hovered = false; | ||||||
|  |                 enterActiveGroup(activeHoverGroup) | ||||||
|  |                 // TODO: check back whether this should add to moving_set | ||||||
|  |                 activeGroup.selected = true; | ||||||
|  |                 activeHoverGroup = null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|             updateSelection(); |             updateSelection(); | ||||||
|             RED.nodes.dirty(true); |             RED.nodes.dirty(true); | ||||||
|             redraw(); |             redraw(); | ||||||
| @@ -4418,6 +4437,7 @@ RED.view = (function() { | |||||||
|                 } |                 } | ||||||
|                 if (d.dirty || dirtyGroups[d.id]) { |                 if (d.dirty || dirtyGroups[d.id]) { | ||||||
|                     var g = d3.select(this); |                     var g = d3.select(this); | ||||||
|  |                     var recalculateLabelOffsets = false; | ||||||
|                     if (d.nodes.length > 0) { |                     if (d.nodes.length > 0) { | ||||||
|                         // If the group was just moved, all of its contents was |                         // If the group was just moved, all of its contents was | ||||||
|                         // also moved - so no need to recalculate its bounding box |                         // also moved - so no need to recalculate its bounding box | ||||||
| @@ -4446,6 +4466,7 @@ RED.view = (function() { | |||||||
|                             d.y = minY; |                             d.y = minY; | ||||||
|                             d.w = maxX - minX; |                             d.w = maxX - minX; | ||||||
|                             d.h = maxY - minY; |                             d.h = maxY - minY; | ||||||
|  |                             recalculateLabelOffsets = true; | ||||||
|                             // if set explicitly to false, this group has just been |                             // if set explicitly to false, this group has just been | ||||||
|                             // imported so needed this initial resize calculation. |                             // imported so needed this initial resize calculation. | ||||||
|                             // Now that's done, delete the flag so the normal |                             // Now that's done, delete the flag so the normal | ||||||
| @@ -4459,28 +4480,31 @@ RED.view = (function() { | |||||||
|                     } else { |                     } else { | ||||||
|                         d.w = 40; |                         d.w = 40; | ||||||
|                         d.h = 40; |                         d.h = 40; | ||||||
|  |                         recalculateLabelOffsets = true; | ||||||
|                     } |                     } | ||||||
|                     if (!d.minWidth) { |                     if (recalculateLabelOffsets) { | ||||||
|                         if (d.style.label && d.name) { |                         if (!d.minWidth) { | ||||||
|                             var labelParts = getLabelParts(d.name||"","red-ui-flow-group-label"); |                             if (d.style.label && d.name) { | ||||||
|                             d.minWidth = labelParts.width + 8; |                                 var labelParts = getLabelParts(d.name||"","red-ui-flow-group-label"); | ||||||
|                             d.labels = labelParts.lines; |                                 d.minWidth = labelParts.width + 8; | ||||||
|                         } else { |                                 d.labels = labelParts.lines; | ||||||
|                             d.minWidth = 40; |                             } else { | ||||||
|                             d.labels = []; |                                 d.minWidth = 40; | ||||||
|  |                                 d.labels = []; | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                         d.w = Math.max(d.minWidth,d.w); | ||||||
|                     d.w = Math.max(d.minWidth,d.w); |                         if (d.style.label && d.labels.length > 0) { | ||||||
|                     if (d.style.label && d.labels.length > 0) { |                             var labelPos = d.style["label-position"] || "nw"; | ||||||
|                         var labelPos = d.style["label-position"] || "nw"; |                             var h = (d.labels.length-1) * 16; | ||||||
|                         var h = (d.labels.length-1) * 16; |                             if (labelPos[0] === "s") { | ||||||
|                         if (labelPos[0] === "s") { |                                 h += 8; | ||||||
|                             h += 8; |                             } | ||||||
|                         } |                             d.h += h; | ||||||
|                         d.h += h; |                             if (labelPos[0] === "n") { | ||||||
|                         if (labelPos[0] === "n") { |                                 if (d.nodes.length > 0) { | ||||||
|                             if (d.nodes.length > 0) { |                                     d.y -= h; | ||||||
|                                 d.y -= h; |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|   | |||||||
| @@ -34,6 +34,8 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| var runtime; | var runtime; | ||||||
|  | var Mutex = require('async-mutex').Mutex; | ||||||
|  | const mutex = new Mutex(); | ||||||
|  |  | ||||||
| var api = module.exports = { | var api = module.exports = { | ||||||
|     init: function(_runtime) { |     init: function(_runtime) { | ||||||
| @@ -64,35 +66,37 @@ var api = module.exports = { | |||||||
|     * @memberof @node-red/runtime_flows |     * @memberof @node-red/runtime_flows | ||||||
|     */ |     */ | ||||||
|     setFlows: function(opts) { |     setFlows: function(opts) { | ||||||
|         return new Promise(function(resolve,reject) { |         return mutex.runExclusive(function() { | ||||||
|  |             return new Promise(function(resolve,reject) { | ||||||
|  |  | ||||||
|             var flows = opts.flows; |                 var flows = opts.flows; | ||||||
|             var deploymentType = opts.deploymentType||"full"; |                 var deploymentType = opts.deploymentType||"full"; | ||||||
|             runtime.log.audit({event: "flows.set",type:deploymentType}, opts.req); |                 runtime.log.audit({event: "flows.set",type:deploymentType}, opts.req); | ||||||
|  |  | ||||||
|             var apiPromise; |                 var apiPromise; | ||||||
|             if (deploymentType === 'reload') { |                 if (deploymentType === 'reload') { | ||||||
|                 apiPromise = runtime.nodes.loadFlows(true); |                     apiPromise = runtime.nodes.loadFlows(true); | ||||||
|             } else { |                 } else { | ||||||
|                 if (flows.hasOwnProperty('rev')) { |                     if (flows.hasOwnProperty('rev')) { | ||||||
|                     var currentVersion = runtime.nodes.getFlows().rev; |                         var currentVersion = runtime.nodes.getFlows().rev; | ||||||
|                     if (currentVersion !== flows.rev) { |                         if (currentVersion !== flows.rev) { | ||||||
|                         var err; |                             var err; | ||||||
|                         err = new Error(); |                             err = new Error(); | ||||||
|                         err.code = "version_mismatch"; |                             err.code = "version_mismatch"; | ||||||
|                         err.status = 409; |                             err.status = 409; | ||||||
|                         //TODO: log warning |                             //TODO: log warning | ||||||
|                         return reject(err); |                             return reject(err); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|  |                     apiPromise = runtime.nodes.setFlows(flows.flows,flows.credentials,deploymentType); | ||||||
|                 } |                 } | ||||||
|                 apiPromise = runtime.nodes.setFlows(flows.flows,flows.credentials,deploymentType); |                 apiPromise.then(function(flowId) { | ||||||
|             } |                     return resolve({rev:flowId}); | ||||||
|             apiPromise.then(function(flowId) { |                 }).catch(function(err) { | ||||||
|                 return resolve({rev:flowId}); |                     runtime.log.warn(runtime.log._("api.flows.error-"+(deploymentType === 'reload'?'reload':'save'),{message:err.message})); | ||||||
|             }).catch(function(err) { |                     runtime.log.warn(err.stack); | ||||||
|                 runtime.log.warn(runtime.log._("api.flows.error-"+(deploymentType === 'reload'?'reload':'save'),{message:err.message})); |                     return reject(err); | ||||||
|                 runtime.log.warn(err.stack); |                 }); | ||||||
|                 return reject(err); |  | ||||||
|             }); |             }); | ||||||
|         });     |         });     | ||||||
|     }, |     }, | ||||||
| @@ -107,19 +111,23 @@ var api = module.exports = { | |||||||
|     * @memberof @node-red/runtime_flows |     * @memberof @node-red/runtime_flows | ||||||
|     */ |     */ | ||||||
|     addFlow: function(opts) { |     addFlow: function(opts) { | ||||||
|         return new Promise(function(resolve,reject) { |         return mutex.runExclusive(function() { | ||||||
|             var flow = opts.flow; |             return new Promise(function (resolve, reject) { | ||||||
|             runtime.nodes.addFlow(flow).then(function(id) { |                 var flow = opts.flow; | ||||||
|                 runtime.log.audit({event: "flow.add",id:id}, opts.req); |                 runtime.nodes.addFlow(flow).then(function (id) { | ||||||
|                 return resolve(id); |                     runtime.log.audit({event: "flow.add", id: id}, opts.req); | ||||||
|             }).catch(function(err) { |                     return resolve(id); | ||||||
|                 runtime.log.audit({event: "flow.add",error:err.code||"unexpected_error",message:err.toString()}, opts.req); |                 }).catch(function (err) { | ||||||
|                 err.status = 400; |                     runtime.log.audit({ | ||||||
|                 return reject(err); |                         event: "flow.add", | ||||||
|  |                         error: err.code || "unexpected_error", | ||||||
|  |                         message: err.toString() | ||||||
|  |                     }, opts.req); | ||||||
|  |                     err.status = 400; | ||||||
|  |                     return reject(err); | ||||||
|  |                 }) | ||||||
|             }) |             }) | ||||||
|         }) |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -145,7 +153,6 @@ var api = module.exports = { | |||||||
|                 return reject(err); |                 return reject(err); | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|     /** |     /** | ||||||
|     * Updates an existing flow configuration |     * Updates an existing flow configuration | ||||||
| @@ -158,33 +165,42 @@ var api = module.exports = { | |||||||
|     * @memberof @node-red/runtime_flows |     * @memberof @node-red/runtime_flows | ||||||
|     */ |     */ | ||||||
|     updateFlow: function(opts) { |     updateFlow: function(opts) { | ||||||
|         return new Promise(function (resolve,reject) { |         return mutex.runExclusive(function() { | ||||||
|             var flow = opts.flow; |             return new Promise(function (resolve, reject) { | ||||||
|             var id = opts.id; |                 var flow = opts.flow; | ||||||
|             try { |                 var id = opts.id; | ||||||
|                 runtime.nodes.updateFlow(id,flow).then(function() { |                 try { | ||||||
|                     runtime.log.audit({event: "flow.update",id:id}, opts.req); |                     runtime.nodes.updateFlow(id, flow).then(function () { | ||||||
|                     return resolve(id); |                         runtime.log.audit({event: "flow.update", id: id}, opts.req); | ||||||
|                 }).catch(function(err) { |                         return resolve(id); | ||||||
|                     runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req); |                     }).catch(function (err) { | ||||||
|                     err.status = 400; |                         runtime.log.audit({ | ||||||
|                     return reject(err); |                             event: "flow.update", | ||||||
|                 }) |                             error: err.code || "unexpected_error", | ||||||
|             } catch(err) { |                             message: err.toString() | ||||||
|                 if (err.code === 404) { |                         }, opts.req); | ||||||
|                     runtime.log.audit({event: "flow.update",id:id,error:"not_found"}, opts.req); |                         err.status = 400; | ||||||
|                     // TODO: this swap around of .code and .status isn't ideal |                         return reject(err); | ||||||
|                     err.status = 404; |                     }) | ||||||
|                     err.code = "not_found"; |                 } catch (err) { | ||||||
|                     return reject(err); |                     if (err.code === 404) { | ||||||
|                 } else { |                         runtime.log.audit({event: "flow.update", id: id, error: "not_found"}, opts.req); | ||||||
|                     runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req); |                         // TODO: this swap around of .code and .status isn't ideal | ||||||
|                     err.status = 400; |                         err.status = 404; | ||||||
|                     return reject(err); |                         err.code = "not_found"; | ||||||
|  |                         return reject(err); | ||||||
|  |                     } else { | ||||||
|  |                         runtime.log.audit({ | ||||||
|  |                             event: "flow.update", | ||||||
|  |                             error: err.code || "unexpected_error", | ||||||
|  |                             message: err.toString() | ||||||
|  |                         }, opts.req); | ||||||
|  |                         err.status = 400; | ||||||
|  |                         return reject(err); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             }); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|     /** |     /** | ||||||
|     * Deletes a flow |     * Deletes a flow | ||||||
| @@ -196,30 +212,42 @@ var api = module.exports = { | |||||||
|     * @memberof @node-red/runtime_flows |     * @memberof @node-red/runtime_flows | ||||||
|     */ |     */ | ||||||
|     deleteFlow: function(opts) { |     deleteFlow: function(opts) { | ||||||
|         return new Promise(function (resolve,reject) { |         return mutex.runExclusive(function() { | ||||||
|             var id = opts.id; |             return new Promise(function (resolve, reject) { | ||||||
|             try { |                 var id = opts.id; | ||||||
|                 runtime.nodes.removeFlow(id).then(function() { |                 try { | ||||||
|                     runtime.log.audit({event: "flow.remove",id:id}, opts.req); |                     runtime.nodes.removeFlow(id).then(function () { | ||||||
|                     return resolve(); |                         runtime.log.audit({event: "flow.remove", id: id}, opts.req); | ||||||
|                 }).catch(function(err) { |                         return resolve(); | ||||||
|                     runtime.log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()}, opts.req); |                     }).catch(function (err) { | ||||||
|                     err.status = 400; |                         runtime.log.audit({ | ||||||
|                     return reject(err); |                             event: "flow.remove", | ||||||
|                 }); |                             id: id, | ||||||
|             } catch(err) { |                             error: err.code || "unexpected_error", | ||||||
|                 if (err.code === 404) { |                             message: err.toString() | ||||||
|                     runtime.log.audit({event: "flow.remove",id:id,error:"not_found"}, opts.req); |                         }, opts.req); | ||||||
|                     // TODO: this swap around of .code and .status isn't ideal |                         err.status = 400; | ||||||
|                     err.status = 404; |                         return reject(err); | ||||||
|                     err.code = "not_found"; |                     }); | ||||||
|                     return reject(err); |                 } catch (err) { | ||||||
|                 } else { |                     if (err.code === 404) { | ||||||
|                     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: "not_found"}, opts.req); | ||||||
|                     err.status = 400; |                         // TODO: this swap around of .code and .status isn't ideal | ||||||
|                     return reject(err); |                         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); | ||||||
|  |                         err.status = 400; | ||||||
|  |                         return reject(err); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             }); | ||||||
|         }); |         }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| @@ -264,5 +292,4 @@ var api = module.exports = { | |||||||
|             resolve(sendCredentials); |             resolve(sendCredentials); | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@node-red/registry": "1.2.0-alpha.1", |         "@node-red/registry": "1.2.0-alpha.1", | ||||||
|         "@node-red/util": "1.2.0-alpha.1", |         "@node-red/util": "1.2.0-alpha.1", | ||||||
|  |         "async-mutex": "0.2.4", | ||||||
|         "clone": "2.1.2", |         "clone": "2.1.2", | ||||||
|         "express": "4.17.1", |         "express": "4.17.1", | ||||||
|         "fs-extra": "8.1.0", |         "fs-extra": "8.1.0", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user