@node-red/runtime 3.0.0-beta.3 → 3.0.1

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.
@@ -100,9 +100,13 @@ function buildDiagnosticReport(scope, callback) {
100
100
  version: os.version(),
101
101
  },
102
102
  runtime: {
103
+ version: runtime.settings.version,
103
104
  isStarted: runtime.isStarted(),
105
+ flows: {
106
+ state: runtime.flows && runtime.flows.state(),
107
+ started: runtime.flows && runtime.flows.started,
108
+ },
104
109
  modules: modules,
105
- version: runtime.settings.version,
106
110
  settings: {
107
111
  available: runtime.settings.available(),
108
112
  apiMaxLength: runtime.settings.apiMaxLength || "UNSET",
@@ -114,6 +118,11 @@ function buildDiagnosticReport(scope, callback) {
114
118
  flowFile: runtime.settings.flowFile || "UNSET",
115
119
  mqttReconnectTime: runtime.settings.mqttReconnectTime || "UNSET",
116
120
  serialReconnectTime: runtime.settings.serialReconnectTime || "UNSET",
121
+ socketReconnectTime: runtime.settings.socketReconnectTime || "UNSET",
122
+ socketTimeout: runtime.settings.socketTimeout || "UNSET",
123
+ tcpMsgQueueSize: runtime.settings.tcpMsgQueueSize || "UNSET",
124
+ inboundWebSocketTimeout: runtime.settings.inboundWebSocketTimeout || "UNSET",
125
+ runtimeState: runtime.settings.runtimeState || "UNSET",
117
126
 
118
127
  adminAuth: runtime.settings.adminAuth ? "SET" : "UNSET",
119
128
 
@@ -131,6 +140,7 @@ function buildDiagnosticReport(scope, callback) {
131
140
  uiHost: runtime.settings.uiHost ? "SET" : "UNSET",
132
141
  uiPort: runtime.settings.uiPort ? "SET" : "UNSET",
133
142
  userDir: runtime.settings.userDir ? "SET" : "UNSET",
143
+ nodesDir: runtime.settings.nodesDir && runtime.settings.nodesDir.length ? "SET" : "UNSET",
134
144
  }
135
145
  }
136
146
  }
package/lib/api/flows.js CHANGED
@@ -255,5 +255,82 @@ var api = module.exports = {
255
255
  }
256
256
  }
257
257
  return sendCredentials;
258
- }
258
+ },
259
+ /**
260
+ * Gets running state of runtime flows
261
+ * @param {Object} opts
262
+ * @param {User} opts.user - the user calling the api
263
+ * @param {Object} opts.req - the request to log (optional)
264
+ * @return {{state:string, started:boolean}} - the current run state of the flows
265
+ * @memberof @node-red/runtime_flows
266
+ */
267
+ getState: async function(opts) {
268
+ runtime.log.audit({event: "flows.getState"}, opts.req);
269
+ const result = {
270
+ state: runtime.flows.state()
271
+ }
272
+ return result;
273
+ },
274
+ /**
275
+ * Sets running state of runtime flows
276
+ * @param {Object} opts
277
+ * @param {Object} opts.req - the request to log (optional)
278
+ * @param {User} opts.user - the user calling the api
279
+ * @param {string} opts.state - the requested state. Valid values are "start" and "stop".
280
+ * @return {Promise<Flow>} - the active flow configuration
281
+ * @memberof @node-red/runtime_flows
282
+ */
283
+ setState: async function(opts) {
284
+ opts = opts || {};
285
+ const makeError = (error, errcode, statusCode) => {
286
+ const message = typeof error == "object" ? error.message : error
287
+ const err = typeof error == "object" ? error : new Error(message||"Unexpected Error")
288
+ err.status = err.status || statusCode || 400;
289
+ err.code = err.code || errcode || "unexpected_error"
290
+ runtime.log.audit({
291
+ event: "flows.setState",
292
+ state: opts.state || "",
293
+ error: errcode || "unexpected_error",
294
+ message: err.code
295
+ }, opts.req);
296
+ return err
297
+ }
298
+
299
+ const getState = () => {
300
+ return {
301
+ state: runtime.flows.state()
302
+ }
303
+ }
304
+
305
+ if(!runtime.settings.runtimeState || runtime.settings.runtimeState.enabled !== true) {
306
+ throw (makeError("Method Not Allowed", "not_allowed", 405))
307
+ }
308
+ switch (opts.state) {
309
+ case "start":
310
+ try {
311
+ try {
312
+ runtime.settings.set('runtimeFlowState', opts.state);
313
+ } catch(err) {}
314
+ if (runtime.settings.safeMode) {
315
+ delete runtime.settings.safeMode
316
+ }
317
+ await runtime.flows.startFlows("full")
318
+ return getState()
319
+ } catch (err) {
320
+ throw (makeError(err, err.code, 500))
321
+ }
322
+ case "stop":
323
+ try {
324
+ try {
325
+ runtime.settings.set('runtimeFlowState', opts.state);
326
+ } catch(err) {}
327
+ await runtime.flows.stopFlows("full")
328
+ return getState()
329
+ } catch (err) {
330
+ throw (makeError(err, err.code, 500))
331
+ }
332
+ default:
333
+ throw (makeError(`Cannot change flows runtime state to '${opts.state}'}`, "invalid_run_state", 400))
334
+ }
335
+ },
259
336
  }
