@rvoh/psychic-websockets 3.0.0-alpha.1 → 3.0.0-alpha.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.
@@ -30,22 +30,46 @@ export default class Cable {
30
30
  cors: config.psychicApp.corsOptions,
31
31
  ...config.socketioOptions,
32
32
  });
33
+ this.attachHealthCheckRoute();
33
34
  }
34
35
  buildHttpServer() {
35
36
  const wsApp = PsychicAppWebsockets.getOrFail();
36
37
  const sslCredentials = wsApp.psychicApp.sslCredentials;
37
- if (sslCredentials?.key && sslCredentials?.cert) {
38
- return https.createServer({
38
+ return sslCredentials?.key && sslCredentials?.cert
39
+ ? https.createServer({
39
40
  key: fs.readFileSync(sslCredentials.key),
40
41
  cert: fs.readFileSync(sslCredentials.cert),
41
42
  ca: sslCredentials.ca?.map(filePath => fs.readFileSync(filePath)),
42
43
  rejectUnauthorized: sslCredentials?.rejectUnauthorized,
43
44
  ...wsApp.psychicApp.httpServerOptions,
44
- });
45
- }
46
- else {
47
- return http.createServer(wsApp.psychicApp.httpServerOptions);
48
- }
45
+ })
46
+ : http.createServer(wsApp.psychicApp.httpServerOptions);
47
+ }
48
+ attachHealthCheckRoute() {
49
+ this.httpServer.on('request', (req, res) => {
50
+ const opts = PsychicAppWebsockets.getOrFail().healthCheckOptions;
51
+ if (opts === null) {
52
+ res.writeHead(404);
53
+ res.end();
54
+ return;
55
+ }
56
+ const sanitizedPath = `/${opts.path.replace(/^\//, '')}`;
57
+ if (req.url === sanitizedPath && req.method === opts.method) {
58
+ if (opts.body === null) {
59
+ res.writeHead(200);
60
+ res.end();
61
+ }
62
+ else {
63
+ res.statusCode = 200;
64
+ res.setHeader('Content-Type', 'text/plain');
65
+ res.end(opts.body);
66
+ }
67
+ }
68
+ else {
69
+ res.writeHead(404);
70
+ res.end();
71
+ }
72
+ });
49
73
  }
50
74
  /**
51
75
  * builds an http server and a socket.io server, binding to redis
@@ -43,6 +43,17 @@ export default class PsychicAppWebsockets {
43
43
  get hooks() {
44
44
  return this._hooks;
45
45
  }
46
+ defaultHealthCheckOptions = {
47
+ path: '/healthcheck',
48
+ method: 'GET',
49
+ body: null,
50
+ };
51
+ _healthCheckOptions = {
52
+ ...this.defaultHealthCheckOptions,
53
+ };
54
+ get healthCheckOptions() {
55
+ return this._healthCheckOptions;
56
+ }
46
57
  on(hookEventType, cb) {
47
58
  switch (hookEventType) {
48
59
  case 'ws:start':
@@ -63,6 +74,17 @@ export default class PsychicAppWebsockets {
63
74
  this._connection = value;
64
75
  this._subConnection = value?.duplicate();
65
76
  break;
77
+ case 'healthCheck':
78
+ if (value === null) {
79
+ this._healthCheckOptions = null;
80
+ }
81
+ else {
82
+ this._healthCheckOptions = {
83
+ ...this.defaultHealthCheckOptions,
84
+ ...value,
85
+ };
86
+ }
87
+ break;
66
88
  case 'socketio':
67
89
  this._socketioOptions = value;
68
90
  break;
@@ -30,22 +30,46 @@ export default class Cable {
30
30
  cors: config.psychicApp.corsOptions,
31
31
  ...config.socketioOptions,
32
32
  });
33
+ this.attachHealthCheckRoute();
33
34
  }
34
35
  buildHttpServer() {
35
36
  const wsApp = PsychicAppWebsockets.getOrFail();
36
37
  const sslCredentials = wsApp.psychicApp.sslCredentials;
37
- if (sslCredentials?.key && sslCredentials?.cert) {
38
- return https.createServer({
38
+ return sslCredentials?.key && sslCredentials?.cert
39
+ ? https.createServer({
39
40
  key: fs.readFileSync(sslCredentials.key),
40
41
  cert: fs.readFileSync(sslCredentials.cert),
41
42
  ca: sslCredentials.ca?.map(filePath => fs.readFileSync(filePath)),
42
43
  rejectUnauthorized: sslCredentials?.rejectUnauthorized,
43
44
  ...wsApp.psychicApp.httpServerOptions,
44
- });
45
- }
46
- else {
47
- return http.createServer(wsApp.psychicApp.httpServerOptions);
48
- }
45
+ })
46
+ : http.createServer(wsApp.psychicApp.httpServerOptions);
47
+ }
48
+ attachHealthCheckRoute() {
49
+ this.httpServer.on('request', (req, res) => {
50
+ const opts = PsychicAppWebsockets.getOrFail().healthCheckOptions;
51
+ if (opts === null) {
52
+ res.writeHead(404);
53
+ res.end();
54
+ return;
55
+ }
56
+ const sanitizedPath = `/${opts.path.replace(/^\//, '')}`;
57
+ if (req.url === sanitizedPath && req.method === opts.method) {
58
+ if (opts.body === null) {
59
+ res.writeHead(200);
60
+ res.end();
61
+ }
62
+ else {
63
+ res.statusCode = 200;
64
+ res.setHeader('Content-Type', 'text/plain');
65
+ res.end(opts.body);
66
+ }
67
+ }
68
+ else {
69
+ res.writeHead(404);
70
+ res.end();
71
+ }
72
+ });
49
73
  }
50
74
  /**
51
75
  * builds an http server and a socket.io server, binding to redis
@@ -43,6 +43,17 @@ export default class PsychicAppWebsockets {
43
43
  get hooks() {
44
44
  return this._hooks;
45
45
  }
46
+ defaultHealthCheckOptions = {
47
+ path: '/healthcheck',
48
+ method: 'GET',
49
+ body: null,
50
+ };
51
+ _healthCheckOptions = {
52
+ ...this.defaultHealthCheckOptions,
53
+ };
54
+ get healthCheckOptions() {
55
+ return this._healthCheckOptions;
56
+ }
46
57
  on(hookEventType, cb) {
47
58
  switch (hookEventType) {
48
59
  case 'ws:start':
@@ -63,6 +74,17 @@ export default class PsychicAppWebsockets {
63
74
  this._connection = value;
64
75
  this._subConnection = value?.duplicate();
65
76
  break;
77
+ case 'healthCheck':
78
+ if (value === null) {
79
+ this._healthCheckOptions = null;
80
+ }
81
+ else {
82
+ this._healthCheckOptions = {
83
+ ...this.defaultHealthCheckOptions,
84
+ ...value,
85
+ };
86
+ }
87
+ break;
66
88
  case 'socketio':
67
89
  this._socketioOptions = value;
68
90
  break;
@@ -14,6 +14,7 @@ export default class Cable {
14
14
  */
