@node-red/runtime 4.0.0-beta.1 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/comms.js +33 -4
- package/lib/api/plugins.js +19 -0
- package/lib/flows/Flow.js +3 -0
- package/lib/flows/Subflow.js +8 -7
- package/lib/flows/index.js +13 -13
- package/lib/flows/util.js +17 -9
- package/lib/index.js +2 -0
- package/lib/multiplayer/index.js +119 -0
- package/lib/nodes/index.js +8 -5
- package/lib/plugins.js +1 -0
- package/lib/storage/localfilesystem/projects/index.js +3 -1
- package/locales/de/runtime.json +0 -1
- package/locales/en-US/runtime.json +1 -1
- package/locales/es-ES/runtime.json +0 -1
- package/locales/fr/runtime.json +4 -2
- package/locales/ja/runtime.json +1 -1
- package/locales/pt-BR/runtime.json +0 -1
- package/locales/ru/runtime.json +0 -1
- package/package.json +6 -5
package/lib/api/comms.js
CHANGED
|
@@ -36,7 +36,7 @@ var connections = [];
|
|
|
36
36
|
const events = require("@node-red/util").events;
|
|
37
37
|
|
|
38
38
|
function handleCommsEvent(event) {
|
|
39
|
-
publish(event.topic,event.data,event.retain);
|
|
39
|
+
publish(event.topic,event.data,event.retain,event.session,event.excludeSession);
|
|
40
40
|
}
|
|
41
41
|
function handleStatusEvent(event) {
|
|
42
42
|
if (!event.status) {
|
|
@@ -74,13 +74,17 @@ function handleEventLog(event) {
|
|
|
74
74
|
publish("event-log/"+event.id,event.payload||{});
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
function publish(topic,data,retain) {
|
|
77
|
+
function publish(topic, data, retain, session, excludeSession) {
|
|
78
78
|
if (retain) {
|
|
79
79
|
retained[topic] = data;
|
|
80
80
|
} else {
|
|
81
81
|
delete retained[topic];
|
|
82
82
|
}
|
|
83
|
-
connections.forEach(connection =>
|
|
83
|
+
connections.forEach(connection => {
|
|
84
|
+
if ((!session || connection.session === session) && (!excludeSession || connection.session !== excludeSession)) {
|
|
85
|
+
connection.send(topic,data)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
|
|
@@ -109,6 +113,10 @@ var api = module.exports = {
|
|
|
109
113
|
*/
|
|
110
114
|
addConnection: async function(opts) {
|
|
111
115
|
connections.push(opts.client);
|
|
116
|
+
events.emit('comms:connection-added', {
|
|
117
|
+
session: opts.client.session,
|
|
118
|
+
user: opts.client.user
|
|
119
|
+
})
|
|
112
120
|
},
|
|
113
121
|
|
|
114
122
|
/**
|
|
@@ -126,6 +134,9 @@ var api = module.exports = {
|
|
|
126
134
|
break;
|
|
127
135
|
}
|
|
128
136
|
}
|
|
137
|
+
events.emit('comms:connection-removed', {
|
|
138
|
+
session: opts.client.session
|
|
139
|
+
})
|
|
129
140
|
},
|
|
130
141
|
|
|
131
142
|
/**
|
|
@@ -157,5 +168,23 @@ var api = module.exports = {
|
|
|
157
168
|
* @return {Promise<Object>} - resolves when complete
|
|
158
169
|
* @memberof @node-red/runtime_comms
|
|
159
170
|
*/
|
|
160
|
-
unsubscribe: async function(opts) {}
|
|
171
|
+
unsubscribe: async function(opts) {},
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {Object} opts
|
|
175
|
+
* @param {User} opts.user - the user calling the api
|
|
176
|
+
* @param {CommsConnection} opts.client - the client connection
|
|
177
|
+
* @param {String} opts.topic - the message topic
|
|
178
|
+
* @param {String} opts.data - the message data
|
|
179
|
+
* @return {Promise<Object>} - resolves when complete
|
|
180
|
+
*/
|
|
181
|
+
receive: async function (opts) {
|
|
182
|
+
if (opts.topic) {
|
|
183
|
+
events.emit('comms:message:' + opts.topic, {
|
|
184
|
+
session: opts.client.session,
|
|
185
|
+
user: opts.user,
|
|
186
|
+
data: opts.data
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
}
|
|
161
190
|
};
|
package/lib/api/plugins.js
CHANGED
|
@@ -65,6 +65,25 @@ var api = module.exports = {
|
|
|
65
65
|
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
|
66
66
|
return runtime.plugins.getPluginConfigs(opts.lang);
|
|
67
67
|
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets the editor content for one registered plugin
|
|
71
|
+
* @param {Object} opts
|
|
72
|
+
* @param {User} opts.user - the user calling the api
|
|
73
|
+
* @param {User} opts.user - the user calling the api
|
|
74
|
+
* @param {Object} opts.req - the request to log (optional)
|
|
75
|
+
* @return {Promise<NodeInfo>} - the plugin information
|
|
76
|
+
* @memberof @node-red/runtime_plugins
|
|
77
|
+
*/
|
|
78
|
+
getPluginConfig: async function(opts) {
|
|
79
|
+
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
|
|
80
|
+
throw new Error("Invalid language: "+opts.lang)
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
|
84
|
+
return runtime.plugins.getPluginConfig(opts.module, opts.lang);
|
|
85
|
+
},
|
|
86
|
+
|
|
68
87
|
/**
|
|
69
88
|
* Gets all registered module message catalogs
|
|
70
89
|
* @param {Object} opts
|
package/lib/flows/Flow.js
CHANGED
|
@@ -678,6 +678,9 @@ class Flow {
|
|
|
678
678
|
if (logMessage.hasOwnProperty('stack')) {
|
|
679
679
|
errorMessage.error.stack = logMessage.stack;
|
|
680
680
|
}
|
|
681
|
+
if (logMessage.hasOwnProperty('cause')) {
|
|
682
|
+
errorMessage.error.cause = logMessage.cause;
|
|
683
|
+
}
|
|
681
684
|
targetCatchNode.receive(errorMessage);
|
|
682
685
|
handled = true;
|
|
683
686
|
});
|
package/lib/flows/Subflow.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
17
|
const clone = require("clone");
|
|
18
|
+
const jsonClone = require("rfdc")();
|
|
18
19
|
const Flow = require('./Flow').Flow;
|
|
19
20
|
const context = require('../nodes/context');
|
|
20
21
|
const util = require("util");
|
|
@@ -108,7 +109,7 @@ class Subflow extends Flow {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
subflowInternalFlowConfig.subflows =
|
|
112
|
+
subflowInternalFlowConfig.subflows = jsonClone(subflowDef.subflows || {});
|
|
112
113
|
|
|
113
114
|
remapSubflowNodes(subflowInternalFlowConfig.configs,node_map);
|
|
114
115
|
remapSubflowNodes(subflowInternalFlowConfig.nodes,node_map);
|
|
@@ -220,7 +221,7 @@ class Subflow extends Flow {
|
|
|
220
221
|
}
|
|
221
222
|
if (this.subflowDef.in) {
|
|
222
223
|
subflowInstanceConfig.wires = this.subflowDef.in.map(function(n) { return n.wires.map(function(w) { return self.node_map[w.id].id;})})
|
|
223
|
-
subflowInstanceConfig._originalWires =
|
|
224
|
+
subflowInstanceConfig._originalWires = jsonClone(subflowInstanceConfig.wires);
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
this.node = new Node(subflowInstanceConfig);
|
|
@@ -244,14 +245,14 @@ class Subflow extends Flow {
|
|
|
244
245
|
if (self.subflowDef.out) {
|
|
245
246
|
var node,wires,i,j;
|
|
246
247
|
// Restore the original wiring to the internal nodes
|
|
247
|
-
subflowInstanceConfig.wires =
|
|
248
|
+
subflowInstanceConfig.wires = jsonClone(subflowInstanceConfig._originalWires);
|
|
248
249
|
for (i=0;i<self.subflowDef.out.length;i++) {
|
|
249
250
|
wires = self.subflowDef.out[i].wires;
|
|
250
251
|
for (j=0;j<wires.length;j++) {
|
|
251
252
|
if (wires[j].id != self.subflowDef.id) {
|
|
252
253
|
node = self.node_map[wires[j].id];
|
|
253
254
|
if (node && node._originalWires) {
|
|
254
|
-
node.wires =
|
|
255
|
+
node.wires = jsonClone(node._originalWires);
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
}
|
|
@@ -300,7 +301,7 @@ class Subflow extends Flow {
|
|
|
300
301
|
var node = self.node_map[wires[j].id];
|
|
301
302
|
if (node) {
|
|
302
303
|
if (!node._originalWires) {
|
|
303
|
-
node._originalWires =
|
|
304
|
+
node._originalWires = jsonClone(node.wires);
|
|
304
305
|
}
|
|
305
306
|
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
|
|
306
307
|
} else {
|
|
@@ -323,7 +324,7 @@ class Subflow extends Flow {
|
|
|
323
324
|
var node = self.node_map[wires[j].id];
|
|
324
325
|
if (node) {
|
|
325
326
|
if (!node._originalWires) {
|
|
326
|
-
node._originalWires =
|
|
327
|
+
node._originalWires = jsonClone(node.wires);
|
|
327
328
|
}
|
|
328
329
|
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
|
|
329
330
|
node.wires[wires[j].port].push(subflowStatusId);
|
|
@@ -463,7 +464,7 @@ class Subflow extends Flow {
|
|
|
463
464
|
* @return {[type]} [description]
|
|
464
465
|
*/
|
|
465
466
|
function createNodeInSubflow(subflowInstanceId, def) {
|
|
466
|
-
let node =
|
|
467
|
+
let node = jsonClone(def);
|
|
467
468
|
let nid = `${subflowInstanceId}-${node.id}` //redUtil.generateId();
|
|
468
469
|
// console.log("Create Node In subflow",node._alias, "--->",nid, "(",node.type,")")
|
|
469
470
|
// node_map[node.id] = node;
|
package/lib/flows/index.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const jsonClone = require("rfdc")();
|
|
18
18
|
|
|
19
19
|
var Flow = require('./Flow');
|
|
20
20
|
|
|
@@ -140,16 +140,16 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
|
|
|
140
140
|
if (type === "load") {
|
|
141
141
|
isLoad = true;
|
|
142
142
|
configSavePromise = loadFlows().then(function(_config) {
|
|
143
|
-
config =
|
|
144
|
-
newFlowConfig = flowUtil.parseConfig(
|
|
143
|
+
config = jsonClone(_config.flows);
|
|
144
|
+
newFlowConfig = flowUtil.parseConfig(jsonClone(config));
|
|
145
145
|
type = "full";
|
|
146
146
|
return _config.rev;
|
|
147
147
|
});
|
|
148
148
|
} else {
|
|
149
149
|
// Clone the provided config so it can be manipulated
|
|
150
|
-
config =
|
|
150
|
+
config = jsonClone(_config);
|
|
151
151
|
// Parse the configuration
|
|
152
|
-
newFlowConfig = flowUtil.parseConfig(
|
|
152
|
+
newFlowConfig = flowUtil.parseConfig(jsonClone(config));
|
|
153
153
|
// Generate a diff to identify what has changed
|
|
154
154
|
diff = flowUtil.diffConfigs(activeFlowConfig,newFlowConfig);
|
|
155
155
|
|
|
@@ -609,7 +609,7 @@ async function addFlow(flow, user) {
|
|
|
609
609
|
nodes.push(node);
|
|
610
610
|
}
|
|
611
611
|
}
|
|
612
|
-
var newConfig =
|
|
612
|
+
var newConfig = jsonClone(activeConfig.flows);
|
|
613
613
|
newConfig = newConfig.concat(nodes);
|
|
614
614
|
|
|
615
615
|
return setFlows(newConfig, null, 'flows', true, null, user).then(function() {
|
|
@@ -650,7 +650,7 @@ function getFlow(id) {
|
|
|
650
650
|
var nodeIds = Object.keys(flow.nodes);
|
|
651
651
|
if (nodeIds.length > 0) {
|
|
652
652
|
result.nodes = nodeIds.map(function(nodeId) {
|
|
653
|
-
var node =
|
|
653
|
+
var node = jsonClone(flow.nodes[nodeId]);
|
|
654
654
|
if (node.type === 'link out') {
|
|
655
655
|
delete node.wires;
|
|
656
656
|
}
|
|
@@ -662,7 +662,7 @@ function getFlow(id) {
|
|
|
662
662
|
if (flow.configs) {
|
|
663
663
|
var configIds = Object.keys(flow.configs);
|
|
664
664
|
result.configs = configIds.map(function(configId) {
|
|
665
|
-
const node =
|
|
665
|
+
const node = jsonClone(flow.configs[configId]);
|
|
666
666
|
delete node.credentials;
|
|
667
667
|
return node
|
|
668
668
|
|
|
@@ -674,17 +674,17 @@ function getFlow(id) {
|
|
|
674
674
|
if (flow.subflows) {
|
|
675
675
|
var subflowIds = Object.keys(flow.subflows);
|
|
676
676
|
result.subflows = subflowIds.map(function(subflowId) {
|
|
677
|
-
var subflow =
|
|
677
|
+
var subflow = jsonClone(flow.subflows[subflowId]);
|
|
678
678
|
var nodeIds = Object.keys(subflow.nodes);
|
|
679
679
|
subflow.nodes = nodeIds.map(function(id) {
|
|
680
|
-
const node =
|
|
680
|
+
const node = jsonClone(subflow.nodes[id])
|
|
681
681
|
delete node.credentials
|
|
682
682
|
return node
|
|
683
683
|
});
|
|
684
684
|
if (subflow.configs) {
|
|
685
685
|
var configIds = Object.keys(subflow.configs);
|
|
686
686
|
subflow.configs = configIds.map(function(id) {
|
|
687
|
-
const node =
|
|
687
|
+
const node = jsonClone(subflow.configs[id])
|
|
688
688
|
delete node.credentials
|
|
689
689
|
return node
|
|
690
690
|
})
|
|
@@ -709,7 +709,7 @@ async function updateFlow(id,newFlow, user) {
|
|
|
709
709
|
}
|
|
710
710
|
label = activeFlowConfig.flows[id].label;
|
|
711
711
|
}
|
|
712
|
-
var newConfig =
|
|
712
|
+
var newConfig = jsonClone(activeConfig.flows);
|
|
713
713
|
var nodes;
|
|
714
714
|
|
|
715
715
|
if (id === 'global') {
|
|
@@ -779,7 +779,7 @@ async function removeFlow(id, user) {
|
|
|
779
779
|
throw e;
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
-
var newConfig =
|
|
782
|
+
var newConfig = jsonClone(activeConfig.flows);
|
|
783
783
|
newConfig = newConfig.filter(function(node) {
|
|
784
784
|
return node.z !== id && node.id !== id;
|
|
785
785
|
});
|
package/lib/flows/util.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
|
-
const
|
|
16
|
+
const jsonClone = require("rfdc")();
|
|
17
17
|
const redUtil = require("@node-red/util").util;
|
|
18
18
|
const Log = require("@node-red/util").log;
|
|
19
19
|
const typeRegistry = require("@node-red/registry");
|
|
@@ -68,7 +68,7 @@ function mapEnvVarProperties(obj,prop,flow,config) {
|
|
|
68
68
|
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
|
|
69
69
|
const envVar = v.substring(2,v.length-1);
|
|
70
70
|
const r = redUtil.getSetting(config, envVar, flow);
|
|
71
|
-
if (r !== undefined
|
|
71
|
+
if (r !== undefined) {
|
|
72
72
|
obj[prop] = r
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -106,14 +106,22 @@ async function evaluateEnvProperties(flow, env, credentials) {
|
|
|
106
106
|
result = { value: result, __clone__: true}
|
|
107
107
|
}
|
|
108
108
|
evaluatedEnv[name] = result
|
|
109
|
+
} else {
|
|
110
|
+
evaluatedEnv[name] = undefined
|
|
111
|
+
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
|
109
112
|
}
|
|
110
113
|
resolve()
|
|
111
114
|
});
|
|
112
115
|
}))
|
|
113
116
|
} else {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
try {
|
|
118
|
+
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
|
119
|
+
if (typeof value === 'object') {
|
|
120
|
+
value = { value: value, __clone__: true}
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
value = undefined
|
|
124
|
+
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
evaluatedEnv[name] = value
|
|
@@ -167,7 +175,7 @@ async function createNode(flow,config) {
|
|
|
167
175
|
try {
|
|
168
176
|
var nodeTypeConstructor = typeRegistry.get(type);
|
|
169
177
|
if (typeof nodeTypeConstructor === "function") {
|
|
170
|
-
var conf =
|
|
178
|
+
var conf = jsonClone(config);
|
|
171
179
|
delete conf.credentials;
|
|
172
180
|
try {
|
|
173
181
|
Object.defineProperty(conf,'_module', {value: typeRegistry.getNodeInfo(type), enumerable: false, writable: true })
|
|
@@ -194,8 +202,8 @@ async function createNode(flow,config) {
|
|
|
194
202
|
var subflowInstanceConfig = subflowConfig.subflows[nodeTypeConstructor.subflow.id];
|
|
195
203
|
delete subflowConfig.subflows[nodeTypeConstructor.subflow.id];
|
|
196
204
|
subflowInstanceConfig.subflows = subflowConfig.subflows;
|
|
197
|
-
var instanceConfig =
|
|
198
|
-
instanceConfig.env =
|
|
205
|
+
var instanceConfig = jsonClone(config);
|
|
206
|
+
instanceConfig.env = jsonClone(nodeTypeConstructor.subflow.env);
|
|
199
207
|
|
|
200
208
|
instanceConfig.env = nodeTypeConstructor.subflow.env.map(nodeProp => {
|
|
201
209
|
var nodePropType;
|
|
@@ -248,7 +256,7 @@ function parseConfig(config) {
|
|
|
248
256
|
flow.missingTypes = [];
|
|
249
257
|
|
|
250
258
|
config.forEach(function (n) {
|
|
251
|
-
flow.allNodes[n.id] =
|
|
259
|
+
flow.allNodes[n.id] = jsonClone(n);
|
|
252
260
|
if (n.type === 'tab') {
|
|
253
261
|
flow.flows[n.id] = n;
|
|
254
262
|
flow.flows[n.id].subflows = {};
|
package/lib/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var storage = require("./storage");
|
|
|
22
22
|
var library = require("./library");
|
|
23
23
|
var plugins = require("./plugins");
|
|
24
24
|
var settings = require("./settings");
|
|
25
|
+
const multiplayer = require("./multiplayer");
|
|
25
26
|
|
|
26
27
|
var express = require("express");
|
|
27
28
|
var path = require('path');
|
|
@@ -135,6 +136,7 @@ function start() {
|
|
|
135
136
|
.then(function() { return storage.init(runtime)})
|
|
136
137
|
.then(function() { return settings.load(storage)})
|
|
137
138
|
.then(function() { return library.init(runtime)})
|
|
139
|
+
.then(function() { return multiplayer.init(runtime)})
|
|
138
140
|
.then(function() {
|
|
139
141
|
if (settings.available()) {
|
|
140
142
|
if (settings.get('instanceId') === undefined) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
let runtime
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Active sessions, mapped by multiplayer session ids
|
|
5
|
+
*/
|
|
6
|
+
const sessions = new Map()
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Active connections, mapping comms session to multiplayer session
|
|
10
|
+
*/
|
|
11
|
+
const connections = new Map()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
function getSessionsList() {
|
|
15
|
+
return Array.from(sessions.values()).filter(session => session.active)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
init: function(_runtime) {
|
|
20
|
+
runtime = _runtime
|
|
21
|
+
runtime.events.on('comms:connection-removed', (opts) => {
|
|
22
|
+
const existingSessionId = connections.get(opts.session)
|
|
23
|
+
if (existingSessionId) {
|
|
24
|
+
connections.delete(opts.session)
|
|
25
|
+
const session = sessions.get(existingSessionId)
|
|
26
|
+
session.active = false
|
|
27
|
+
session.idleTimeout = setTimeout(() => {
|
|
28
|
+
sessions.delete(existingSessionId)
|
|
29
|
+
}, 30000)
|
|
30
|
+
runtime.events.emit('comms', {
|
|
31
|
+
topic: "multiplayer/connection-removed",
|
|
32
|
+
data: { session: existingSessionId }
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
runtime.events.on('comms:message:multiplayer/connect', (opts) => {
|
|
37
|
+
let session
|
|
38
|
+
if (!sessions.has(opts.data.session)) {
|
|
39
|
+
// Brand new session
|
|
40
|
+
let user = opts.user
|
|
41
|
+
if (!user || user.anonymous) {
|
|
42
|
+
user = user || { anonymous: true }
|
|
43
|
+
user.username = `Anon ${Math.floor(Math.random()*100)}`
|
|
44
|
+
}
|
|
45
|
+
session = {
|
|
46
|
+
session: opts.data.session,
|
|
47
|
+
user,
|
|
48
|
+
active: true
|
|
49
|
+
}
|
|
50
|
+
sessions.set(opts.data.session, session)
|
|
51
|
+
connections.set(opts.session, opts.data.session)
|
|
52
|
+
runtime.log.trace(`multiplayer new session:${opts.data.session} user:${user.username}`)
|
|
53
|
+
} else {
|
|
54
|
+
// Reconnected connection - keep existing state
|
|
55
|
+
connections.set(opts.session, opts.data.session)
|
|
56
|
+
// const existingConnection = connections.get(opts.data.session)
|
|
57
|
+
session = sessions.get(opts.data.session)
|
|
58
|
+
session.active = true
|
|
59
|
+
runtime.log.trace(`multiplayer reconnected session:${opts.data.session} user:${session.user.username}`)
|
|
60
|
+
clearTimeout(session.idleTimeout)
|
|
61
|
+
}
|
|
62
|
+
// Tell existing sessions about the new connection
|
|
63
|
+
runtime.events.emit('comms', {
|
|
64
|
+
topic: "multiplayer/connection-added",
|
|
65
|
+
excludeSession: opts.session,
|
|
66
|
+
data: session
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Send init info to new connection
|
|
70
|
+
const initPacket = {
|
|
71
|
+
topic: "multiplayer/init",
|
|
72
|
+
data: { sessions: getSessionsList() },
|
|
73
|
+
session: opts.session
|
|
74
|
+
}
|
|
75
|
+
// console.log('<<', initPacket)
|
|
76
|
+
runtime.events.emit('comms', initPacket)
|
|
77
|
+
})
|
|
78
|
+
runtime.events.on('comms:message:multiplayer/disconnect', (opts) => {
|
|
79
|
+
const existingSessionId = connections.get(opts.session)
|
|
80
|
+
connections.delete(opts.session)
|
|
81
|
+
sessions.delete(existingSessionId)
|
|
82
|
+
|
|
83
|
+
runtime.events.emit('comms', {
|
|
84
|
+
topic: "multiplayer/connection-removed",
|
|
85
|
+
data: { session: existingSessionId, disconnected: true }
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
runtime.events.on('comms:message:multiplayer/location', (opts) => {
|
|
89
|
+
// console.log('>>>', opts.user, opts.data)
|
|
90
|
+
|
|
91
|
+
const sessionId = connections.get(opts.session)
|
|
92
|
+
const session = sessions.get(sessionId)
|
|
93
|
+
|
|
94
|
+
if (opts.user) {
|
|
95
|
+
if (session.user.anonymous !== opts.user.anonymous) {
|
|
96
|
+
session.user = opts.user
|
|
97
|
+
runtime.events.emit('comms', {
|
|
98
|
+
topic: 'multiplayer/connection-added',
|
|
99
|
+
excludeSession: opts.session,
|
|
100
|
+
data: session
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
session.location = opts.data
|
|
106
|
+
|
|
107
|
+
const payload = {
|
|
108
|
+
session: sessionId,
|
|
109
|
+
workspace: opts.data.workspace,
|
|
110
|
+
node: opts.data.node
|
|
111
|
+
}
|
|
112
|
+
runtime.events.emit('comms', {
|
|
113
|
+
topic: 'multiplayer/location',
|
|
114
|
+
data: payload,
|
|
115
|
+
excludeSession: opts.session
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
package/lib/nodes/index.js
CHANGED
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
var clone = require("clone");
|
|
17
|
+
|
|
18
|
+
const jsonClone = require("rfdc")();
|
|
20
19
|
var util = require("util");
|
|
21
20
|
|
|
22
21
|
var registry = require("@node-red/registry");
|
|
@@ -98,7 +97,7 @@ function createNode(node,def) {
|
|
|
98
97
|
}
|
|
99
98
|
var creds = credentials.get(id);
|
|
100
99
|
if (creds) {
|
|
101
|
-
creds =
|
|
100
|
+
creds = jsonClone(creds);
|
|
102
101
|
//console.log("Attaching credentials to ",node.id);
|
|
103
102
|
// allow $(foo) syntax to substitute env variables for credentials also...
|
|
104
103
|
for (var p in creds) {
|
|
@@ -173,7 +172,11 @@ function installModule(module,version,url) {
|
|
|
173
172
|
if (info.pending_version) {
|
|
174
173
|
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:info.name,version:info.pending_version}});
|
|
175
174
|
} else {
|
|
176
|
-
|
|
175
|
+
if (!info.nodes.length && info.plugins.length) {
|
|
176
|
+
events.emit("runtime-event",{id:"plugin/added",retain:false,payload:info.plugins});
|
|
177
|
+
} else {
|
|
178
|
+
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
|
|
179
|
+
}
|
|
177
180
|
}
|
|
178
181
|
return info;
|
|
179
182
|
});
|
package/lib/plugins.js
CHANGED
|
@@ -242,7 +242,9 @@ function loadProject(name) {
|
|
|
242
242
|
|
|
243
243
|
function getProject(user, name) {
|
|
244
244
|
checkActiveProject(name);
|
|
245
|
-
return
|
|
245
|
+
return loadProject(name).then(function () {
|
|
246
|
+
return Promise.resolve(activeProject.export());
|
|
247
|
+
});
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
function deleteProject(user, name) {
|
package/locales/de/runtime.json
CHANGED
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"refresh-interval": "Erneuerung der https-Einstellungen erfolgt alle __interval__ Stunden",
|
|
57
57
|
"settings-refreshed": "https-Einstellungen wurden erneuert",
|
|
58
58
|
"refresh-failed": "Erneuerung der https-Einstellungen fehlgeschlagen: __message__",
|
|
59
|
-
"nodejs-version": "httpsRefreshInterval erfordert Node.js 11 oder höher",
|
|
60
59
|
"function-required": "httpsRefreshInterval erfordert die https-Eigenschaft in Form einer Funktion"
|
|
61
60
|
}
|
|
62
61
|
},
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"removing-modules": "Removing modules from config",
|
|
26
26
|
"added-types": "Added node types:",
|
|
27
27
|
"removed-types": "Removed node types:",
|
|
28
|
+
"removed-plugins": "Removed plugins:",
|
|
28
29
|
"install": {
|
|
29
30
|
"invalid": "Invalid module name",
|
|
30
31
|
"installing": "Installing module: __name__, version: __version__",
|
|
@@ -57,7 +58,6 @@
|
|
|
57
58
|
"refresh-interval": "Refreshing https settings every __interval__ hours",
|
|
58
59
|
"settings-refreshed": "Server https settings have been refreshed",
|
|
59
60
|
"refresh-failed": "Failed to refresh https settings: __message__",
|
|
60
|
-
"nodejs-version": "httpsRefreshInterval requires Node.js 11 or later",
|
|
61
61
|
"function-required": "httpsRefreshInterval requires https property to be a function"
|
|
62
62
|
}
|
|
63
63
|
},
|
|
@@ -57,7 +57,6 @@
|
|
|
57
57
|
"refresh-interval": "Actualizando la configuración HTTPS cada __interval__ horas",
|
|
58
58
|
"settings-refreshed": "La configuración HTTPS del servidor se ha actualizado",
|
|
59
59
|
"refresh-failed": "No se pudo actualizar la configuración HTTPS: __message__",
|
|
60
|
-
"nodejs-version": "httpsRefreshInterval requiere Node.js 11 o superior",
|
|
61
60
|
"function-required": "httpsRefreshInterval requiere que la propiedad HTTPS sea una función"
|
|
62
61
|
}
|
|
63
62
|
},
|
package/locales/fr/runtime.json
CHANGED
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
"errors-help": "Exécuter avec -v pour plus de détails",
|
|
21
21
|
"missing-modules": "Modules de noeud manquants :",
|
|
22
22
|
"node-version-mismatch": "Le module de noeud ne peut pas être chargé sur cette version. Nécessite : __version__ ",
|
|
23
|
+
"set-has-no-types": "L'ensemble n'a aucun type. Nom : '__name__', module : '__module__', fichier : '__file__'",
|
|
23
24
|
"type-already-registered": "'__type__' déjà enregistré par le module __module__",
|
|
24
25
|
"removing-modules": "Suppression de modules de la configuration",
|
|
25
26
|
"added-types": "Types de noeuds ajoutés :",
|
|
26
27
|
"removed-types": "Types de noeuds supprimés :",
|
|
28
|
+
"removed-plugins": "Plugins supprimés :",
|
|
27
29
|
"install": {
|
|
28
30
|
"invalid": "Nom de module invalide",
|
|
29
31
|
"installing": "Installation du module : __name__, version : __version__",
|
|
@@ -56,7 +58,6 @@
|
|
|
56
58
|
"refresh-interval": "Actualisation des paramètres https toutes les __interval__ heures",
|
|
57
59
|
"settings-refreshed": "Les paramètres https du serveur ont été actualisés",
|
|
58
60
|
"refresh-failed": "Échec de l'actualisation des paramètres https : __message__",
|
|
59
|
-
"nodejs-version": "httpsRefreshInterval nécessite Node.js 11 ou version ultérieure",
|
|
60
61
|
"function-required": "httpsRefreshInterval nécessite que la propriété https soit une fonction"
|
|
61
62
|
}
|
|
62
63
|
},
|
|
@@ -134,7 +135,8 @@
|
|
|
134
135
|
"flow": {
|
|
135
136
|
"unknown-type": "Type inconnu : __type__",
|
|
136
137
|
"missing-types": "Types manquants",
|
|
137
|
-
"error-loop": "Le message a dépassé le nombre maximum de captures (catches)"
|
|
138
|
+
"error-loop": "Le message a dépassé le nombre maximum de captures (catches)",
|
|
139
|
+
"non-message-returned": "Le noeud a tenté d'envoyer un message du type __type__"
|
|
138
140
|
},
|
|
139
141
|
"index": {
|
|
140
142
|
"unrecognised-id": "Identifiant non reconnu : __id__",
|
package/locales/ja/runtime.json
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"removing-modules": "設定からモジュールを削除します",
|
|
26
26
|
"added-types": "追加したノード:",
|
|
27
27
|
"removed-types": "削除したノード:",
|
|
28
|
+
"removed-plugins": "削除したプラグイン:",
|
|
28
29
|
"install": {
|
|
29
30
|
"invalid": "不正なモジュール名",
|
|
30
31
|
"installing": "モジュール __name__, バージョン: __version__ をインストールします",
|
|
@@ -57,7 +58,6 @@
|
|
|
57
58
|
"refresh-interval": "__interval__ 時間毎にhttps設定を更新します",
|
|
58
59
|
"settings-refreshed": "サーバのhttps設定が更新されました",
|
|
59
60
|
"refresh-failed": "https設定の更新で失敗しました: __message__",
|
|
60
|
-
"nodejs-version": "httpsRefreshIntervalにはNode.js 11以降が必要です",
|
|
61
61
|
"function-required": "httpsRefreshIntervalでは、httpsプロパティはfunctionである必要があります"
|
|
62
62
|
}
|
|
63
63
|
},
|
|
@@ -57,7 +57,6 @@
|
|
|
57
57
|
"refresh-interval": "Atualizando as configurações de https a cada __interval__ hora(s)",
|
|
58
58
|
"settings-refreshed": "As configurações https do servidor foram atualizadas",
|
|
59
59
|
"refresh-failed": "Falha ao atualizar as configurações https: __message__",
|
|
60
|
-
"nodejs-version": "httpsRefreshInterval requer Node.js 11 ou posterior",
|
|
61
60
|
"function-required": "httpsRefreshInterval requer que a propriedade https seja uma função"
|
|
62
61
|
}
|
|
63
62
|
},
|
package/locales/ru/runtime.json
CHANGED
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"refresh-interval": "Обновление настроек https каждые __interval__ часов",
|
|
56
56
|
"settings-refreshed": "Настройки сервера https обновлены",
|
|
57
57
|
"refresh-failed": "Не удалось обновить настройки https: __message__",
|
|
58
|
-
"nodejs-version": "httpsRefreshInterval требует Node.js 11 или выше",
|
|
59
58
|
"function-required": "httpsRefreshInterval требует, чтобы свойство https было функцией"
|
|
60
59
|
}
|
|
61
60
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-red/runtime",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@node-red/registry": "4.0.0-beta.
|
|
20
|
-
"@node-red/util": "4.0.0-beta.
|
|
19
|
+
"@node-red/registry": "4.0.0-beta.3",
|
|
20
|
+
"@node-red/util": "4.0.0-beta.3",
|
|
21
21
|
"async-mutex": "0.4.0",
|
|
22
22
|
"clone": "2.1.2",
|
|
23
|
-
"express": "4.
|
|
23
|
+
"express": "4.19.2",
|
|
24
24
|
"fs-extra": "11.1.1",
|
|
25
|
-
"json-stringify-safe": "5.0.1"
|
|
25
|
+
"json-stringify-safe": "5.0.1",
|
|
26
|
+
"rfdc": "^1.3.1"
|
|
26
27
|
}
|
|
27
28
|
}
|