sockethub 4.0.0 → 5.0.0-alpha.0
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 +3 -3
- package/bin/sockethub +23 -19
- package/coverage/tmp/coverage-15699-1646422276150-0.json +1 -0
- package/dist/bootstrap/init.js +14 -4
- package/dist/bootstrap/init.js.map +1 -1
- package/dist/bootstrap/platforms.js +81 -69
- package/dist/common.js +10 -12
- package/dist/common.js.map +1 -1
- package/dist/config.js +4 -22
- package/dist/config.js.map +1 -1
- package/dist/crypto.js +7 -8
- package/dist/crypto.js.map +1 -1
- package/dist/janitor.js +14 -9
- package/dist/janitor.js.map +1 -1
- package/dist/middleware/create-activity-object.js +19 -0
- package/dist/middleware/create-activity-object.js.map +1 -0
- package/dist/middleware/expand-activity-stream.js +33 -0
- package/dist/middleware/expand-activity-stream.js.map +1 -0
- package/dist/middleware/expand-activity-stream.test.data.js +360 -0
- package/dist/middleware/expand-activity-stream.test.data.js.map +1 -0
- package/dist/middleware/store-credentials.js +19 -0
- package/dist/middleware/store-credentials.js.map +1 -0
- package/dist/middleware/validate.js +77 -0
- package/dist/middleware/validate.js.map +1 -0
- package/dist/middleware/validate.test.data.js +321 -0
- package/dist/middleware/validate.test.data.js.map +1 -0
- package/dist/middleware.js +47 -49
- package/dist/middleware.js.map +1 -1
- package/dist/platform-instance.js +84 -66
- package/dist/platform-instance.js.map +1 -1
- package/dist/platform.js +50 -25
- package/dist/platform.js.map +1 -1
- package/dist/process-manager.js +7 -4
- package/dist/process-manager.js.map +1 -1
- package/dist/routes.js +9 -7
- package/dist/routes.js.map +1 -1
- package/dist/serve.js +3 -3
- package/dist/serve.js.map +1 -1
- package/dist/sockethub-client.js +2604 -0
- package/dist/sockethub-client.js.map +1 -0
- package/dist/sockethub-client.min.js +2 -0
- package/dist/sockethub-client.min.js.LICENSE.txt +24 -0
- package/dist/sockethub.js +75 -58
- package/dist/sockethub.js.map +1 -1
- package/dist/store.js +17 -0
- package/dist/store.js.map +1 -0
- package/package.json +48 -44
- package/src/bootstrap/init.ts +16 -2
- package/src/bootstrap/platforms.js +14 -18
- package/src/common.test.ts +44 -33
- package/src/common.ts +9 -17
- package/src/config.test.ts +16 -38
- package/src/config.ts +1 -23
- package/src/crypto.test.ts +15 -17
- package/src/crypto.ts +4 -5
- package/src/janitor.ts +19 -12
- package/src/middleware/create-activity-object.test.ts +10 -0
- package/src/middleware/create-activity-object.ts +13 -0
- package/src/middleware/expand-activity-stream.test.data.ts +365 -0
- package/src/middleware/expand-activity-stream.test.ts +78 -0
- package/src/middleware/expand-activity-stream.ts +27 -0
- package/src/middleware/store-credentials.test.ts +72 -0
- package/src/middleware/store-credentials.ts +16 -0
- package/src/{validate.d.ts → middleware/validate.d.ts} +0 -0
- package/src/middleware/validate.test.data.ts +320 -0
- package/src/middleware/validate.test.ts +47 -0
- package/src/middleware/validate.ts +49 -0
- package/src/middleware.test.ts +148 -0
- package/src/middleware.ts +46 -51
- package/src/platform-instance.test.ts +224 -196
- package/src/platform-instance.ts +74 -58
- package/src/platform.ts +44 -24
- package/src/process-manager.ts +7 -4
- package/src/routes.test.ts +32 -17
- package/src/routes.ts +8 -6
- package/src/serve.ts +1 -1
- package/src/sockethub-client.test.ts +235 -0
- package/src/sockethub-client.ts +164 -0
- package/src/sockethub.ts +96 -93
- package/src/store.test.ts +26 -0
- package/src/store.ts +17 -0
- package/tsconfig.json +8 -8
- package/views/examples/dummy.ejs +7 -7
- package/views/examples/feeds.ejs +10 -10
- package/views/examples/irc.ejs +65 -59
- package/views/examples/shared.js +31 -29
- package/views/examples/xmpp.ejs +49 -58
- package/webpack.minified.config.js +14 -0
- package/webpack.normal.config.js +14 -0
- package/coverage/clover.xml +0 -190
- package/coverage/coverage-final.json +0 -6
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -79
- package/coverage/lcov-report/common.ts.html +0 -143
- package/coverage/lcov-report/config.ts.html +0 -359
- package/coverage/lcov-report/crypto.ts.html +0 -203
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -171
- package/coverage/lcov-report/platform-instance.ts.html +0 -740
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/routes.ts.html +0 -353
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -170
- package/coverage/lcov-report/src/common.ts.html +0 -143
- package/coverage/lcov-report/src/config.ts.html +0 -359
- package/coverage/lcov-report/src/crypto.ts.html +0 -182
- package/coverage/lcov-report/src/index.html +0 -156
- package/coverage/lcov-report/src/platform-instance.ts.html +0 -740
- package/coverage/lcov-report/src/routes/base.ts.html +0 -359
- package/coverage/lcov-report/src/routes/examples.ts.html +0 -311
- package/coverage/lcov-report/src/routes/index.html +0 -111
- package/coverage/lcov-report/src/services/http.ts.html +0 -239
- package/coverage/lcov-report/src/services/index.html +0 -111
- package/coverage/lcov-report/src/services/redis.ts.html +0 -140
- package/coverage/lcov-report/src/shared-resources.ts.html +0 -104
- package/coverage/lcov.info +0 -336
- package/coverage/tmp/coverage-70996-1620314182345-0.json +0 -1
- package/dist/bootstrap/platforms.js.map +0 -1
- package/dist/js/client.js +0 -177
- package/dist/js/client.js.map +0 -1
- package/dist/resource-manager.js +0 -66
- package/dist/resource-manager.js.map +0 -1
- package/dist/routes/base.js +0 -92
- package/dist/routes/base.js.map +0 -1
- package/dist/routes/examples.js +0 -93
- package/dist/routes/examples.js.map +0 -1
- package/dist/services/http.js +0 -68
- package/dist/services/http.js.map +0 -1
- package/dist/services/redis.js +0 -1
- package/dist/services/redis.js.map +0 -1
- package/dist/shared-resources.js +0 -11
- package/dist/shared-resources.js.map +0 -1
- package/dist/validate.js +0 -157
- package/dist/validate.js.map +0 -1
- package/jest.config.js +0 -18
- package/src/js/client.js +0 -190
- package/src/validate.ts +0 -147
- package/test/middleware-suite.js +0 -101
- package/test/validate-suite.js +0 -338
package/src/platform-instance.ts
CHANGED
|
@@ -3,13 +3,13 @@ import { join } from 'path';
|
|
|
3
3
|
import { debug, Debugger } from 'debug';
|
|
4
4
|
import Queue from 'bull';
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { ActivityObject, Job } from "./sockethub";
|
|
6
|
+
import config from "./config";
|
|
7
|
+
import { ActivityStream, JobDataDecrypted, JobEncrypted } from "./sockethub";
|
|
9
8
|
import { getSocket } from "./serve";
|
|
9
|
+
import { decryptJobData } from "./common";
|
|
10
10
|
|
|
11
11
|
// collection of platform instances, stored by `id`
|
|
12
|
-
export const platformInstances = new Map();
|
|
12
|
+
export const platformInstances = new Map<string, PlatformInstance>();
|
|
13
13
|
|
|
14
14
|
export interface PlatformInstanceParams {
|
|
15
15
|
identifier: string;
|
|
@@ -18,26 +18,32 @@ export interface PlatformInstanceParams {
|
|
|
18
18
|
actor?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
interface MessageFromPlatform extends Array<string|
|
|
22
|
-
0: string, 1:
|
|
21
|
+
interface MessageFromPlatform extends Array<string|ActivityStream>{
|
|
22
|
+
0: string, 1: ActivityStream, 2: string}
|
|
23
23
|
export interface MessageFromParent extends Array<string|any>{0: string, 1: any}
|
|
24
24
|
|
|
25
|
+
interface PlatformConfig {
|
|
26
|
+
persist?: boolean;
|
|
27
|
+
requireCredentials?: Array<string>;
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
export default class PlatformInstance {
|
|
27
31
|
flaggedForTermination: boolean = false;
|
|
28
32
|
id: string;
|
|
29
33
|
queue: Queue;
|
|
34
|
+
completedJobHandlers: Map<string, Function> = new Map();
|
|
35
|
+
config: PlatformConfig = {};
|
|
30
36
|
readonly name: string;
|
|
31
37
|
readonly process: ChildProcess;
|
|
32
38
|
readonly debug: Debugger;
|
|
33
39
|
readonly parentId: string;
|
|
34
40
|
readonly sessions: Set<string> = new Set();
|
|
35
|
-
|
|
36
|
-
private readonly actor?: string;
|
|
37
|
-
private readonly sessionCallbacks: object = {
|
|
41
|
+
readonly sessionCallbacks: object = {
|
|
38
42
|
'close': (() => new Map())(),
|
|
39
|
-
'message': (() => new Map())()
|
|
43
|
+
'message': (() => new Map())()
|
|
40
44
|
};
|
|
45
|
+
public readonly global?: boolean = false;
|
|
46
|
+
private readonly actor?: string;
|
|
41
47
|
|
|
42
48
|
constructor(params: PlatformInstanceParams) {
|
|
43
49
|
this.id = params.identifier;
|
|
@@ -57,37 +63,46 @@ export default class PlatformInstance {
|
|
|
57
63
|
/**
|
|
58
64
|
* Destroys all references to this platform instance, internal listeners and controlled processes
|
|
59
65
|
*/
|
|
60
|
-
public destroy() {
|
|
66
|
+
public async destroy() {
|
|
67
|
+
this.debug(`cleaning up`);
|
|
61
68
|
this.flaggedForTermination = true;
|
|
62
|
-
platformInstances.delete(this.id);
|
|
63
69
|
try {
|
|
64
|
-
this.queue.
|
|
70
|
+
await this.queue.removeAllListeners();
|
|
71
|
+
} catch (e) { }
|
|
72
|
+
try {
|
|
73
|
+
await this.queue.obliterate({ force: true });
|
|
65
74
|
} catch (e) { }
|
|
66
75
|
try {
|
|
67
|
-
this.
|
|
68
|
-
this.process.
|
|
69
|
-
this.process.
|
|
76
|
+
delete this.queue;
|
|
77
|
+
await this.process.removeAllListeners('close');
|
|
78
|
+
await this.process.unref();
|
|
79
|
+
await this.process.kill();
|
|
70
80
|
} catch (e) { }
|
|
81
|
+
platformInstances.delete(this.id);
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
/**
|
|
74
85
|
* When jobs are completed or failed, we prepare the results and send them to the client socket
|
|
75
86
|
*/
|
|
76
87
|
public initQueue(secret: string) {
|
|
77
|
-
this.queue = new Queue(this.parentId + this.id,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
this.queue = new Queue(this.parentId + this.id, { redis: config.get('redis') });
|
|
89
|
+
|
|
90
|
+
this.queue.on('global:completed', (jobId, resultString) => {
|
|
91
|
+
const result = resultString ? JSON.parse(resultString) : "";
|
|
92
|
+
this.queue.getJob(jobId).then(async (job: JobEncrypted) => {
|
|
93
|
+
await this.handleJobResult('completed', decryptJobData(job, secret), result);
|
|
94
|
+
await job.remove();
|
|
82
95
|
});
|
|
83
96
|
});
|
|
97
|
+
|
|
84
98
|
this.queue.on('global:error', (jobId, result) => {
|
|
85
99
|
this.debug("unknown queue error", jobId, result);
|
|
86
100
|
});
|
|
101
|
+
|
|
87
102
|
this.queue.on('global:failed', (jobId, result) => {
|
|
88
|
-
this.queue.getJob(jobId).then((job) => {
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
this.queue.getJob(jobId).then(async (job: JobEncrypted) => {
|
|
104
|
+
await this.handleJobResult('failed', decryptJobData(job, secret), result);
|
|
105
|
+
await job.remove();
|
|
91
106
|
});
|
|
92
107
|
});
|
|
93
108
|
}
|
|
@@ -111,54 +126,58 @@ export default class PlatformInstance {
|
|
|
111
126
|
* Sends a message to client (user), can be registered with an event emitted from the platform
|
|
112
127
|
* process.
|
|
113
128
|
* @param sessionId ID of the socket connection to send the message to
|
|
114
|
-
* @param type type of message to emit. 'message', 'completed', 'failed'
|
|
115
129
|
* @param msg ActivityStream object to send to client
|
|
116
130
|
*/
|
|
117
|
-
public sendToClient(sessionId: string,
|
|
131
|
+
public sendToClient(sessionId: string, msg: ActivityStream) {
|
|
118
132
|
getSocket(sessionId).then((socket) => {
|
|
119
133
|
try {
|
|
120
134
|
// this property should never be exposed externally
|
|
121
135
|
delete msg.sessionSecret;
|
|
122
136
|
} catch (e) {}
|
|
123
137
|
msg.context = this.name;
|
|
124
|
-
|
|
138
|
+
if ((msg.type === 'error') && (typeof msg.actor === 'undefined') && (this.actor)) {
|
|
139
|
+
// ensure an actor is present if not otherwise defined
|
|
140
|
+
msg.actor = { id: this.actor };
|
|
141
|
+
}
|
|
142
|
+
socket.emit('message', msg);
|
|
125
143
|
}, (err) => this.debug(`sendToClient ${err}`));
|
|
126
144
|
}
|
|
127
145
|
|
|
128
146
|
// send message to every connected socket associated with this platform instance.
|
|
129
|
-
private async broadcastToSharedPeers(sessionId: string, msg:
|
|
147
|
+
private async broadcastToSharedPeers(sessionId: string, msg: ActivityStream) {
|
|
130
148
|
for (let sid of this.sessions.values()) {
|
|
131
149
|
if (sid !== sessionId) {
|
|
132
150
|
this.debug(`broadcasting message to ${sid}`);
|
|
133
|
-
this.sendToClient(sid,
|
|
151
|
+
await this.sendToClient(sid, msg);
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
// handle job results coming in on the queue from platform instances
|
|
139
|
-
private handleJobResult(type: string,
|
|
140
|
-
this.debug(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
'@type': 'error',
|
|
150
|
-
content: result ? result : "job failed for unknown reason"
|
|
151
|
-
};
|
|
157
|
+
private async handleJobResult(type: string, jobData: JobDataDecrypted, result) {
|
|
158
|
+
this.debug(`${type} job ${jobData.title}`);
|
|
159
|
+
delete jobData.msg.sessionSecret;
|
|
160
|
+
let msg = jobData.msg;
|
|
161
|
+
if (type === 'failed') {
|
|
162
|
+
msg.error = result ? result : "job failed for unknown reason";
|
|
163
|
+
if ((this.config.persist) && (this.config.requireCredentials.includes(jobData.msg.type))) {
|
|
164
|
+
this.debug(`critical job type ${jobData.msg.type} failed, terminating platform instance`);
|
|
165
|
+
await this.destroy();
|
|
166
|
+
}
|
|
152
167
|
}
|
|
153
168
|
|
|
154
|
-
// send
|
|
155
|
-
this.
|
|
169
|
+
// send result to client
|
|
170
|
+
const callback = this.completedJobHandlers.get(jobData.title);
|
|
171
|
+
if (callback) {
|
|
172
|
+
callback(msg);
|
|
173
|
+
this.completedJobHandlers.delete(jobData.title);
|
|
174
|
+
} else {
|
|
175
|
+
await this.sendToClient(jobData.sessionId, msg);
|
|
176
|
+
}
|
|
156
177
|
|
|
157
178
|
// let all related peers know of result as an independent message
|
|
158
179
|
// (not as part of a job completion, or failure)
|
|
159
|
-
this.broadcastToSharedPeers(
|
|
160
|
-
|
|
161
|
-
job.remove();
|
|
180
|
+
await this.broadcastToSharedPeers(jobData.sessionId, msg);
|
|
162
181
|
}
|
|
163
182
|
|
|
164
183
|
/**
|
|
@@ -166,19 +185,16 @@ export default class PlatformInstance {
|
|
|
166
185
|
* @param sessionId
|
|
167
186
|
* @param errorMessage
|
|
168
187
|
*/
|
|
169
|
-
private reportError(sessionId: string, errorMessage: any) {
|
|
170
|
-
const errorObject:
|
|
188
|
+
private async reportError(sessionId: string, errorMessage: any) {
|
|
189
|
+
const errorObject: ActivityStream = {
|
|
171
190
|
context: this.name,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'@type': 'error',
|
|
176
|
-
content: errorMessage
|
|
177
|
-
}
|
|
191
|
+
type: 'error',
|
|
192
|
+
actor: { id: this.actor },
|
|
193
|
+
error: errorMessage
|
|
178
194
|
};
|
|
179
|
-
this.sendToClient(sessionId,
|
|
195
|
+
this.sendToClient(sessionId, errorObject);
|
|
180
196
|
this.sessions.clear();
|
|
181
|
-
this.destroy();
|
|
197
|
+
await this.destroy();
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
/**
|
|
@@ -211,7 +227,7 @@ export default class PlatformInstance {
|
|
|
211
227
|
this.reportError(sessionId, data[1]);
|
|
212
228
|
} else {
|
|
213
229
|
// treat like a message to clients
|
|
214
|
-
this.sendToClient(sessionId,
|
|
230
|
+
this.sendToClient(sessionId, data[1]);
|
|
215
231
|
}
|
|
216
232
|
}
|
|
217
233
|
};
|
package/src/platform.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
2
|
import hash from "object-hash";
|
|
3
|
-
import
|
|
4
|
-
import crypto from "./crypto";
|
|
3
|
+
import config from './config';
|
|
5
4
|
import Queue from 'bull';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { getPlatformId, decryptJobData } from "./common";
|
|
6
|
+
import { ActivityStream, JobDataDecrypted, JobEncrypted } from "./sockethub";
|
|
8
7
|
import { MessageFromParent } from './platform-instance';
|
|
8
|
+
import { getSessionStore } from "./store";
|
|
9
9
|
|
|
10
10
|
// command-line params
|
|
11
11
|
const parentId = process.argv[2];
|
|
@@ -23,7 +23,7 @@ logger(`platform handler initialized for ${platformName} ${identifier}`);
|
|
|
23
23
|
|
|
24
24
|
export interface PlatformSession {
|
|
25
25
|
debug(msg: string): void;
|
|
26
|
-
sendToClient(msg:
|
|
26
|
+
sendToClient(msg: ActivityStream, special?: string): void;
|
|
27
27
|
updateActor(credentials: object): void;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -31,7 +31,8 @@ export interface PlatformSession {
|
|
|
31
31
|
* Handle any uncaught errors from the platform by alerting the worker and shutting down.
|
|
32
32
|
*/
|
|
33
33
|
process.on('uncaughtException', (err) => {
|
|
34
|
-
console.log(
|
|
34
|
+
console.log('EXCEPTION IN PLATFORM');
|
|
35
|
+
// eslint-disable-next-line security-node/detect-crlf
|
|
35
36
|
console.log(err.stack);
|
|
36
37
|
process.send(['error', err.toString()]);
|
|
37
38
|
process.exit(1);
|
|
@@ -70,7 +71,7 @@ const platform = new PlatformModule(platformSession);
|
|
|
70
71
|
*/
|
|
71
72
|
function getCredentials(actorId: string, sessionId: string, sessionSecret: string, cb: Function) {
|
|
72
73
|
if (platform.config.noCredentials) { return cb(); }
|
|
73
|
-
const store
|
|
74
|
+
const store = getSessionStore(parentId, parentSecret1, sessionId, sessionSecret);
|
|
74
75
|
store.get(actorId, (err, credentials) => {
|
|
75
76
|
if (platform.config.persist) {
|
|
76
77
|
// don't continue if we don't get credentials
|
|
@@ -83,7 +84,7 @@ function getCredentials(actorId: string, sessionId: string, sessionSecret: strin
|
|
|
83
84
|
if (platform.credentialsHash) {
|
|
84
85
|
if (platform.credentialsHash !== hash(credentials.object)) {
|
|
85
86
|
return cb('provided credentials do not match existing platform instance for actor '
|
|
86
|
-
+ platform.actor
|
|
87
|
+
+ platform.actor.id);
|
|
87
88
|
}
|
|
88
89
|
} else {
|
|
89
90
|
platform.credentialsHash = hash(credentials.object);
|
|
@@ -97,29 +98,48 @@ function getCredentials(actorId: string, sessionId: string, sessionSecret: strin
|
|
|
97
98
|
* @param secret the secret used to decrypt credentials
|
|
98
99
|
*/
|
|
99
100
|
function getJobHandler(secret: string) {
|
|
100
|
-
return (job:
|
|
101
|
-
|
|
102
|
-
const jobLog = debug(`${loggerPrefix}:${
|
|
103
|
-
jobLog(`
|
|
104
|
-
const sessionSecret =
|
|
105
|
-
delete
|
|
106
|
-
|
|
107
|
-
return getCredentials(
|
|
101
|
+
return (job: JobEncrypted, done: Function) => {
|
|
102
|
+
const jobData: JobDataDecrypted = decryptJobData(job, secret);
|
|
103
|
+
const jobLog = debug(`${loggerPrefix}:${jobData.sessionId}`);
|
|
104
|
+
jobLog(`received ${jobData.title} ${jobData.msg.type}`);
|
|
105
|
+
const sessionSecret = jobData.msg.sessionSecret;
|
|
106
|
+
delete jobData.msg.sessionSecret;
|
|
107
|
+
|
|
108
|
+
return getCredentials(jobData.msg.actor.id, jobData.sessionId, sessionSecret,
|
|
108
109
|
(err, credentials) => {
|
|
109
110
|
if (err) { return done(new Error(err)); }
|
|
111
|
+
let jobCallbackCalled = false;
|
|
110
112
|
const doneCallback = (err, result) => {
|
|
113
|
+
if (jobCallbackCalled) { return; }
|
|
114
|
+
jobCallbackCalled = true;
|
|
111
115
|
if (err) {
|
|
112
|
-
|
|
116
|
+
jobLog(`errored ${jobData.title} ${jobData.msg.type}`);
|
|
117
|
+
let errMsg;
|
|
118
|
+
// some error objects (eg. TimeoutError) don't interoplate correctly to human-readable
|
|
119
|
+
// so we have to do this little dance
|
|
120
|
+
try {
|
|
121
|
+
errMsg = err.toString();
|
|
122
|
+
} catch (e) {
|
|
123
|
+
errMsg = err;
|
|
124
|
+
}
|
|
125
|
+
done(new Error(errMsg));
|
|
113
126
|
} else {
|
|
127
|
+
jobLog(`completed ${jobData.title} ${jobData.msg.type}`);
|
|
114
128
|
done(null, result);
|
|
115
129
|
}
|
|
116
130
|
};
|
|
117
131
|
if ((Array.isArray(platform.config.requireCredentials)) &&
|
|
118
|
-
(platform.config.requireCredentials.includes(
|
|
132
|
+
(platform.config.requireCredentials.includes(jobData.msg.type))) {
|
|
119
133
|
// add the credentials object if this method requires it
|
|
120
|
-
platform[
|
|
134
|
+
platform[jobData.msg.type](jobData.msg, credentials, doneCallback);
|
|
135
|
+
} else if (platform.config.persist) {
|
|
136
|
+
if (platform.initialized) {
|
|
137
|
+
platform[jobData.msg.type](jobData.msg, doneCallback);
|
|
138
|
+
} else {
|
|
139
|
+
done(new Error(`${jobData.msg.type} called on uninitialized platform`));
|
|
140
|
+
}
|
|
121
141
|
} else {
|
|
122
|
-
platform[
|
|
142
|
+
platform[jobData.msg.type](jobData.msg, doneCallback);
|
|
123
143
|
}
|
|
124
144
|
});
|
|
125
145
|
};
|
|
@@ -131,7 +151,7 @@ function getJobHandler(secret: string) {
|
|
|
131
151
|
* @param command string containing the type of command to be sent. 'message' or 'close'
|
|
132
152
|
*/
|
|
133
153
|
function getSendFunction(command: string) {
|
|
134
|
-
return function (msg:
|
|
154
|
+
return function (msg: ActivityStream, special?: string) {
|
|
135
155
|
process.send([command, msg, special]);
|
|
136
156
|
};
|
|
137
157
|
}
|
|
@@ -142,8 +162,8 @@ function getSendFunction(command: string) {
|
|
|
142
162
|
* @param credentials
|
|
143
163
|
*/
|
|
144
164
|
function updateActor(credentials) {
|
|
145
|
-
identifier = getPlatformId(platformName, credentials.actor
|
|
146
|
-
logger(`platform actor updated to ${credentials.actor
|
|
165
|
+
identifier = getPlatformId(platformName, credentials.actor.id);
|
|
166
|
+
logger(`platform actor updated to ${credentials.actor.id} identifier ${identifier}`);
|
|
147
167
|
logger = debug(`sockethub:platform:${identifier}`);
|
|
148
168
|
platform.credentialsHash = hash(credentials.object);
|
|
149
169
|
platform.debug = debug(`sockethub:platform:${platformName}:${identifier}`);
|
|
@@ -162,7 +182,7 @@ function startQueueListener(refresh: boolean = false) {
|
|
|
162
182
|
logger('start queue called multiple times, skipping');
|
|
163
183
|
return;
|
|
164
184
|
}
|
|
165
|
-
const queue = new Queue(parentId + identifier,
|
|
185
|
+
const queue = new Queue(parentId + identifier, { redis: config.get('redis') });
|
|
166
186
|
queueStarted = true;
|
|
167
187
|
logger('listening on the queue for incoming jobs');
|
|
168
188
|
queue.process(getJobHandler(secret));
|
package/src/process-manager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import init from './bootstrap/init';
|
|
2
|
-
import PlatformInstance, {
|
|
2
|
+
import PlatformInstance, {
|
|
3
|
+
platformInstances, PlatformInstanceParams, MessageFromParent } from "./platform-instance";
|
|
3
4
|
import { getPlatformId } from "./common";
|
|
4
|
-
import { MessageFromParent } from "./platform-instance";
|
|
5
5
|
|
|
6
6
|
class ProcessManager {
|
|
7
7
|
private readonly parentId: string;
|
|
@@ -16,14 +16,17 @@ class ProcessManager {
|
|
|
16
16
|
|
|
17
17
|
get(platform: string, actorId: string, sessionId?: string): PlatformInstance {
|
|
18
18
|
const platformDetails = init.platforms.get(platform);
|
|
19
|
+
let pi;
|
|
19
20
|
|
|
20
21
|
if (platformDetails.config.persist) {
|
|
21
22
|
// ensure process is started - one for each actor
|
|
22
|
-
|
|
23
|
+
pi = this.ensureProcess(platform, sessionId, actorId);
|
|
23
24
|
} else {
|
|
24
25
|
// ensure process is started - one for all jobs
|
|
25
|
-
|
|
26
|
+
pi = this.ensureProcess(platform);
|
|
26
27
|
}
|
|
28
|
+
pi.config = platformDetails.config;
|
|
29
|
+
return pi;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
private createPlatformInstance(identifier: string, platform: string,
|
package/src/routes.test.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import * as sinon from 'sinon';
|
|
1
3
|
import { existsSync } from "fs";
|
|
2
4
|
|
|
3
5
|
import routes, { basePaths, examplePaths, examplePages } from "./routes";
|
|
4
6
|
|
|
5
7
|
describe('routes/base', () => {
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
sinon.restore();
|
|
11
|
+
});
|
|
12
|
+
|
|
6
13
|
it('can find each of the base files it serves', () => {
|
|
7
14
|
Object.values(basePaths).forEach((fwd: string) => {
|
|
8
15
|
try {
|
|
9
|
-
expect(existsSync(fwd)).
|
|
16
|
+
expect(existsSync(fwd)).to.be.true;
|
|
10
17
|
} catch (e) {
|
|
11
18
|
throw new Error(`Unable to resolve path ${fwd}`);
|
|
12
19
|
}
|
|
@@ -16,7 +23,7 @@ describe('routes/base', () => {
|
|
|
16
23
|
it('can find each of the example files it serves', () => {
|
|
17
24
|
Object.values(examplePaths).forEach((fwd: string) => {
|
|
18
25
|
try {
|
|
19
|
-
expect(existsSync(fwd)).
|
|
26
|
+
expect(existsSync(fwd)).to.be.true;
|
|
20
27
|
} catch (e) {
|
|
21
28
|
throw new Error(`Unable to resolve path ${fwd}`);
|
|
22
29
|
}
|
|
@@ -26,7 +33,7 @@ describe('routes/base', () => {
|
|
|
26
33
|
it('can find each of the example page files it serves', () => {
|
|
27
34
|
Object.values(examplePages).forEach((fwd: string) => {
|
|
28
35
|
try {
|
|
29
|
-
expect(existsSync(fwd)).
|
|
36
|
+
expect(existsSync(fwd)).to.be.true;
|
|
30
37
|
} catch (e) {
|
|
31
38
|
throw new Error(`Unable to resolve path ${fwd}`);
|
|
32
39
|
}
|
|
@@ -34,20 +41,26 @@ describe('routes/base', () => {
|
|
|
34
41
|
});
|
|
35
42
|
|
|
36
43
|
it('adds base routes', () => {
|
|
37
|
-
|
|
38
|
-
get:
|
|
44
|
+
const app = {
|
|
45
|
+
get: sinon.spy()
|
|
39
46
|
};
|
|
40
|
-
routes.setup(app);
|
|
41
|
-
|
|
47
|
+
routes.setup(app, false);
|
|
48
|
+
sinon.assert.callCount(
|
|
49
|
+
app.get,
|
|
50
|
+
Object.keys(basePaths).length
|
|
51
|
+
);
|
|
42
52
|
});
|
|
43
53
|
|
|
44
54
|
it('adds base and example routes', () => {
|
|
45
|
-
|
|
46
|
-
get:
|
|
55
|
+
const app = {
|
|
56
|
+
get: sinon.spy()
|
|
47
57
|
};
|
|
48
58
|
routes.setup(app, true);
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
sinon.assert.callCount(
|
|
60
|
+
app.get,
|
|
61
|
+
Object.keys(basePaths).length
|
|
62
|
+
+ Object.keys(examplePaths).length
|
|
63
|
+
+ Object.keys(examplePages).length
|
|
51
64
|
);
|
|
52
65
|
});
|
|
53
66
|
|
|
@@ -63,12 +76,13 @@ describe('routes/base', () => {
|
|
|
63
76
|
function verifyPathRoutes(pathMap) {
|
|
64
77
|
Object.keys(pathMap).forEach((path) => {
|
|
65
78
|
const res = {
|
|
66
|
-
setHeader:
|
|
67
|
-
sendFile:
|
|
79
|
+
setHeader: sinon.spy(),
|
|
80
|
+
sendFile: sinon.spy()
|
|
68
81
|
};
|
|
82
|
+
expect(pathMap[path].endsWith('.ejs')).to.be.false;
|
|
69
83
|
routeHandlers[path]({url: path}, res);
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
sinon.assert.called(res.setHeader);
|
|
85
|
+
sinon.assert.calledWith(res.sendFile, pathMap[path]);
|
|
72
86
|
});
|
|
73
87
|
}
|
|
74
88
|
verifyPathRoutes(basePaths);
|
|
@@ -76,10 +90,11 @@ describe('routes/base', () => {
|
|
|
76
90
|
|
|
77
91
|
Object.keys(examplePages).forEach((path) => {
|
|
78
92
|
const res = {
|
|
79
|
-
render:
|
|
93
|
+
render: sinon.spy()
|
|
80
94
|
};
|
|
95
|
+
expect(examplePages[path].endsWith('.ejs')).to.be.true;
|
|
81
96
|
routeHandlers[path]({url: path}, res);
|
|
82
|
-
|
|
97
|
+
sinon.assert.called(res.render);
|
|
83
98
|
});
|
|
84
99
|
});
|
|
85
100
|
});
|
package/src/routes.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import config from "./config";
|
|
3
|
+
import debug from 'debug';
|
|
3
4
|
|
|
4
5
|
const debug_scope = process.env.DEBUG || '',
|
|
6
|
+
logger = debug('sockethub:routes'),
|
|
5
7
|
address = config.get('public:protocol') + '://' +
|
|
6
8
|
config.get('public:host') + ':' +
|
|
7
9
|
config.get('public:port') +
|
|
8
10
|
config.get('public:path');
|
|
9
11
|
|
|
10
12
|
export const basePaths = {
|
|
11
|
-
'/sockethub-client.js': path.resolve(`${__dirname}/
|
|
13
|
+
'/sockethub-client.js': path.resolve(`${__dirname}/../dist/sockethub-client.js`),
|
|
14
|
+
'/sockethub-client.min.js': path.resolve(`${__dirname}/../dist/sockethub-client.min.js`),
|
|
15
|
+
'/sockethub-client.js.map': path.resolve(`${__dirname}/../dist/sockethub-client.js.map`),
|
|
12
16
|
'/socket.io.js': path.resolve(`${__dirname}/../node_modules/socket.io/client-dist/socket.io.js`),
|
|
13
17
|
'/socket.io.js.map': path.resolve(
|
|
14
18
|
`${__dirname}/../node_modules/socket.io/client-dist/socket.io.js.map`),
|
|
15
|
-
// '/activity-streams.js':
|
|
16
|
-
// path.resolve(`${__dirname}/../node_modules/activity-streams/browser/activity-streams.js`),
|
|
17
|
-
'/activity-streams.min.js':
|
|
18
|
-
path.resolve(`${__dirname}/../node_modules/activity-streams/browser/activity-streams.min.js`),
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export const examplePaths = {
|
|
22
|
-
'/': path.resolve(`${__dirname}/../views/index.ejs`),
|
|
23
22
|
'/jquery.js': path.resolve(`${__dirname}/../node_modules/jquery/dist/jquery.min.js`),
|
|
24
23
|
'/jquery.min.map': path.resolve(`${__dirname}/../node_modules/jquery/dist/jquery.min.map`),
|
|
25
24
|
'/examples/shared.js': path.resolve(`${__dirname}/../views/examples/shared.js`)
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
export const examplePages = {
|
|
28
|
+
'/': path.resolve(`${__dirname}/../views/index.ejs`),
|
|
29
29
|
'/examples/dummy': path.resolve(`${__dirname}/../views/examples/dummy.ejs`),
|
|
30
30
|
'/examples/feeds': path.resolve(`${__dirname}/../views/examples/feeds.ejs`),
|
|
31
31
|
'/examples/irc': path.resolve(`${__dirname}/../views/examples/irc.ejs`),
|
|
@@ -42,6 +42,7 @@ function prepFileRoutes(pathMap) {
|
|
|
42
42
|
path: key
|
|
43
43
|
},
|
|
44
44
|
route: (req, res) => {
|
|
45
|
+
logger(`serving resource ${req.url}`);
|
|
45
46
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
46
47
|
res.sendFile(pathMap[req.url]);
|
|
47
48
|
}
|
|
@@ -60,6 +61,7 @@ Object.keys(examplePages).forEach((key) => {
|
|
|
60
61
|
path: key
|
|
61
62
|
},
|
|
62
63
|
route: (req, res) => {
|
|
64
|
+
logger(`serving page ${req.url}`);
|
|
63
65
|
res.render(examplePages[req.url], {
|
|
64
66
|
debug_scope: debug_scope,
|
|
65
67
|
address: address,
|