@@ -91,7 +91,7 @@ var api = module.exports = {
91
91
  safeSettings.context = runtime.nodes.listContextStores();
92
92
  if (runtime.settings.editorTheme && runtime.settings.editorTheme.codeEditor) {
93
93
  safeSettings.codeEditor = runtime.settings.editorTheme.codeEditor || {};
94
- safeSettings.codeEditor.lib = safeSettings.codeEditor.lib || "ace";
94
+ safeSettings.codeEditor.lib = safeSettings.codeEditor.lib || "monaco";
95
95
  safeSettings.codeEditor.options = safeSettings.codeEditor.options || {};
96
96
  }
97
97
  safeSettings.libraries = runtime.library.getLibraries();
@@ -148,6 +148,18 @@ var api = module.exports = {
148
148
  enabled: (runtime.settings.diagnostics && runtime.settings.diagnostics.enabled === false) ? false : true,
149
149
  ui: (runtime.settings.diagnostics && runtime.settings.diagnostics.ui === false) ? false : true
150
150
  }
151
+ if(safeSettings.diagnostics.enabled === false) {
152
+ safeSettings.diagnostics.ui = false; // cannot have UI without endpoint
153
+ }
154
+
155
+ safeSettings.runtimeState = {
156
+ //unless runtimeState.ui and runtimeState.enabled are explicitly true, they will default to false.
157
+ enabled: !!runtime.settings.runtimeState && runtime.settings.runtimeState.enabled === true,
158
+ ui: !!runtime.settings.runtimeState && runtime.settings.runtimeState.ui === true
159
+ }
160
+ if(safeSettings.runtimeState.enabled !== true) {
161
+ safeSettings.runtimeState.ui = false; // cannot have UI without endpoint
162
+ }
151
163
 
152
164
  runtime.settings.exportNodeSettings(safeSettings);
153
165
  runtime.plugins.exportPluginSettings(safeSettings);
@@ -390,7 +390,6 @@ class Subflow extends Flow {
390
390
  }
391
391
  name = newName;
392
392
  }
393
-
394
393
 
395
394
  var parent = this.parent;
