@sockethub/server 5.0.0-alpha.3 → 5.0.0-alpha.6
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/README.md +54 -60
- package/bin/sockethub +4 -3
- package/package.json +42 -60
- package/res/socket.io.js +4908 -0
- package/res/sockethub-client.js +602 -0
- package/res/sockethub-client.min.js +19 -0
- package/sockethub.config.example.json +2 -3
- package/src/bootstrap/init.d.ts +20 -7
- package/src/bootstrap/init.test.ts +211 -0
- package/src/bootstrap/init.ts +152 -75
- package/src/bootstrap/load-platforms.ts +151 -0
- package/src/config.test.ts +27 -22
- package/src/config.ts +82 -78
- package/src/defaults.json +24 -16
- package/src/index.ts +67 -27
- package/src/janitor.test.ts +211 -0
- package/src/janitor.ts +145 -77
- package/src/listener.ts +151 -57
- package/src/middleware/create-activity-object.test.ts +28 -8
- package/src/middleware/create-activity-object.ts +17 -8
- package/src/middleware/expand-activity-stream.test.data.ts +332 -346
- package/src/middleware/expand-activity-stream.test.ts +65 -66
- package/src/middleware/expand-activity-stream.ts +29 -19
- package/src/middleware/store-credentials.test.ts +74 -62
- package/src/middleware/store-credentials.ts +15 -15
- package/src/middleware/validate.test.data.ts +240 -242
- package/src/middleware/validate.test.ts +39 -78
- package/src/middleware/validate.ts +63 -39
- package/src/middleware.test.ts +168 -138
- package/src/middleware.ts +62 -43
- package/src/platform-instance.test.ts +507 -213
- package/src/platform-instance.ts +337 -219
- package/src/platform.test.ts +375 -0
- package/src/platform.ts +306 -139
- package/src/process-manager.ts +75 -51
- package/src/routes.test.ts +43 -89
- package/src/routes.ts +40 -77
- package/src/sentry.test.ts +106 -0
- package/src/sentry.ts +19 -0
- package/src/sockethub.ts +186 -153
- package/src/util.ts +5 -0
- package/coverage/tmp/coverage-93126-1649152190997-0.json +0 -1
- package/dist/bootstrap/init.d.ts +0 -18
- package/dist/bootstrap/init.js +0 -63
- package/dist/bootstrap/init.js.map +0 -1
- package/dist/bootstrap/platforms.js +0 -75
- package/dist/common.d.ts +0 -3
- package/dist/common.js +0 -20
- package/dist/common.js.map +0 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.js +0 -102
- package/dist/config.js.map +0 -1
- package/dist/crypto.d.ts +0 -10
- package/dist/crypto.js +0 -38
- package/dist/crypto.js.map +0 -1
- package/dist/defaults.json +0 -28
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -25
- package/dist/index.js.map +0 -1
- package/dist/janitor.d.ts +0 -15
- package/dist/janitor.js +0 -89
- package/dist/janitor.js.map +0 -1
- package/dist/listener.d.ts +0 -28
- package/dist/listener.js +0 -91
- package/dist/listener.js.map +0 -1
- package/dist/middleware/create-activity-object.d.ts +0 -6
- package/dist/middleware/create-activity-object.js +0 -19
- package/dist/middleware/create-activity-object.js.map +0 -1
- package/dist/middleware/expand-activity-stream.d.ts +0 -2
- package/dist/middleware/expand-activity-stream.js +0 -33
- package/dist/middleware/expand-activity-stream.js.map +0 -1
- package/dist/middleware/expand-activity-stream.test.data.d.ts +0 -480
- package/dist/middleware/expand-activity-stream.test.data.js +0 -360
- package/dist/middleware/expand-activity-stream.test.data.js.map +0 -1
- package/dist/middleware/store-credentials.d.ts +0 -3
- package/dist/middleware/store-credentials.js +0 -19
- package/dist/middleware/store-credentials.js.map +0 -1
- package/dist/middleware/validate.d.ts +0 -2
- package/dist/middleware/validate.js +0 -58
- package/dist/middleware/validate.js.map +0 -1
- package/dist/middleware/validate.test.data.d.ts +0 -532
- package/dist/middleware/validate.test.data.js +0 -263
- package/dist/middleware/validate.test.data.js.map +0 -1
- package/dist/middleware.d.ts +0 -10
- package/dist/middleware.js +0 -54
- package/dist/middleware.js.map +0 -1
- package/dist/platform-instance.d.ts +0 -77
- package/dist/platform-instance.js +0 -211
- package/dist/platform-instance.js.map +0 -1
- package/dist/platform.d.ts +0 -6
- package/dist/platform.js +0 -187
- package/dist/platform.js.map +0 -1
- package/dist/process-manager.d.ts +0 -11
- package/dist/process-manager.js +0 -78
- package/dist/process-manager.js.map +0 -1
- package/dist/routes.d.ts +0 -13
- package/dist/routes.js +0 -83
- package/dist/routes.js.map +0 -1
- package/dist/sockethub.d.ts +0 -39
- package/dist/sockethub.js +0 -119
- package/dist/sockethub.js.map +0 -1
- package/dist/store.d.ts +0 -5
- package/dist/store.js +0 -17
- package/dist/store.js.map +0 -1
- package/src/bootstrap/platforms.js +0 -75
- package/src/common.test.ts +0 -54
- package/src/common.ts +0 -14
- package/src/config.d.ts +0 -2
- package/src/crypto.d.ts +0 -5
- package/src/crypto.test.ts +0 -41
- package/src/crypto.ts +0 -41
- package/src/janitor.d.ts +0 -8
- package/src/middleware/validate.d.ts +0 -1
- package/src/middleware.d.ts +0 -21
- package/src/sockethub.d.ts +0 -1
- package/src/store.test.ts +0 -28
- package/src/store.ts +0 -17
- package/test/init-suite.js +0 -41
- package/test/queue.functional.test.js +0 -0
- package/test/sockethub-suite.js +0 -25
- package/tsconfig.json +0 -18
- package/views/examples/dummy.ejs +0 -93
- package/views/examples/feeds.ejs +0 -90
- package/views/examples/irc.ejs +0 -239
- package/views/examples/shared.js +0 -72
- package/views/examples/xmpp.ejs +0 -191
- package/views/index.ejs +0 -17
package/src/index.ts
CHANGED
|
@@ -1,28 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// eslint-disable-next-line security-node/detect-crlf
|
|
8
|
-
console.log(err.stack);
|
|
9
|
-
process.exit(1);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
process.once('SIGTERM', function () {
|
|
13
|
-
console.log('Received TERM signal. Exiting.');
|
|
14
|
-
process.exit(0);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
process.once('SIGINT', function () {
|
|
18
|
-
console.log('Received INT signal. Exiting.');
|
|
19
|
-
process.exit(0);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
process.once('exit', async function () {
|
|
23
|
-
console.log('destroying all platform instances');
|
|
24
|
-
await sockethub.removeAllPlatformInstances();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
sockethub.boot();
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
import config from "./config";
|
|
3
|
+
import Sockethub from "./sockethub";
|
|
4
|
+
|
|
5
|
+
let sentry: { readonly reportError: (err: Error) => void } = {
|
|
6
|
+
reportError: (err: Error) => {},
|
|
28
7
|
};
|
|
8
|
+
|
|
9
|
+
export async function server() {
|
|
10
|
+
let sockethub: Sockethub;
|
|
11
|
+
const log = debug("sockethub:init");
|
|
12
|
+
|
|
13
|
+
// conditionally initialize sentry
|
|
14
|
+
if (config.get("sentry:dsn")) {
|
|
15
|
+
log("initializing sentry");
|
|
16
|
+
sentry = await import("./sentry");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
sockethub = new Sockethub();
|
|
21
|
+
} catch (err) {
|
|
22
|
+
sentry.reportError(err);
|
|
23
|
+
console.error(err);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
process.once("uncaughtException", (err: Error) => {
|
|
28
|
+
console.error(
|
|
29
|
+
`${(new Date()).toUTCString()} UNCAUGHT EXCEPTION\n`,
|
|
30
|
+
err.stack,
|
|
31
|
+
);
|
|
32
|
+
sentry.reportError(err);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
process.once("unhandledRejection", (err: Error) => {
|
|
37
|
+
console.error(
|
|
38
|
+
`${(new Date()).toUTCString()} UNHANDLED REJECTION\n`,
|
|
39
|
+
err,
|
|
40
|
+
);
|
|
41
|
+
sentry.reportError(err);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
process.once("SIGTERM", () => {
|
|
46
|
+
console.log("Received TERM signal. Exiting.");
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
process.once("SIGINT", () => {
|
|
51
|
+
console.log("Received INT signal. Exiting.");
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
process.once("exit", async () => {
|
|
56
|
+
console.log("sockethub shutdown...");
|
|
57
|
+
await sockethub.shutdown();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await sockethub.boot();
|
|
63
|
+
} catch (err) {
|
|
64
|
+
sentry.reportError(err);
|
|
65
|
+
console.error(err);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import * as sinon from "sinon";
|
|
3
|
+
|
|
4
|
+
import { Janitor } from "./janitor.js";
|
|
5
|
+
|
|
6
|
+
const sockets = [
|
|
7
|
+
{ id: "socket foo", emit: () => {} },
|
|
8
|
+
{ id: "socket bar", emit: () => {} },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
function getPlatformInstanceFake() {
|
|
12
|
+
return {
|
|
13
|
+
flaggedForTermination: false,
|
|
14
|
+
config: {
|
|
15
|
+
initialized: false,
|
|
16
|
+
persist: true,
|
|
17
|
+
requireCredentials: ["foo", "bar"],
|
|
18
|
+
},
|
|
19
|
+
global: false,
|
|
20
|
+
shutdown: sinon.stub(),
|
|
21
|
+
process: {
|
|
22
|
+
removeListener: sinon.stub(),
|
|
23
|
+
},
|
|
24
|
+
sessions: new Set(["session foo", "session bar"]),
|
|
25
|
+
sessionCallbacks: {
|
|
26
|
+
close: (() =>
|
|
27
|
+
new Map([
|
|
28
|
+
["session foo", function sessionFooClose() {}],
|
|
29
|
+
["session bar", function sessionBarClose() {}],
|
|
30
|
+
]))(),
|
|
31
|
+
message: (() =>
|
|
32
|
+
new Map([
|
|
33
|
+
["session foo", function sessionFooMessage() {}],
|
|
34
|
+
["session bar", function sessionBarMessage() {}],
|
|
35
|
+
]))(),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cycleInterval = 10;
|
|
41
|
+
|
|
42
|
+
describe("Janitor", () => {
|
|
43
|
+
let sandbox, fetchSocketsFake, janitor;
|
|
44
|
+
|
|
45
|
+
beforeEach((done) => {
|
|
46
|
+
sandbox = sinon.createSandbox();
|
|
47
|
+
fetchSocketsFake = sandbox.stub().returns(sockets);
|
|
48
|
+
|
|
49
|
+
janitor = new Janitor();
|
|
50
|
+
janitor.getSockets = fetchSocketsFake;
|
|
51
|
+
expect(janitor.cycleInterval).not.toEqual(cycleInterval);
|
|
52
|
+
janitor.cycleInterval = cycleInterval;
|
|
53
|
+
expect(janitor.cycleInterval).toEqual(cycleInterval);
|
|
54
|
+
janitor.start();
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
expect(janitor.cycleCount).toEqual(1);
|
|
57
|
+
done();
|
|
58
|
+
}, cycleInterval);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach((done) => {
|
|
62
|
+
sandbox.reset();
|
|
63
|
+
janitor.stop();
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
done();
|
|
66
|
+
}, janitor.cycleInterval * 2);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("runs cycle at every cycleInterval", (done) => {
|
|
70
|
+
const currCycleCount = janitor.cycleCount;
|
|
71
|
+
expect(currCycleCount).not.toEqual(0);
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
expect(janitor.cycleCount).toEqual(currCycleCount + 1);
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
expect(janitor.cycleCount).toEqual(currCycleCount + 2);
|
|
76
|
+
done();
|
|
77
|
+
}, cycleInterval);
|
|
78
|
+
}, cycleInterval);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("removeSessionCallbacks", () => {
|
|
82
|
+
it("removes session listeners and callbacks for a given platform", () => {
|
|
83
|
+
const pi = getPlatformInstanceFake();
|
|
84
|
+
const barMessage = pi.sessionCallbacks.message.get("session bar");
|
|
85
|
+
const barClose = pi.sessionCallbacks.close.get("session bar");
|
|
86
|
+
pi.flaggedForTermination = true;
|
|
87
|
+
janitor.removeSessionCallbacks(pi, "session foo");
|
|
88
|
+
sinon.assert.calledTwice(pi.process.removeListener);
|
|
89
|
+
expect(
|
|
90
|
+
pi.sessionCallbacks.message.get("session foo"),
|
|
91
|
+
).toBeUndefined();
|
|
92
|
+
expect(pi.sessionCallbacks.message.get("session bar")).toEqual(
|
|
93
|
+
barMessage,
|
|
94
|
+
);
|
|
95
|
+
expect(
|
|
96
|
+
pi.sessionCallbacks.close.get("session foo"),
|
|
97
|
+
).toBeUndefined();
|
|
98
|
+
expect(pi.sessionCallbacks.close.get("session bar")).toEqual(
|
|
99
|
+
barClose,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("removeStaleSocketSessions", () => {
|
|
105
|
+
it("doesnt do anything if the socket is active and stop is not flagged", async () => {
|
|
106
|
+
const pi = getPlatformInstanceFake();
|
|
107
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
108
|
+
janitor.socketExists = sinon.stub().returns(true);
|
|
109
|
+
expect(janitor.stopTriggered).toBeFalse();
|
|
110
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
111
|
+
sinon.assert.notCalled(janitor.removeSessionCallbacks);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("removes session if the socket is active and stop is flagged", async () => {
|
|
115
|
+
const pi = getPlatformInstanceFake();
|
|
116
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
117
|
+
janitor.socketExists = sinon.stub().returns(true);
|
|
118
|
+
janitor.stop();
|
|
119
|
+
expect(janitor.stopTriggered).toBeTrue();
|
|
120
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
121
|
+
sinon.assert.calledTwice(janitor.removeSessionCallbacks);
|
|
122
|
+
sinon.assert.calledWith(
|
|
123
|
+
janitor.removeSessionCallbacks,
|
|
124
|
+
pi,
|
|
125
|
+
"session foo",
|
|
126
|
+
);
|
|
127
|
+
sinon.assert.calledWith(
|
|
128
|
+
janitor.removeSessionCallbacks,
|
|
129
|
+
pi,
|
|
130
|
+
"session bar",
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("removes session if the socket is inactive", async () => {
|
|
135
|
+
const pi = getPlatformInstanceFake();
|
|
136
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
137
|
+
janitor.socketExists = sinon
|
|
138
|
+
.stub()
|
|
139
|
+
.onFirstCall()
|
|
140
|
+
.returns(false)
|
|
141
|
+
.onSecondCall()
|
|
142
|
+
.returns(true);
|
|
143
|
+
expect(janitor.stopTriggered).toBeFalse();
|
|
144
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
145
|
+
sinon.assert.calledOnce(janitor.removeSessionCallbacks);
|
|
146
|
+
sinon.assert.calledWith(
|
|
147
|
+
janitor.removeSessionCallbacks,
|
|
148
|
+
pi,
|
|
149
|
+
"session foo",
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("performStaleCheck", () => {
|
|
155
|
+
it("removes flagged and uninitialized platform instances", async () => {
|
|
156
|
+
const pi = getPlatformInstanceFake();
|
|
157
|
+
pi.flaggedForTermination = true;
|
|
158
|
+
pi.config.initialized = false;
|
|
159
|
+
janitor.removeStaleSocketSessions = sandbox.stub();
|
|
160
|
+
janitor.removeStalePlatformInstance = sandbox.stub();
|
|
161
|
+
await janitor.performStaleCheck(pi);
|
|
162
|
+
sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
|
|
163
|
+
sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
|
|
164
|
+
expect(pi.flaggedForTermination).toBeTrue();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("flags for termination when there are not sockets", async () => {
|
|
168
|
+
const pi = getPlatformInstanceFake();
|
|
169
|
+
pi.sessions = new Set();
|
|
170
|
+
pi.flaggedForTermination = false;
|
|
171
|
+
pi.config.initialized = true;
|
|
172
|
+
janitor.removeStaleSocketSessions = sandbox.stub();
|
|
173
|
+
janitor.removeStalePlatformInstance = sandbox.stub();
|
|
174
|
+
await janitor.performStaleCheck(pi);
|
|
175
|
+
sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
|
|
176
|
+
sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("removeStalePlatformInstance", () => {
|
|
181
|
+
it("flags stale platform", async () => {
|
|
182
|
+
const pi = getPlatformInstanceFake();
|
|
183
|
+
expect(pi.flaggedForTermination).toBeFalse();
|
|
184
|
+
await janitor.removeStalePlatformInstance(pi);
|
|
185
|
+
sinon.assert.notCalled(pi.shutdown);
|
|
186
|
+
expect(pi.flaggedForTermination).toBeTrue();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("removes flagged stale platform", async () => {
|
|
190
|
+
const pi = getPlatformInstanceFake();
|
|
191
|
+
pi.flaggedForTermination = true;
|
|
192
|
+
await janitor.removeStalePlatformInstance(pi);
|
|
193
|
+
sinon.assert.calledOnce(pi.shutdown);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("closes all connections when stop() is called", (done) => {
|
|
198
|
+
const prevCycle = janitor.cycleCount;
|
|
199
|
+
janitor.stop();
|
|
200
|
+
setTimeout(() => {
|
|
201
|
+
expect(janitor.cycleCount).toEqual(prevCycle);
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
expect(janitor.cycleCount).toEqual(prevCycle);
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
expect(janitor.cycleCount).toEqual(prevCycle);
|
|
206
|
+
done();
|
|
207
|
+
}, cycleInterval);
|
|
208
|
+
}, cycleInterval);
|
|
209
|
+
}, cycleInterval);
|
|
210
|
+
});
|
|
211
|
+
});
|
package/src/janitor.ts
CHANGED
|
@@ -1,89 +1,157 @@
|
|
|
1
|
-
import debug from
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
rmLog(`socket sessions: ${sockets.length} platform instances: ${platformInstances.size}`);
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
|
|
3
|
+
import listener, { type SocketInstance } from "./listener.js";
|
|
4
|
+
import type PlatformInstance from "./platform-instance.js";
|
|
5
|
+
import { platformInstances } from "./platform-instance.js";
|
|
6
|
+
|
|
7
|
+
const rmLog = debug("sockethub:server:janitor");
|
|
8
|
+
|
|
9
|
+
export class Janitor {
|
|
10
|
+
cycleInterval = 15000;
|
|
11
|
+
cycleCount = 0; // a counter for each cycleInterval
|
|
12
|
+
reportCount = 0; // number of times a report is printed
|
|
13
|
+
protected stopTriggered = false;
|
|
14
|
+
protected sockets: Array<SocketInstance>;
|
|
15
|
+
private cycleRunning = false;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Every TICK the `Janitor` will compare existing platform instances with `socket.ids`
|
|
19
|
+
* (aka. `sessionId`). If all the `sessionIds` associated with a `platformInstance` have
|
|
20
|
+
* no corresponding `socket.id` (from the `http.io` `socket.io` instance), then the
|
|
21
|
+
* `platformInstance` will first be flagged, if after the next `cycleInterval` the same
|
|
22
|
+
* state is determined, the platform will be destroyed (this allows for page
|
|
23
|
+
* refreshes not destroying platform instances)
|
|
24
|
+
*/
|
|
25
|
+
start(): void {
|
|
26
|
+
rmLog("initializing");
|
|
27
|
+
this.clean().then(() => {
|
|
28
|
+
rmLog("cleaning cycle started");
|
|
29
|
+
});
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
async stop(): Promise<void> {
|
|
33
|
+
this.stopTriggered = true;
|
|
34
|
+
rmLog("stopping, terminating all sessions");
|
|
35
|
+
for (const platformInstance of platformInstances.values()) {
|
|
36
|
+
this.removeStaleSocketSessions(platformInstance);
|
|
37
|
+
await this.removeStalePlatformInstance(platformInstance);
|
|
38
|
+
await platformInstance.shutdown();
|
|
39
|
+
}
|
|
41
40
|
}
|
|
42
|
-
}, TICK);
|
|
43
|
-
}
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
private removeSessionCallbacks(
|
|
43
|
+
platformInstance: PlatformInstance,
|
|
44
|
+
sessionId: string,
|
|
45
|
+
): void {
|
|
46
|
+
for (const key in platformInstance.sessionCallbacks) {
|
|
47
|
+
platformInstance.process.removeListener(
|
|
48
|
+
key,
|
|
49
|
+
platformInstance.sessionCallbacks[key].get(sessionId),
|
|
50
|
+
);
|
|
51
|
+
platformInstance.sessionCallbacks[key].delete(sessionId);
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
private removeStaleSocketSessions(
|
|
56
|
+
platformInstance: PlatformInstance,
|
|
57
|
+
): void {
|
|
58
|
+
for (const sessionId of platformInstance.sessions.values()) {
|
|
59
|
+
if (this.stopTriggered || !this.socketExists(sessionId)) {
|
|
60
|
+
this.removeStaleSocketSession(platformInstance, sessionId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
private removeStaleSocketSession(
|
|
66
|
+
platformInstance: PlatformInstance,
|
|
67
|
+
sessionId: string,
|
|
68
|
+
) {
|
|
69
|
+
rmLog(
|
|
70
|
+
`removing ${
|
|
71
|
+
!this.stopTriggered ? "stale " : ""
|
|
72
|
+
}socket session reference ${sessionId}
|
|
73
|
+
in platform instance ${platformInstance.id}`,
|
|
74
|
+
);
|
|
75
|
+
platformInstance.sessions.delete(sessionId);
|
|
76
|
+
this.removeSessionCallbacks(platformInstance, sessionId);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async removeStalePlatformInstance(
|
|
80
|
+
platformInstance: PlatformInstance,
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
if (platformInstance.flaggedForTermination || this.stopTriggered) {
|
|
83
|
+
rmLog(`terminating platform instance ${platformInstance.id}`);
|
|
84
|
+
await platformInstance.shutdown(); // terminate
|
|
85
|
+
} else {
|
|
86
|
+
rmLog(
|
|
87
|
+
`flagging for termination platform instance ${platformInstance.id} (no registered sessions found)`,
|
|
88
|
+
);
|
|
89
|
+
platformInstance.flaggedForTermination = true;
|
|
90
|
+
}
|
|
68
91
|
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
92
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
private socketExists(sessionId: string) {
|
|
94
|
+
for (const socket of this.sockets) {
|
|
95
|
+
if (socket.id === sessionId) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async delay(ms: number): Promise<void> {
|
|
103
|
+
return await new Promise((resolve) => setTimeout(resolve, ms));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private async getSockets(): Promise<Array<SocketInstance>> {
|
|
107
|
+
return listener.io.fetchSockets();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async performStaleCheck(platformInstance: PlatformInstance) {
|
|
111
|
+
this.removeStaleSocketSessions(platformInstance);
|
|
112
|
+
// Static platforms are for global use, not tied to a unique to session
|
|
113
|
+
// (e.g. a stateful platform where credentials are supplied)
|
|
114
|
+
if (!platformInstance.global) {
|
|
115
|
+
if (
|
|
116
|
+
(platformInstance.config.persist &&
|
|
117
|
+
!platformInstance.config.initialized) ||
|
|
118
|
+
platformInstance.sessions.size === 0
|
|
119
|
+
) {
|
|
120
|
+
// either the platform failed to initialize, or there are no more sessions linked to it
|
|
121
|
+
await this.removeStalePlatformInstance(platformInstance);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async clean(): Promise<void> {
|
|
127
|
+
if (this.stopTriggered) {
|
|
128
|
+
this.cycleRunning = false;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (this.cycleRunning) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
"janitor cleanup cycle called while already running",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
this.cycleRunning = true;
|
|
137
|
+
this.cycleCount++;
|
|
138
|
+
this.sockets = await this.getSockets();
|
|
139
|
+
|
|
140
|
+
if (!(this.cycleCount % 4)) {
|
|
141
|
+
this.reportCount++;
|
|
142
|
+
rmLog(
|
|
143
|
+
`socket sessions: ${this.sockets.length} platform instances: ${platformInstances.size}`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const platformInstance of platformInstances.values()) {
|
|
148
|
+
await this.performStaleCheck(platformInstance);
|
|
149
|
+
}
|
|
150
|
+
this.cycleRunning = false;
|
|
151
|
+
await this.delay(this.cycleInterval);
|
|
152
|
+
return this.clean();
|
|
153
|
+
}
|
|
81
154
|
}
|
|
82
155
|
|
|
83
|
-
const janitor =
|
|
84
|
-
clean: janitorCycle,
|
|
85
|
-
alreadyCalled: alreadyCalled,
|
|
86
|
-
cycleCount: cycleCount,
|
|
87
|
-
reportCount: reportCount
|
|
88
|
-
};
|
|
156
|
+
const janitor = new Janitor();
|
|
89
157
|
export default janitor;
|