node-red-contrib-mobius-flow-base 3.4.0-beta.2 → 3.5.2

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.
@@ -41,13 +41,7 @@ jobs:
41
41
  run: |
42
42
  npm install -g yarn
43
43
  yarn install
44
- tsc && mkdir -p dist/nodes
45
- cp src/nodes/*.js dist/nodes/
46
- cp src/nodes/*.html dist/nodes/
47
- cp -r src/nodes/icons dist/nodes/
48
- mkdir -p dist/nodes/common
49
- cp src/mobsearch.js dist/nodes/common/
50
- cp src/node-red-ui-funcs.js dist/
44
+ yarn build
51
45
  env:
52
46
  GH_PKG_READ_TOKEN: ${{ secrets.GH_PKG_READ_TOKEN }}
53
47
 
@@ -80,6 +80,11 @@ class EngineApiClient extends engine_api_calls_1.EngineApiCalls {
80
80
  this.tokens = response.data;
81
81
  this.loggedIn = true;
82
82
  console.log(`(${this.hostname}) -> Logged in to Mobius Engine API`);
83
+ // Clear any previous refresh loop so repeated logins don't stack intervals
84
+ if (this.refreshLoop) {
85
+ clearInterval(this.refreshLoop);
86
+ this.refreshLoop = null;
87
+ }
83
88
  this.refreshLoop = setInterval(this.refresh, 1000 * 60 * 5);
84
89
  this.EngineWebSocket.connectToWebSocket(this.tokens.token);
85
90
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { EventEmitter } from "stream";
2
3
  type WebSocketTopics = "authorize" | "subscribe" | "unsubscribe" | "connected" | "authorized" | "object-cov" | "resource-cov" | "service-status" | "broadcast-command" | "error" | "ping" | "pong";
3
4
  interface EngineWebSocketInterface {
@@ -27,6 +28,8 @@ export declare class EngineWebSocket extends EventEmitter implements EngineWebSo
27
28
  constructor(hostname: string, protocol: "http" | "https");
28
29
  connectToWebSocket: (token: string) => void;
29
30
  private sendWebSocketMessage;
31
+ private clearTimers;
32
+ private teardownSocket;
30
33
  private sendPingMessage;
31
34
  registerCallback: (topic: WebSocketTopics, callback: Function) => void;
32
35
  clearAllCallbacks: () => void;
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.EngineWebSocket = void 0;
7
7
  const stream_1 = require("stream");
8
8
  const ws_1 = __importDefault(require("ws"));
9
+ const maxPendingWebSocketMessages = 1000;
9
10
  class EngineWebSocket extends stream_1.EventEmitter {
10
- webSocket;
11
+ webSocket = null;
11
12
  pendingWebSocketMessages = [];
12
13
  pendingWebSocketMessageInterval = null;
13
14
  pingInterval = null;
@@ -24,16 +25,23 @@ class EngineWebSocket extends stream_1.EventEmitter {
24
25
  }
25
26
  connectToWebSocket = (token) => {
26
27
  this.authToken = token;
28
+ // Tear down any previous socket and timers so repeated logins/reconnects
29
+ // cannot accumulate sockets, ping intervals or reconnect intervals
30
+ this.teardownSocket();
27
31
  let webSocketProtocol = "wss";
28
32
  if (this.protocol === "http") {
29
33
  webSocketProtocol = "ws";
30
34
  }
31
- this.webSocket = new ws_1.default(`${webSocketProtocol}://${this.hostname}/api/engine/v1/mobius/subscribe/connect`);
32
- this.webSocket.onopen = () => {
35
+ const ws = new ws_1.default(`${webSocketProtocol}://${this.hostname}/api/engine/v1/mobius/subscribe/connect`);
36
+ this.webSocket = ws;
37
+ ws.onopen = () => {
38
+ if (this.webSocket !== ws) {
39
+ return;
40
+ }
33
41
  this.attemptReconnect = true;
34
42
  console.log(`(${this.hostname}) -> Engine API Subscription WebSocket Client Connected`);
43
+ this.clearTimers();
35
44
  this.pingInterval = setInterval(this.sendPingMessage, 30000);
36
- clearInterval(this.reconnectInterval);
37
45
  this.sendWebSocketMessage({
38
46
  topic: "authorize",
39
47
  payload: `Bearer ${this.authToken}`,
@@ -45,16 +53,14 @@ class EngineWebSocket extends stream_1.EventEmitter {
45
53
  }
46
54
  }, 1000);
47
55
  };
48
- this.webSocket.onclose = () => {
49
- console.log(`(${this.hostname}) -> Engine API Subscription WebSocket Client Closed`);
50
- if (this.pingInterval) {
51
- clearInterval(this.pingInterval);
56
+ ws.onclose = () => {
57
+ if (this.webSocket !== ws) {
58
+ // A newer socket has replaced this one; don't touch current state
59
+ return;
52
60
  }
61
+ console.log(`(${this.hostname}) -> Engine API Subscription WebSocket Client Closed`);
53
62
  this.webSocket = null;
54
- clearInterval(this.reconnectInterval);
55
- if (this.pendingWebSocketMessageInterval) {
56
- clearInterval(this.pendingWebSocketMessageInterval);
57
- }
63
+ this.clearTimers();
58
64
  if (this.attemptReconnect) {
59
65
  this.reconnectInterval = setInterval(() => {
60
66
  this.reconnect();
@@ -62,7 +68,10 @@ class EngineWebSocket extends stream_1.EventEmitter {
62
68
  }
63
69
  this.emit("disconnected");
64
70
  };
65
- this.webSocket.onmessage = (message) => {
71
+ ws.onmessage = (message) => {
72
+ if (this.webSocket !== ws) {
73
+ return;
74
+ }
66
75
  try {
67
76
  if (message.data) {
68
77
  const parsedMessage = JSON.parse(message.data);
@@ -73,7 +82,7 @@ class EngineWebSocket extends stream_1.EventEmitter {
73
82
  console.log(`(${this.hostname}) -> Error parsing message: ${e.code}, ${e.message ? e.message : ""}`);
74
83
  }
75
84
  };
76
- this.webSocket.on("error", (e) => {
85
+ ws.on("error", (e) => {
77
86
  if (e.code === "ECONNREFUSED") {
78
87
  this.emit("disconnected");
79
88
  }
@@ -93,6 +102,42 @@ class EngineWebSocket extends stream_1.EventEmitter {
93
102
  }
94
103
  else {
95
104
  this.pendingWebSocketMessages.push(message);
105
+ if (this.pendingWebSocketMessages.length > maxPendingWebSocketMessages) {
106
+ this.pendingWebSocketMessages.shift();
107
+ }
108
+ }
109
+ };
110
+ clearTimers = () => {
111
+ if (this.pingInterval) {
112
+ clearInterval(this.pingInterval);
113
+ this.pingInterval = null;
114
+ }
115
+ if (this.pendingWebSocketMessageInterval) {
116
+ clearInterval(this.pendingWebSocketMessageInterval);
117
+ this.pendingWebSocketMessageInterval = null;
118
+ }
119
+ if (this.reconnectInterval) {
120
+ clearInterval(this.reconnectInterval);
121
+ this.reconnectInterval = null;
122
+ }
123
+ };
124
+ teardownSocket = () => {
125
+ this.clearTimers();
126
+ const oldSocket = this.webSocket;
127
+ this.webSocket = null;
128
+ if (oldSocket) {
129
+ oldSocket.onopen = null;
130
+ oldSocket.onclose = null;
131
+ oldSocket.onmessage = null;
132
+ oldSocket.removeAllListeners();
133
+ // Swallow errors from the discarded socket so they can't crash the process
134
+ oldSocket.on("error", () => { });
135
+ try {
136
+ oldSocket.terminate();
137
+ }
138
+ catch {
139
+ // socket already closed
140
+ }
96
141
  }
97
142
  };
98
143
  sendPingMessage = () => {
@@ -136,14 +181,10 @@ class EngineWebSocket extends stream_1.EventEmitter {
136
181
  };
137
182
  killWebSocket = () => {
138
183
  this.attemptReconnect = false;
139
- if (this.reconnectInterval) {
140
- clearInterval(this.reconnectInterval);
141
- }
142
184
  this.registeredCallbacks = [];
143
185
  this.pendingWebSocketMessages = [];
144
- if (this.webSocket) {
145
- this.webSocket.close();
146
- }
186
+ this.teardownSocket();
187
+ this.emit("disconnected");
147
188
  console.log(`(${this.hostname}) -> Killing Engine API Subscription WebSocket Client`);
148
189
  };
149
190
  disconnectFromWebSocket = () => {
@@ -30,7 +30,7 @@ POSSIBILITY OF SUCH DAMAGE.
30
30
  <input class="input-append-left" type="text" id="node-config-input-host" placeholder="e.g. localhost" style="width: 70%;" >
31
31
  </div>
32
32
  <p style="text-align:center"> Ensure port number is included in host path if required</p>
33
- <p style="text-align:center"> e.g. <b>myHostname:8443</b></p>
33
+ <p style="text-align:center"> e.g. <b>myHostname:8442</b></p>
34
34
  <hr>
35
35
  <div class="form-row">
36
36
  <label for="node-config-input-username"><i class="fa fa-user"></i> Username</label>
@@ -49,25 +49,25 @@ POSSIBILITY OF SUCH DAMAGE.
49
49
  </script>
50
50
 
51
51
  <script type="text/javascript">
52
- hostLabel = 'test';
53
- RED.nodes.registerType('mobius connection', {
54
- category: 'config',
52
+ hostLabel = "test";
53
+ RED.nodes.registerType("mobius connection", {
54
+ category: "config",
55
55
  defaults: {
56
- host: { required: true, value: 'mobius:8443' },
57
- username: { required: true, value: '' },
58
- password: { required: true, value: '' },
56
+ host: { required: true, value: "mobius:8442" },
57
+ username: { required: true, value: "" },
58
+ password: { required: true, value: "" },
59
59
  secure: { required: true, value: false },
60
60
  },
61
61
  label: function () {
62
62
  return `${this.host}`;
63
63
  },
64
64
  oneditprepare: function () {
65
- $('#node-config-input-secure').change(function () {
66
- let secure = $('#node-config-input-secure').is(':checked');
65
+ $("#node-config-input-secure").change(function () {
66
+ let secure = $("#node-config-input-secure").is(":checked");
67
67
  if (secure) {
68
- document.getElementById('hostLabel').innerHTML = 'https://';
68
+ document.getElementById("hostLabel").innerHTML = "https://";
69
69
  } else {
70
- document.getElementById('hostLabel').innerHTML = 'http://';
70
+ document.getElementById("hostLabel").innerHTML = "http://";
71
71
  }
72
72
  });
73
73
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-mobius-flow-base",
3
- "version": "3.4.0-beta.2",
3
+ "version": "3.5.2",
4
4
  "description": "Node-RED base nodes for Mobius Flow",
5
5
  "main": "index.js",
6
6
  "types": "dist/index.d.ts",