396
395
  if (parent) {
@@ -36,6 +36,8 @@ var activeFlowConfig = null;
36
36
 
37
37
  var activeFlows = {};
38
38
  var started = false;
39
+ var state = 'stop'
40
+
39
41
  var credentialsPendingReset = false;
40
42
 
41
43
  var activeNodesToFlow = {};
@@ -50,6 +52,7 @@ function init(runtime) {
50
52
  storage = runtime.storage;
51
53
  log = runtime.log;
52
54
  started = false;
55
+ state = 'stop';
53
56
  if (!typeEventRegistered) {
54
57
  events.on('type-registered',function(type) {
55
58
  if (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
@@ -214,19 +217,26 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
214
217
  // Flows are running (or should be)
215
218
 
216
219
  // Stop the active flows (according to deploy type and the diff)
217
- return stop(type,diff,muteLog).then(() => {
220
+ return stop(type,diff,muteLog,true).then(() => {
218
221
  // Once stopped, allow context to remove anything no longer needed
219
222
  return context.clean(activeFlowConfig)
220
223
  }).then(() => {
224
+ if (!isLoad) {
225
+ log.info(log._("nodes.flows.updated-flows"));
226
+ }
221
227
  // Start the active flows
222
- start(type,diff,muteLog).then(() => {
228
+ start(type,diff,muteLog,true).then(() => {
223
229
  events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
224
230
  });
225
231
  // Return the new revision asynchronously to the actual start
226
232
  return flowRevision;
227
233
  }).catch(function(err) { })
228
234
  } else {
235
+ if (!isLoad) {
236
+ log.info(log._("nodes.flows.updated-flows"));
237
+ }
229
238
  events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
239
+ return flowRevision;
230
240
  }
231
241
  });
232
242
  }
@@ -259,9 +269,10 @@ function getFlows() {
259
269
  return activeConfig;
260
270
  }
261
271
 
262
- async function start(type,diff,muteLog) {
263
- type = type||"full";
272
+ async function start(type,diff,muteLog,isDeploy) {
273
+ type = type || "full";
264
274
  started = true;
275
+ state = 'start'
265
276
  var i;
266
277
  // If there are missing types, report them, emit the necessary runtime event and return
267
278
  if (activeFlowConfig.missingTypes.length > 0) {
@@ -283,7 +294,7 @@ async function start(type,diff,muteLog) {
283
294
  log.info(log._("nodes.flows.missing-type-install-2"));
284
295
  log.info(" "+settings.userDir);
285
296
  }
286
- events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-types", type:"warning",text:"notification.warnings.missing-types",types:activeFlowConfig.missingTypes},retain:true});
297
+ 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});
287
298
  return;
288
299
  }
289
300
 
@@ -297,7 +308,7 @@ async function start(type,diff,muteLog) {
297
308
  missingModules.push({module:err[i].module.module, error: err[i].error.code || err[i].error.toString()})
298
309
  log.info(` - ${err[i].module.spec} [${err[i].error.code || "unknown_error"}]`);
299
310
  }
300
- events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});
311
+ events.emit("runtime-event",{id:"runtime-state",payload:{state: 'stop', error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});
301
312
  return;
302
313
  }
303
314
 
@@ -306,10 +317,23 @@ async function start(type,diff,muteLog) {
306
317
  log.info("*****************************************************************")
307
318
  log.info(log._("nodes.flows.safe-mode"));
308
319
  log.info("*****************************************************************")
309
- events.emit("runtime-event",{id:"runtime-state",payload:{error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
320
+ state = 'safe'
321
+ events.emit("runtime-event",{id:"runtime-state",payload:{state: 'safe', error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
310
322
  return;
311
323
  }
312
324
 
325
+ let runtimeState
326
+ try {
327
+ runtimeState = settings.get('runtimeFlowState') || 'start'
328
+ } catch (err) {}
329
+ if (runtimeState === 'stop') {
330
+ log.info(log._("nodes.flows.stopped-flows"));
331
+ events.emit("runtime-event",{id:"runtime-state",payload:{ state: 'stop', deploy:isDeploy },retain:true});
332
+ state = 'stop'
333
+ started = false
334
+ return
335
+ }
336
+
313
337
  if (!muteLog) {
314
338
  if (type !== "full") {
315
339
  log.info(log._("nodes.flows.starting-modified-"+type));
@@ -364,12 +388,10 @@ async function start(type,diff,muteLog) {
364
388
  }
365
389
  }
366
390
  }
367
- // Having created or updated all flows, now start them.
368
391
  for (id in activeFlows) {
369
392
  if (activeFlows.hasOwnProperty(id)) {
370
393
  try {
371
394
  activeFlows[id].start(diff);
372
-
373
395
  // Create a map of node id to flow id and also a subflowInstance lookup map
374
396
  var activeNodes = activeFlows[id].getActiveNodes();
375
397
  Object.keys(activeNodes).forEach(function(nid) {
@@ -387,7 +409,7 @@ async function start(type,diff,muteLog) {
387
409
  if (credentialsPendingReset === true) {
388
410
  credentialsPendingReset = false;
389
411
  } else {
390
- events.emit("runtime-event",{id:"runtime-state",retain:true});
412
+ events.emit("runtime-event",{id:"runtime-state", payload:{ state: 'start', deploy:isDeploy}, retain:true});
391
413
  }
392
414
 
393
415
  if (!muteLog) {
@@ -400,7 +422,7 @@ async function start(type,diff,muteLog) {
400
422
  return;
401
423
  }
402
424
 
403
- function stop(type,diff,muteLog) {
425
+ function stop(type,diff,muteLog,isDeploy) {
404
426
  if (!started) {
405
427
  return Promise.resolve();
406
428
  }
@@ -420,6 +442,7 @@ function stop(type,diff,muteLog) {
420
442
  }
421
443
  }
422
444
  started = false;
445
+ state = 'stop'
423
446
  var promises = [];
424
447
  var stopList;
425
448
  var removedList = diff.removed;
@@ -471,6 +494,8 @@ function stop(type,diff,muteLog) {
471
494
  }
472
495
  }
473
496
  events.emit("flows:stopped",{config: activeConfig, type: type, diff: diff});
497
+
498
+ events.emit("runtime-event",{ id:"runtime-state", payload:{ state: 'stop', deploy:isDeploy }, retain:true });
474
499
  // Deprecated event
475
500
  events.emit("nodes-stopped");
476
501
  });
@@ -790,7 +815,7 @@ module.exports = {
790
815
  stopFlows: stop,
791
816
 
792
817
  get started() { return started },
793
-
818
+ state: () => { return state },
794
819
  // handleError: handleError,
795
820
  // handleStatus: handleStatus,
796
821
 
package/lib/index.js CHANGED
@@ -215,7 +215,7 @@ function start() {
215
215
  }
216
216
  }
217
217
  return redNodes.loadContextsPlugin().then(function () {
218
- redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
218
+ redNodes.loadFlows().then(() => { redNodes.startFlows() }).catch(function(err) {});
219
219
  started = true;
220
220
  });
221
221
  });
@@ -399,12 +399,12 @@ module.exports = {
399
399
  * @memberof @node-red/runtime
400
400
  */
401
401
  version: externalAPI.version,
402
-
402
+
403
403
  /**
404
404
  * @memberof @node-red/diagnostics
405
405
  */
406
406
  diagnostics:externalAPI.diagnostics,
407
-
407
+
408
408
  storage: storage,
409
409
  events: events,
410
410
  hooks: hooks,
@@ -373,11 +373,13 @@ var api = module.exports = {
373
373
 
374
374
  }
375
375
  }
376
- for (cred in savedCredentials) {
377
- if (savedCredentials.hasOwnProperty(cred)) {
378
- if (!newCreds.hasOwnProperty(cred)) {
379
- delete savedCredentials[cred];
380
- dirty = true;
376
+ if (/^subflow(:|$)/.test(nodeType)) {
377
+ for (cred in savedCredentials) {
378
+ if (savedCredentials.hasOwnProperty(cred)) {
379
+ if (!newCreds.hasOwnProperty(cred)) {
380
+ delete savedCredentials[cred];
381
+ dirty = true;
382
+ }
381
383
  }
382
384
  }
383
385
  }
@@ -122,6 +122,7 @@
122
122
  "stopped-flows": "Stopped flows",
123
123
  "stopped": "Stopped",
124
124
  "stopping-error": "Error stopping node: __message__",
125
+ "updated-flows": "Updated flows",
125
126
  "added-flow": "Adding flow: __label__",
126
127
  "updated-flow": "Updated flow: __label__",
127
128
  "removed-flow": "Removed flow: __label__",
@@ -122,6 +122,7 @@
122
122
  "stopped-flows": "フローを停止しました",
123
123
  "stopped": "停止しました",
124
124
  "stopping-error": "ノードの停止に失敗しました: __message__",
125
+ "updated-flows": "フローを更新しました",
125
126
  "added-flow": "フローを追加します: __label__",
126
127
  "updated-flow": "フローを更新しました: __label__",
127
128
  "removed-flow": "フローを削除しました: __label__",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-red/runtime",
3
- "version": "3.0.0-beta.3",
3
+ "version": "3.0.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./lib/index.js",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  }
17
17
  ],
18
18
  "dependencies": {
19
- "@node-red/registry": "3.0.0-beta.3",
20
- "@node-red/util": "3.0.0-beta.3",
19
+ "@node-red/registry": "3.0.1",
20
+ "@node-red/util": "3.0.1",
21
21
  "async-mutex": "0.3.2",
22
22
  "clone": "2.1.2",
23
23
  "express": "4.18.1",