15
15
  connect(httpServer?: http.Server | https.Server): void;
16
16
  private buildHttpServer;
17
+ private attachHealthCheckRoute;
17
18
  /**
18
19
  * builds an http server and a socket.io server, binding to redis
19
20
  * to enable redis pubsub, then starts the http server.
@@ -21,13 +21,22 @@ export default class PsychicAppWebsockets {
21
21
  get subConnection(): RedisOrRedisClusterConnection;
22
22
  private _hooks;
23
23
  get hooks(): PsychicAppWebsocketsHooks;
24
+ private defaultHealthCheckOptions;
25
+ private _healthCheckOptions;
26
+ get healthCheckOptions(): HealthCheckOptions | null;
24
27
  on<T extends PsychicWebsocketsHookEventType>(hookEventType: T, cb: T extends 'ws:start' ? (server: SocketServer) => void | Promise<void> : T extends 'ws:connect' ? (socket: Socket) => void | Promise<void> : never): void;
25
- set<Opt extends PsychicAppWebsocketsOption>(option: Opt, value: Opt extends 'connection' ? Redis : Opt extends 'socketio' ? Partial<SocketioServerOptions> : never): void;
28
+ set<Opt extends PsychicAppWebsocketsOption>(option: Opt, value: Opt extends 'connection' ? Redis : Opt extends 'socketio' ? Partial<SocketioServerOptions> : Opt extends 'healthCheck' ? Partial<HealthCheckOptions> | null : never): void;
29
+ }
30
+ export type PsychicAppWebsocketsOption = 'connection' | 'socketio' | 'healthCheck';
31
+ interface HealthCheckOptions {
32
+ path: string;
33
+ method: 'GET' | 'HEAD';
34
+ body: string | null;
26
35
  }
27
- export type PsychicAppWebsocketsOption = 'connection' | 'socketio';
28
36
  export type PsychicWebsocketsHookEventType = 'ws:start' | 'ws:connect';
29
37
  export interface PsychicAppWebsocketsHooks {
30
38
  wsStart: ((server: SocketServer) => void | Promise<void>)[];
31
39
  wsConnect: ((socket: Socket) => void | Promise<void>)[];
32
40
  }
33
41
  export type RedisOrRedisClusterConnection = Redis | Cluster;
42
+ export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "name": "@rvoh/psychic-websockets",
4
4
  "description": "Websocket system for Psychic applications",
5
- "version": "3.0.0-alpha.1",
5
+ "version": "3.0.0-alpha.2",
6
6
  "author": "RVO Health",
7
7
  "repository": {
8
8
  "type": "git",