@sockethub/server 5.0.0-alpha.3 → 5.0.0-alpha.4
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/coverage/tmp/coverage-39338-1663949520416-0.json +1 -0
- package/dist/bootstrap/init.js +2 -1
- package/dist/bootstrap/init.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +6 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/janitor.d.ts +27 -12
- package/dist/janitor.js +97 -66
- package/dist/janitor.js.map +1 -1
- package/dist/listener.d.ts +4 -1
- package/dist/listener.js +8 -5
- package/dist/listener.js.map +1 -1
- package/dist/middleware/create-activity-object.d.ts +3 -1
- package/dist/middleware/create-activity-object.js.map +1 -1
- package/dist/middleware/expand-activity-stream.d.ts +2 -1
- package/dist/middleware/expand-activity-stream.js +4 -1
- package/dist/middleware/expand-activity-stream.js.map +1 -1
- package/dist/middleware/expand-activity-stream.test.data.js +4 -4
- package/dist/middleware/expand-activity-stream.test.data.js.map +1 -1
- package/dist/middleware/store-credentials.d.ts +3 -3
- package/dist/middleware/store-credentials.js +2 -12
- package/dist/middleware/store-credentials.js.map +1 -1
- package/dist/middleware/validate.d.ts +2 -2
- package/dist/middleware/validate.js +1 -3
- package/dist/middleware/validate.js.map +1 -1
- package/dist/middleware/validate.test.data.js +5 -5
- package/dist/middleware/validate.test.data.js.map +1 -1
- package/dist/middleware.d.ts +14 -3
- package/dist/middleware.js +3 -1
- package/dist/middleware.js.map +1 -1
- package/dist/platform-instance.d.ts +11 -10
- package/dist/platform-instance.js +77 -62
- package/dist/platform-instance.js.map +1 -1
- package/dist/platform.js +93 -104
- package/dist/platform.js.map +1 -1
- package/dist/process-manager.js +7 -3
- package/dist/process-manager.js.map +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +1 -1
- package/dist/routes.js.map +1 -1
- package/dist/sockethub.d.ts +1 -22
- package/dist/sockethub.js +19 -26
- package/dist/sockethub.js.map +1 -1
- package/package.json +30 -36
- package/src/bootstrap/init.d.ts +17 -7
- package/src/bootstrap/init.ts +2 -1
- package/src/config.ts +9 -1
- package/src/index.ts +3 -2
- package/src/janitor.test.ts +189 -0
- package/src/janitor.ts +110 -65
- package/src/listener.ts +11 -7
- package/src/middleware/create-activity-object.ts +5 -2
- package/src/middleware/expand-activity-stream.test.data.ts +5 -5
- package/src/middleware/expand-activity-stream.test.ts +2 -2
- package/src/middleware/expand-activity-stream.ts +12 -7
- package/src/middleware/store-credentials.test.ts +4 -6
- package/src/middleware/store-credentials.ts +8 -14
- package/src/middleware/validate.test.data.ts +5 -5
- package/src/middleware/validate.ts +4 -6
- package/src/middleware.ts +28 -11
- package/src/platform-instance.test.ts +18 -18
- package/src/platform-instance.ts +98 -73
- package/src/platform.ts +79 -101
- package/src/process-manager.ts +1 -1
- package/src/routes.ts +3 -2
- package/src/sockethub.ts +29 -57
- package/views/examples/dummy.ejs +3 -1
- package/views/examples/shared.js +1 -1
- package/views/examples/xmpp.ejs +60 -34
- package/coverage/tmp/coverage-93126-1649152190997-0.json +0 -1
- package/dist/common.d.ts +0 -3
- package/dist/common.js +0 -20
- package/dist/common.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/store.d.ts +0 -5
- package/dist/store.js +0 -17
- package/dist/store.js.map +0 -1
- 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/queue.functional.test.js +0 -0
package/src/bootstrap/init.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
version:
|
|
3
|
-
platforms: Map<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
export interface IInitObject {
|
|
2
|
+
version: string;
|
|
3
|
+
platforms: Map<string, {
|
|
4
|
+
id: string;
|
|
5
|
+
moduleName: string;
|
|
6
|
+
config: {
|
|
7
|
+
persist?: boolean;
|
|
8
|
+
};
|
|
9
|
+
schemas: {
|
|
10
|
+
credentials?: object;
|
|
11
|
+
messages?: object;
|
|
12
|
+
};
|
|
13
|
+
version: string;
|
|
14
|
+
types: Array<string>;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
declare const init: IInitObject;
|
|
8
18
|
export default init;
|
package/src/bootstrap/init.ts
CHANGED
|
@@ -6,6 +6,7 @@ import platformLoad from './platforms';
|
|
|
6
6
|
const log = debug('sockethub:server:bootstrap:init');
|
|
7
7
|
log('running init routines');
|
|
8
8
|
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
9
10
|
const packageJSON = require('./../../package.json');
|
|
10
11
|
const platforms = platformLoad(config.get('platforms'));
|
|
11
12
|
|
|
@@ -40,7 +41,7 @@ if (config.get('info')) {
|
|
|
40
41
|
console.log('platforms: ' + Array.from(platforms.keys()).join(', '));
|
|
41
42
|
|
|
42
43
|
if (platforms.size > 0) {
|
|
43
|
-
for (
|
|
44
|
+
for (const platform of platforms.values()) {
|
|
44
45
|
console.log();
|
|
45
46
|
// eslint-disable-next-line security-node/detect-crlf
|
|
46
47
|
console.log(platform.moduleName);
|
package/src/config.ts
CHANGED
|
@@ -4,6 +4,13 @@ import * as fs from "fs";
|
|
|
4
4
|
|
|
5
5
|
const log = debug('sockethub:server:bootstrap:config');
|
|
6
6
|
|
|
7
|
+
export type ActivityStreamConfigOptions = {
|
|
8
|
+
warnOnUnknownObjectProperties?: boolean;
|
|
9
|
+
failOnUnknownObjectProperties?: boolean;
|
|
10
|
+
specialObjs?: object;
|
|
11
|
+
customProps?: object;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
export class Config {
|
|
8
15
|
constructor() {
|
|
9
16
|
log('initializing config');
|
|
@@ -58,6 +65,7 @@ export class Config {
|
|
|
58
65
|
nconf.set('examples:enabled', (examples ? true : nconf.get('examples:enabled')));
|
|
59
66
|
|
|
60
67
|
// load defaults
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
61
69
|
const defaults: object = require(__dirname + '/defaults.json');
|
|
62
70
|
nconf.defaults(defaults);
|
|
63
71
|
|
|
@@ -87,7 +95,7 @@ export class Config {
|
|
|
87
95
|
nconf.clear('redis:port');
|
|
88
96
|
}
|
|
89
97
|
}
|
|
90
|
-
get = (key: string):
|
|
98
|
+
get = (key: string): unknown => nconf.get(key);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
const config = new Config();
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import Sockethub from "./sockethub";
|
|
2
|
+
|
|
2
3
|
const sockethub = new Sockethub();
|
|
3
4
|
|
|
4
5
|
module.exports = async () => {
|
|
@@ -21,7 +22,7 @@ module.exports = async () => {
|
|
|
21
22
|
|
|
22
23
|
process.once('exit', async function () {
|
|
23
24
|
console.log('destroying all platform instances');
|
|
24
|
-
await sockethub.
|
|
25
|
+
await sockethub.shutdown();
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
sockethub.boot();
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import proxyquire from 'proxyquire';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import * as sinon from 'sinon';
|
|
4
|
+
|
|
5
|
+
let sockets = [
|
|
6
|
+
{ id: 'socket foo', emit: () => {} },
|
|
7
|
+
{ id: 'socket bar', emit: () => {} }
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
proxyquire.noPreserveCache();
|
|
11
|
+
proxyquire.noCallThru();
|
|
12
|
+
|
|
13
|
+
function getPlatformInstanceFake() {
|
|
14
|
+
return {
|
|
15
|
+
flaggedForTermination: false,
|
|
16
|
+
initialized: false,
|
|
17
|
+
global: false,
|
|
18
|
+
shutdown: sinon.stub(),
|
|
19
|
+
process: {
|
|
20
|
+
removeListener: sinon.stub()
|
|
21
|
+
},
|
|
22
|
+
sessions: new Set(['session foo', 'session bar']),
|
|
23
|
+
sessionCallbacks: {
|
|
24
|
+
'close': (() => new Map([
|
|
25
|
+
['session foo', function sessionFooClose() {}],
|
|
26
|
+
['session bar', function sessionBarClose() {}]
|
|
27
|
+
]))(),
|
|
28
|
+
'message': (() => new Map([
|
|
29
|
+
['session foo', function sessionFooMessage() {}],
|
|
30
|
+
['session bar', function sessionBarMessage() {}]
|
|
31
|
+
]))()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const cycleInterval = 10;
|
|
37
|
+
|
|
38
|
+
describe('Janitor', () => {
|
|
39
|
+
let sandbox, fetchSocketsFake, janitor;
|
|
40
|
+
|
|
41
|
+
beforeEach(function (done) {
|
|
42
|
+
this.timeout(3000);
|
|
43
|
+
sandbox = sinon.createSandbox();
|
|
44
|
+
fetchSocketsFake = sandbox.stub().returns(sockets);
|
|
45
|
+
const janitorMod = proxyquire('./janitor', {
|
|
46
|
+
listener: {
|
|
47
|
+
io: {
|
|
48
|
+
fetchSockets: fetchSocketsFake
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
janitor = janitorMod.default;
|
|
53
|
+
janitor.getSockets = fetchSocketsFake;
|
|
54
|
+
expect(janitor.cycleInterval).to.not.equal(cycleInterval);
|
|
55
|
+
janitor.cycleInterval = cycleInterval;
|
|
56
|
+
expect(janitor.cycleInterval).to.equal(cycleInterval);
|
|
57
|
+
janitor.start();
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
expect(janitor.cycleCount).to.equal(1);
|
|
60
|
+
done();
|
|
61
|
+
}, cycleInterval)
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach((done) => {
|
|
65
|
+
sandbox.reset();
|
|
66
|
+
janitor.stop();
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
done();
|
|
69
|
+
}, janitor.cycleInterval * 2)
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('runs cycle at every cycleInterval', (done) => {
|
|
73
|
+
const currCycleCount = janitor.cycleCount;
|
|
74
|
+
expect(currCycleCount).to.not.equal(0);
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
expect(janitor.cycleCount).to.equal(currCycleCount + 1);
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
expect(janitor.cycleCount).to.equal(currCycleCount + 2);
|
|
79
|
+
done();
|
|
80
|
+
}, cycleInterval);
|
|
81
|
+
}, cycleInterval);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('removeSessionCallbacks', () => {
|
|
85
|
+
it('removes session listeners and callbacks for a given platform', () => {
|
|
86
|
+
const pi = getPlatformInstanceFake();
|
|
87
|
+
const barMessage = pi.sessionCallbacks.message.get('session bar');
|
|
88
|
+
const barClose = pi.sessionCallbacks.close.get('session bar');
|
|
89
|
+
pi.flaggedForTermination = true;
|
|
90
|
+
janitor.removeSessionCallbacks(pi, 'session foo');
|
|
91
|
+
sinon.assert.calledTwice(pi.process.removeListener);
|
|
92
|
+
expect(pi.sessionCallbacks.message.get('session foo')).to.be.undefined;
|
|
93
|
+
expect(pi.sessionCallbacks.message.get('session bar')).to.equal(barMessage);
|
|
94
|
+
expect(pi.sessionCallbacks.close.get('session foo')).to.be.undefined;
|
|
95
|
+
expect(pi.sessionCallbacks.close.get('session bar')).to.equal(barClose);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('removeStaleSocketSessions', () => {
|
|
100
|
+
it('doesnt do anything if the socket is active and stop is not flagged', async () => {
|
|
101
|
+
const pi = getPlatformInstanceFake();
|
|
102
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
103
|
+
janitor.socketExists = sinon.stub().returns(true);
|
|
104
|
+
expect(janitor.stopTriggered).to.be.false;
|
|
105
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
106
|
+
sinon.assert.notCalled(janitor.removeSessionCallbacks);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('removes session if the socket is active and stop is flagged', async () => {
|
|
110
|
+
const pi = getPlatformInstanceFake();
|
|
111
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
112
|
+
janitor.socketExists = sinon.stub().returns(true);
|
|
113
|
+
janitor.stop();
|
|
114
|
+
expect(janitor.stopTriggered).to.be.true;
|
|
115
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
116
|
+
sinon.assert.calledTwice(janitor.removeSessionCallbacks);
|
|
117
|
+
sinon.assert.calledWith(janitor.removeSessionCallbacks, pi, 'session foo');
|
|
118
|
+
sinon.assert.calledWith(janitor.removeSessionCallbacks, pi, 'session bar');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('removes session if the socket is inactive', async () => {
|
|
122
|
+
const pi = getPlatformInstanceFake();
|
|
123
|
+
janitor.removeSessionCallbacks = sinon.stub();
|
|
124
|
+
janitor.socketExists = sinon.stub().onFirstCall().returns(false).onSecondCall().returns(true);
|
|
125
|
+
expect(janitor.stopTriggered).to.be.false;
|
|
126
|
+
await janitor.removeStaleSocketSessions(pi);
|
|
127
|
+
sinon.assert.calledOnce(janitor.removeSessionCallbacks);
|
|
128
|
+
sinon.assert.calledWith(janitor.removeSessionCallbacks, pi, 'session foo');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('performStaleCheck', () => {
|
|
133
|
+
it('removes flagged and uninitialized platform instances', async () => {
|
|
134
|
+
const pi = getPlatformInstanceFake();
|
|
135
|
+
pi.flaggedForTermination = true;
|
|
136
|
+
pi.initialized = false;
|
|
137
|
+
janitor.removeStaleSocketSessions = sandbox.stub();
|
|
138
|
+
janitor.removeStalePlatformInstance = sandbox.stub();
|
|
139
|
+
await janitor.performStaleCheck(pi);
|
|
140
|
+
sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
|
|
141
|
+
sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
|
|
142
|
+
expect(pi.flaggedForTermination).to.be.true;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('flags for termination when there are not sockets', async () => {
|
|
146
|
+
const pi = getPlatformInstanceFake();
|
|
147
|
+
pi.sessions = new Set();
|
|
148
|
+
pi.flaggedForTermination = false;
|
|
149
|
+
pi.initialized = true;
|
|
150
|
+
janitor.removeStaleSocketSessions = sandbox.stub();
|
|
151
|
+
janitor.removeStalePlatformInstance = sandbox.stub();
|
|
152
|
+
await janitor.performStaleCheck(pi);
|
|
153
|
+
sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
|
|
154
|
+
sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('removeStalePlatformInstance', () => {
|
|
159
|
+
it('flags stale platform', async () => {
|
|
160
|
+
const pi = getPlatformInstanceFake();
|
|
161
|
+
expect(pi.flaggedForTermination).to.be.false;
|
|
162
|
+
await janitor.removeStalePlatformInstance(pi);
|
|
163
|
+
sinon.assert.notCalled(pi.shutdown);
|
|
164
|
+
expect(pi.flaggedForTermination).to.be.true;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('removes flagged stale platform', async () => {
|
|
168
|
+
const pi = getPlatformInstanceFake();
|
|
169
|
+
pi.flaggedForTermination = true;
|
|
170
|
+
await janitor.removeStalePlatformInstance(pi);
|
|
171
|
+
sinon.assert.calledOnce(pi.shutdown);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('closes all connections when stop() is called', (done) => {
|
|
176
|
+
const prevCycle = janitor.cycleCount;
|
|
177
|
+
janitor.stop();
|
|
178
|
+
setTimeout(() => {
|
|
179
|
+
expect(janitor.cycleCount).to.equal(prevCycle);
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
expect(janitor.cycleCount).to.equal(prevCycle);
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
expect(janitor.cycleCount).to.equal(prevCycle);
|
|
184
|
+
done();
|
|
185
|
+
}, cycleInterval)
|
|
186
|
+
}, cycleInterval)
|
|
187
|
+
}, cycleInterval);
|
|
188
|
+
});
|
|
189
|
+
})
|
package/src/janitor.ts
CHANGED
|
@@ -5,85 +5,130 @@ import listener, { SocketInstance } from "./listener";
|
|
|
5
5
|
|
|
6
6
|
const rmLog = debug('sockethub:server:janitor');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
class Janitor {
|
|
9
|
+
cycleInterval = 15000;
|
|
10
|
+
cycleCount = 0; // a counter for each cycleInterval
|
|
11
|
+
reportCount = 0; // number of times a report is printed
|
|
12
|
+
protected stopTriggered = false;
|
|
13
|
+
protected sockets: Array<SocketInstance>;
|
|
14
|
+
private cycleRunning = false;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Every TICK the `Janitor` will compare existing platform instances with `socket.ids`
|
|
18
|
+
* (aka. `sessionId`). If all of the `sessionIds` associated with a `platformInstance` have
|
|
19
|
+
* no corresponding `socket.id` (from the `http.io` `socket.io` instance), then the
|
|
20
|
+
* `platformInstance` will first be flagged, if after the next `cycleInterval` the same
|
|
21
|
+
* state is determined, the platform will be destroyed (this allows for page
|
|
22
|
+
* refreshes not destroying platform instances)
|
|
23
|
+
*/
|
|
24
|
+
start(): void {
|
|
25
|
+
rmLog('initializing');
|
|
26
|
+
this.clean().then(() => {
|
|
27
|
+
rmLog('cleaning cycle started');
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async stop(): Promise<void> {
|
|
32
|
+
this.stopTriggered = true;
|
|
33
|
+
rmLog('stopping, terminating all sessions');
|
|
34
|
+
for (const platformInstance of platformInstances.values()) {
|
|
35
|
+
this.removeStaleSocketSessions(platformInstance);
|
|
36
|
+
await this.removeStalePlatformInstance(platformInstance);
|
|
37
|
+
await platformInstance.shutdown();
|
|
31
38
|
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private removeSessionCallbacks(platformInstance: PlatformInstance, sessionId: string): void {
|
|
42
|
+
for (const key in platformInstance.sessionCallbacks) {
|
|
43
|
+
platformInstance.process.removeListener(
|
|
44
|
+
key, platformInstance.sessionCallbacks[key].get(sessionId));
|
|
45
|
+
platformInstance.sessionCallbacks[key].delete(sessionId);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
platformInstance.flaggedForTermination = false;
|
|
49
|
+
private removeStaleSocketSessions(
|
|
50
|
+
platformInstance: PlatformInstance
|
|
51
|
+
): void {
|
|
52
|
+
for (const sessionId of platformInstance.sessions.values()) {
|
|
53
|
+
if ((this.stopTriggered) || (!this.socketExists(sessionId))) {
|
|
54
|
+
this.removeStaleSocketSession(platformInstance, sessionId);
|
|
40
55
|
}
|
|
41
56
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private removeStaleSocketSession(platformInstance: PlatformInstance, sessionId: string) {
|
|
60
|
+
rmLog(
|
|
61
|
+
`removing ${!this.stopTriggered ? 'stale ' : ''}socket session reference ${sessionId}
|
|
62
|
+
in platform instance ${platformInstance.id}`
|
|
63
|
+
);
|
|
64
|
+
platformInstance.sessions.delete(sessionId);
|
|
65
|
+
this.removeSessionCallbacks(platformInstance, sessionId);
|
|
66
|
+
}
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
68
|
+
private async removeStalePlatformInstance(platformInstance: PlatformInstance): Promise<void> {
|
|
69
|
+
if ((platformInstance.flaggedForTermination) || (this.stopTriggered)) {
|
|
70
|
+
rmLog(`terminating platform instance ${platformInstance.id}`);
|
|
71
|
+
await platformInstance.shutdown(); // terminate
|
|
72
|
+
} else {
|
|
73
|
+
rmLog(`flagging for termination platform instance ${platformInstance.id} ` +
|
|
74
|
+
`(no registered sessions found)`);
|
|
75
|
+
platformInstance.flaggedForTermination = true;
|
|
49
76
|
}
|
|
50
77
|
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
78
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
79
|
+
private socketExists(sessionId: string) {
|
|
80
|
+
for (const socket of this.sockets) {
|
|
81
|
+
if (socket.id === sessionId) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private async delay(ms): Promise<void> {
|
|
89
|
+
return await new Promise(resolve => setTimeout(resolve, ms));
|
|
57
90
|
}
|
|
58
|
-
}
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
private async getSockets(): Promise<Array<SocketInstance>> {
|
|
93
|
+
return await listener.io.fetchSockets();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private async performStaleCheck(platformInstance: PlatformInstance) {
|
|
97
|
+
this.removeStaleSocketSessions(platformInstance);
|
|
98
|
+
// Static platforms are for global use, not tied to a unique to session / eg. credentials)
|
|
99
|
+
if (!platformInstance.global) {
|
|
100
|
+
if ((!platformInstance.initialized) || (platformInstance.sessions.size === 0)) {
|
|
101
|
+
// either the platform failed to initialize, or there are no more sessions linked to it
|
|
102
|
+
await this.removeStalePlatformInstance(platformInstance);
|
|
103
|
+
}
|
|
68
104
|
}
|
|
69
105
|
}
|
|
70
|
-
}
|
|
71
106
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
private async clean(): Promise<void> {
|
|
108
|
+
if (this.stopTriggered) {
|
|
109
|
+
this.cycleRunning = false;
|
|
110
|
+
return;
|
|
111
|
+
} else if (this.cycleRunning) {
|
|
112
|
+
throw new Error('janitor cleanup cycle called while already running');
|
|
113
|
+
}
|
|
114
|
+
this.cycleRunning = true;
|
|
115
|
+
this.cycleCount++;
|
|
116
|
+
this.sockets = await this.getSockets();
|
|
117
|
+
|
|
118
|
+
if (!(this.cycleCount % 4)) {
|
|
119
|
+
this.reportCount++;
|
|
120
|
+
rmLog(
|
|
121
|
+
`socket sessions: ${this.sockets.length} platform instances: ${platformInstances.size}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const platformInstance of platformInstances.values()) {
|
|
125
|
+
await this.performStaleCheck(platformInstance);
|
|
126
|
+
}
|
|
127
|
+
this.cycleRunning = false;
|
|
128
|
+
await this.delay(this.cycleInterval);
|
|
129
|
+
return this.clean();
|
|
80
130
|
}
|
|
81
131
|
}
|
|
82
132
|
|
|
83
|
-
const janitor =
|
|
84
|
-
clean: janitorCycle,
|
|
85
|
-
alreadyCalled: alreadyCalled,
|
|
86
|
-
cycleCount: cycleCount,
|
|
87
|
-
reportCount: reportCount
|
|
88
|
-
};
|
|
133
|
+
const janitor = new Janitor();
|
|
89
134
|
export default janitor;
|
package/src/listener.ts
CHANGED
|
@@ -29,7 +29,7 @@ class Listener {
|
|
|
29
29
|
const app = Listener.initExpress();
|
|
30
30
|
this.http = new HTTP.Server(app);
|
|
31
31
|
this.io = new Server(this.http, {
|
|
32
|
-
path: config.get('sockethub:path'),
|
|
32
|
+
path: config.get('sockethub:path') as string,
|
|
33
33
|
cors: {
|
|
34
34
|
origin: "*",
|
|
35
35
|
methods: [ "GET", "POST" ]
|
|
@@ -40,14 +40,14 @@ class Listener {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
private startHttp() {
|
|
43
|
-
this.http.listen(config.get('sockethub:port'), config.get('sockethub:host'), () => {
|
|
43
|
+
this.http.listen(config.get('sockethub:port'), config.get('sockethub:host') as number, () => {
|
|
44
44
|
log(`sockethub listening on ` +
|
|
45
|
-
|
|
45
|
+
`${config.get('sockethub:host')}:${config.get('sockethub:port')}`);
|
|
46
46
|
});
|
|
47
|
-
}
|
|
47
|
+
}
|
|
48
48
|
|
|
49
49
|
private static initExpress() {
|
|
50
|
-
|
|
50
|
+
const app = express();
|
|
51
51
|
// templating engines
|
|
52
52
|
app.set('view engine', 'ejs');
|
|
53
53
|
// use bodyParser
|
|
@@ -59,15 +59,19 @@ class Listener {
|
|
|
59
59
|
|
|
60
60
|
const listener = new Listener();
|
|
61
61
|
|
|
62
|
+
interface EmitFunction {
|
|
63
|
+
(type: string, data: unknown)
|
|
64
|
+
}
|
|
65
|
+
|
|
62
66
|
export interface SocketInstance {
|
|
63
67
|
id: string;
|
|
64
|
-
emit:
|
|
68
|
+
emit: EmitFunction;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
export async function getSocket(sessionId: string): Promise<SocketInstance> {
|
|
68
72
|
const sockets: Array<SocketInstance> = await listener.io.fetchSockets();
|
|
69
73
|
return new Promise((resolve, reject) => {
|
|
70
|
-
for (
|
|
74
|
+
for (const socket of sockets) {
|
|
71
75
|
if (sessionId === socket.id) {
|
|
72
76
|
return resolve(socket);
|
|
73
77
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ActivityStreams from '@sockethub/activity-streams';
|
|
2
2
|
import config from "../config";
|
|
3
|
+
import {IActivityStream} from "@sockethub/schemas";
|
|
4
|
+
import {MiddlewareChainInterface} from "../middleware";
|
|
3
5
|
const activity = ActivityStreams(config.get('activity-streams:opts'));
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -7,7 +9,8 @@ const activity = ActivityStreams(config.get('activity-streams:opts'));
|
|
|
7
9
|
* @param obj
|
|
8
10
|
* @param done
|
|
9
11
|
*/
|
|
10
|
-
export default function createActivityObject(obj:
|
|
12
|
+
export default function createActivityObject(obj: IActivityStream,
|
|
13
|
+
done: MiddlewareChainInterface) {
|
|
11
14
|
activity.Object.create(obj);
|
|
12
15
|
done(obj);
|
|
13
|
-
}
|
|
16
|
+
}
|
|
@@ -68,10 +68,10 @@ export default [
|
|
|
68
68
|
"image":{
|
|
69
69
|
"height":250,
|
|
70
70
|
"mediaType":"image/jpeg",
|
|
71
|
-
"url":"
|
|
71
|
+
"url":"https://example.org/image.jpg",
|
|
72
72
|
"width":250
|
|
73
73
|
},
|
|
74
|
-
"url":"
|
|
74
|
+
"url":"https://sockethub.org"
|
|
75
75
|
},
|
|
76
76
|
"object":{
|
|
77
77
|
"type":"credentials",
|
|
@@ -340,10 +340,10 @@ export default [
|
|
|
340
340
|
"image":{
|
|
341
341
|
"height":250,
|
|
342
342
|
"mediaType":"image/jpeg",
|
|
343
|
-
"url":"
|
|
343
|
+
"url":"https://example.org/image.jpg",
|
|
344
344
|
"width":250
|
|
345
345
|
},
|
|
346
|
-
"url":"
|
|
346
|
+
"url":"https://sockethub.org"
|
|
347
347
|
},
|
|
348
348
|
"target":{
|
|
349
349
|
"id":"blah3",
|
|
@@ -362,4 +362,4 @@ export default [
|
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
|
-
];
|
|
365
|
+
];
|
|
@@ -30,10 +30,10 @@ const activity = ActivityStreams();
|
|
|
30
30
|
"image":{
|
|
31
31
|
"height":250,
|
|
32
32
|
"mediaType":"image/jpeg",
|
|
33
|
-
"url":"
|
|
33
|
+
"url":"https://example.org/image.jpg",
|
|
34
34
|
"width":250
|
|
35
35
|
},
|
|
36
|
-
"url":"
|
|
36
|
+
"url":"https://sockethub.org"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"id":"blah3",
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import ActivityStreams from '@sockethub/activity-streams';
|
|
2
|
-
import {
|
|
2
|
+
import {IActivityStream} from "@sockethub/schemas";
|
|
3
3
|
|
|
4
|
-
import config from "../config";
|
|
4
|
+
import config, {ActivityStreamConfigOptions} from "../config";
|
|
5
|
+
import {MiddlewareChainInterface} from "../middleware";
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
+
const asConfig = config.get('activity-streams:opts') as ActivityStreamConfigOptions;
|
|
8
|
+
asConfig.warnOnUnknownObjectProperties = false;
|
|
9
|
+
asConfig.failOnUnknownObjectProperties = false;
|
|
10
|
+
const activity = ActivityStreams(asConfig);
|
|
7
11
|
|
|
8
|
-
function ensureObject(msg:
|
|
12
|
+
function ensureObject(msg: unknown) {
|
|
9
13
|
return !((typeof msg !== 'object') || (Array.isArray(msg)));
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
export default function expandActivityStream(msg: IActivityStream,
|
|
16
|
+
export default function expandActivityStream(msg: IActivityStream,
|
|
17
|
+
done: MiddlewareChainInterface) {
|
|
13
18
|
if (! ensureObject(msg)) {
|
|
14
19
|
done(new Error(`message received is not an object.`));
|
|
15
20
|
} else if (typeof msg.context !== 'string') {
|
|
16
21
|
done(new Error('activity stream must contain a context property'));
|
|
17
|
-
}
|
|
22
|
+
} else if (typeof msg.type !== 'string') {
|
|
18
23
|
done(new Error('activity stream must contain a type property.'));
|
|
19
24
|
} else {
|
|
20
25
|
msg = activity.Stream(msg);
|
|
21
|
-
if (!
|
|
26
|
+
if (!msg.actor) {
|
|
22
27
|
done(new Error('activity stream must contain an actor property.'));
|
|
23
28
|
} else {
|
|
24
29
|
done(msg);
|
|
@@ -24,7 +24,7 @@ const creds = {
|
|
|
24
24
|
|
|
25
25
|
describe('Middleware: storeCredentials', () => {
|
|
26
26
|
let storeSuccess: any, storeError: any, saveErrorFake: any,
|
|
27
|
-
saveSuccessFake: any
|
|
27
|
+
saveSuccessFake: any;
|
|
28
28
|
|
|
29
29
|
beforeEach(() => {
|
|
30
30
|
storeSuccess = {
|
|
@@ -39,7 +39,6 @@ describe('Middleware: storeCredentials', () => {
|
|
|
39
39
|
};
|
|
40
40
|
saveSuccessFake = sinon.replace(storeSuccess, 'save', sinon.fake(storeSuccess.save));
|
|
41
41
|
saveErrorFake = sinon.replace(storeError, 'save', sinon.fake(storeError.save));
|
|
42
|
-
sessionLogStub = sinon.stub();
|
|
43
42
|
});
|
|
44
43
|
|
|
45
44
|
afterEach(() => {
|
|
@@ -47,14 +46,13 @@ describe('Middleware: storeCredentials', () => {
|
|
|
47
46
|
});
|
|
48
47
|
|
|
49
48
|
it('returns a middleware handler', () => {
|
|
50
|
-
const sc = storeCredentials(storeSuccess
|
|
49
|
+
const sc = storeCredentials(storeSuccess);
|
|
51
50
|
expect(typeof sc).to.equal('function');
|
|
52
51
|
expect(saveSuccessFake.callCount).to.equal(0);
|
|
53
|
-
expect(sessionLogStub.callCount).to.equal(0);
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
it('successfully store credentials', () => {
|
|
57
|
-
const sc = storeCredentials(storeSuccess
|
|
55
|
+
const sc = storeCredentials(storeSuccess);
|
|
58
56
|
sc(creds, (err: any) => {
|
|
59
57
|
expect(saveSuccessFake.callCount).to.equal(1);
|
|
60
58
|
expect(saveSuccessFake.firstArg).to.equal(creds.actor.id);
|
|
@@ -63,7 +61,7 @@ describe('Middleware: storeCredentials', () => {
|
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
it('handle error while storing credentials', () => {
|
|
66
|
-
const sc = storeCredentials(storeError
|
|
64
|
+
const sc = storeCredentials(storeError);
|
|
67
65
|
sc(creds, (err: any) => {
|
|
68
66
|
expect(saveErrorFake.callCount).to.equal(1);
|
|
69
67
|
expect(saveErrorFake.firstArg).to.equal(creds.actor.id);
|