@clairejs/server 3.15.2 → 3.16.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.
Files changed (85) hide show
  1. package/.mocharc.json +3 -0
  2. package/dist/common/AbstractController.js +3 -0
  3. package/dist/common/ControllerMetadata.js +1 -0
  4. package/dist/common/FileOperation.js +6 -0
  5. package/dist/common/ServerModelMetadata.js +1 -0
  6. package/dist/common/Transactionable.js +17 -0
  7. package/dist/common/auth/AbstractPrincipalResolver.js +2 -0
  8. package/dist/common/auth/IPrincipal.js +1 -0
  9. package/dist/common/constants.js +7 -0
  10. package/dist/common/decorator.d.ts +2 -2
  11. package/dist/common/decorator.js +6 -0
  12. package/dist/common/request/EndpointMetadata.js +1 -0
  13. package/dist/common/request/HttpData.js +1 -0
  14. package/dist/common/request/HttpEndpoint.js +1 -0
  15. package/dist/common/request/JobData.js +1 -0
  16. package/dist/common/request/MountedEndpointInfo.js +1 -0
  17. package/dist/common/request/RequestOptions.js +1 -0
  18. package/dist/common/request/SocketData.js +1 -0
  19. package/dist/common/request/types.d.ts +1 -1
  20. package/dist/common/request/types.js +1 -0
  21. package/dist/controllers/FileManageController.js +90 -0
  22. package/dist/controllers/FileUploadController.js +64 -0
  23. package/dist/controllers/dto/system.js +14 -0
  24. package/dist/controllers/dto/upload.js +205 -0
  25. package/dist/http/auth/AbstractHttpAuthorizer.js +2 -0
  26. package/dist/http/common/HttpRequest.js +72 -0
  27. package/dist/http/common/HttpResponse.js +62 -0
  28. package/dist/http/controller/AbstractHttpController.js +21 -0
  29. package/dist/http/controller/AbstractHttpMiddleware.js +2 -0
  30. package/dist/http/controller/AbstractHttpRequestHandler.js +69 -0
  31. package/dist/http/controller/CrudHttpController.js +302 -0
  32. package/dist/http/controller/DefaultHttpRequestHandler.js +143 -0
  33. package/dist/http/decorators.d.ts +1 -1
  34. package/dist/http/decorators.js +86 -0
  35. package/dist/http/file-upload/AbstractFileUploadHandler.js +2 -0
  36. package/dist/http/file-upload/FileUploadHandler.js +41 -0
  37. package/dist/http/file-upload/types.d.ts +1 -1
  38. package/dist/http/file-upload/types.js +1 -0
  39. package/dist/http/repository/AbstractRepository.js +26 -0
  40. package/dist/http/repository/DtoRepository.d.ts +3 -3
  41. package/dist/http/repository/DtoRepository.js +204 -0
  42. package/dist/http/repository/ICrudRepository.js +1 -0
  43. package/dist/http/repository/ModelRepository.js +696 -0
  44. package/dist/http/security/AbstractAccessCondition.js +2 -0
  45. package/dist/http/security/access-conditions/FilterModelFieldAccessCondition.js +30 -0
  46. package/dist/http/security/access-conditions/MaximumQueryLimit.js +31 -0
  47. package/dist/http/security/cors.js +1 -0
  48. package/dist/http/utils.js +32 -0
  49. package/dist/index.js +75 -1
  50. package/dist/job/AbstractJobController.js +9 -0
  51. package/dist/job/AbstractJobRepository.js +2 -0
  52. package/dist/job/AbstractJobScheduler.js +48 -0
  53. package/dist/job/AwsJobScheduler.js +405 -0
  54. package/dist/job/LocalJobScheduler.js +273 -0
  55. package/dist/job/decorators.js +57 -0
  56. package/dist/job/interfaces.js +10 -0
  57. package/dist/logging/FileLogMedium.js +44 -0
  58. package/dist/services/AbstractFileService.js +28 -0
  59. package/dist/services/AbstractMailService.js +2 -0
  60. package/dist/services/AbstractService.js +3 -0
  61. package/dist/services/AbstractSmsService.js +2 -0
  62. package/dist/services/implementations/LocalFileService.js +42 -0
  63. package/dist/services/implementations/LocalMailService.js +27 -0
  64. package/dist/services/implementations/LocalSmsService.js +17 -0
  65. package/dist/services/implementations/S3FileService.js +107 -0
  66. package/dist/services/implementations/SesMailService.js +64 -0
  67. package/dist/socket/AbstractServerSocket.js +44 -0
  68. package/dist/socket/AbstractServerSocketManager.d.ts +1 -1
  69. package/dist/socket/AbstractServerSocketManager.js +348 -0
  70. package/dist/socket/AbstractSocketConnectionHandler.js +2 -0
  71. package/dist/socket/AbstractSocketController.d.ts +3 -3
  72. package/dist/socket/AbstractSocketController.js +12 -0
  73. package/dist/socket/AwsSocketManager.d.ts +2 -2
  74. package/dist/socket/AwsSocketManager.js +160 -0
  75. package/dist/socket/IServerSocket.js +1 -0
  76. package/dist/socket/LocalSocketManager.js +292 -0
  77. package/dist/system/ClaireServer.js +78 -0
  78. package/dist/system/ExpressWrapper.js +122 -0
  79. package/dist/system/LambdaWrapper.js +151 -0
  80. package/dist/system/ServerGlobalStore.js +1 -0
  81. package/dist/system/lamba-request-mapper.js +49 -0
  82. package/dist/system/locale/LocaleEntry.js +13 -0
  83. package/dist/system/locale/LocaleTranslation.js +47 -0
  84. package/dist/system/locale/decorators.js +14 -0
  85. package/package.json +13 -20
@@ -0,0 +1,348 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { AccessConditionValueType, DataType, Errors, getObjectMetadata, getServiceProvider, MessageType, Register, SocketMethod, } from "@clairejs/core";
8
+ import { AbstractAccessCondition } from "../http/security/AbstractAccessCondition";
9
+ import { HttpRequest } from "../http/common/HttpRequest";
10
+ import { AbstractSocketConnectionHandler } from "./AbstractSocketConnectionHandler";
11
+ import { AbstractSocketController } from "./AbstractSocketController";
12
+ const SOCKET_ACTION_HEADER = "x-socket-action";
13
+ const SOCKET_ACTION_READ = "read";
14
+ const SOCKET_ACTION_WRITE = "write";
15
+ let SocketReadCondition = class SocketReadCondition extends AbstractAccessCondition {
16
+ async resolveConditionValue(request) {
17
+ return request.headers[SOCKET_ACTION_HEADER] === SOCKET_ACTION_READ;
18
+ }
19
+ async validate(conditionValue) {
20
+ return !!conditionValue;
21
+ }
22
+ getConditionMetadata() {
23
+ return {
24
+ name: "disable_write",
25
+ description: "Disable send message to channel",
26
+ valueType: AccessConditionValueType.BOOLEAN,
27
+ };
28
+ }
29
+ };
30
+ SocketReadCondition = __decorate([
31
+ Register()
32
+ ], SocketReadCondition);
33
+ let SocketWriteCondition = class SocketWriteCondition extends AbstractAccessCondition {
34
+ async resolveConditionValue(request) {
35
+ return request.headers[SOCKET_ACTION_HEADER] === SOCKET_ACTION_WRITE;
36
+ }
37
+ async validate(conditionValue) {
38
+ return !!conditionValue;
39
+ }
40
+ getConditionMetadata() {
41
+ return {
42
+ name: "disable_read",
43
+ description: "Disable receive message from channel",
44
+ valueType: AccessConditionValueType.BOOLEAN,
45
+ };
46
+ }
47
+ };
48
+ SocketWriteCondition = __decorate([
49
+ Register()
50
+ ], SocketWriteCondition);
51
+ export class AbstractServerSocketManager {
52
+ requireAuthentication;
53
+ logger;
54
+ redisClient;
55
+ principalResolver;
56
+ requestAuthorizer;
57
+ mountedEndpointInfo;
58
+ _connectionHandler = null;
59
+ constructor(requireAuthentication, logger, redisClient, principalResolver, requestAuthorizer) {
60
+ this.requireAuthentication = requireAuthentication;
61
+ this.logger = logger;
62
+ this.redisClient = redisClient;
63
+ this.principalResolver = principalResolver;
64
+ this.requestAuthorizer = requestAuthorizer;
65
+ }
66
+ getUniqueChannelPrefix() {
67
+ return `CHANNEL:`;
68
+ }
69
+ getSocketInfoHashKey() {
70
+ return `__SOCKET_INFO__`;
71
+ }
72
+ async getConnectionHandler() {
73
+ if (this._connectionHandler === null) {
74
+ const injector = getServiceProvider().getInjector();
75
+ this._connectionHandler = injector.resolveOptional(AbstractSocketConnectionHandler);
76
+ await injector.initInstances();
77
+ }
78
+ return this._connectionHandler;
79
+ }
80
+ formatBroadcastData(channel, data) {
81
+ const rawData = {
82
+ type: MessageType.PLAIN,
83
+ data: { channel, message: data },
84
+ };
85
+ return rawData;
86
+ }
87
+ getUniqueChannelName(channel) {
88
+ return `${this.getUniqueChannelPrefix()}${channel}`;
89
+ }
90
+ async getSocketIdsOfChannel(channel) {
91
+ const socketIds = await this.redisClient.smembers(this.getUniqueChannelName(channel));
92
+ return socketIds || [];
93
+ }
94
+ async addSocketToChannel(socketId, channelInfos) {
95
+ const socket = await this.getById(socketId);
96
+ if (socket) {
97
+ socket.addChannels(channelInfos);
98
+ await Promise.all(channelInfos.map((channelInfo) => this.redisClient.sadd(this.getUniqueChannelName(channelInfo.name), socketId)));
99
+ await socket.saveSocketInfo();
100
+ }
101
+ return socket;
102
+ }
103
+ async removeSocketFromChannel(socketId, channels, persist = true) {
104
+ if (!channels.length) {
105
+ return;
106
+ }
107
+ const socket = await this.getById(socketId);
108
+ if (socket) {
109
+ socket.removeChannels(channels);
110
+ await Promise.all(channels.map((channel) => this.redisClient.srem(this.getUniqueChannelName(channel), socketId)));
111
+ if (persist) {
112
+ await socket.saveSocketInfo();
113
+ }
114
+ }
115
+ }
116
+ async removeSocket(socketId, _physicRemove = true) {
117
+ const socket = await this.getById(socketId);
118
+ if (socket) {
119
+ this.logger.debug("Remove from all channels");
120
+ await this.removeSocketFromChannel(socketId, socket.getChannelsInfo().map((c) => c.name), false);
121
+ }
122
+ //-- remove from redis
123
+ this.logger.debug("Remove from redis");
124
+ await this.redisClient.hdel(this.getSocketInfoHashKey(), socketId);
125
+ }
126
+ async handle(socketData) {
127
+ this.logger.debug("Handle socket data", socketData);
128
+ //-- check and handle socket creation
129
+ const connectionHandler = await this.getConnectionHandler();
130
+ switch (socketData.method) {
131
+ case SocketMethod.CONNECT: {
132
+ this.logger.debug("Socket connection attempt", socketData);
133
+ //-- try resolve principal
134
+ const socketRequest = new HttpRequest({
135
+ method: SocketMethod.CONNECT,
136
+ query: socketData.data.queries,
137
+ pathName: "",
138
+ });
139
+ const principal = await this.principalResolver.resolvePrincipal(socketRequest);
140
+ if (!principal && this.requireAuthentication) {
141
+ throw Errors.ACCESS_DENIED("Authentication required");
142
+ }
143
+ //-- generate server socket
144
+ await this.addSocket({
145
+ id: socketData.socketId,
146
+ authInfo: principal,
147
+ channels: [],
148
+ createdAt: Date.now(),
149
+ }, socketData.data);
150
+ break;
151
+ }
152
+ case SocketMethod.DISCONNECT: {
153
+ this.logger.debug("Socket disconnect", socketData);
154
+ const socket = await this.getById(socketData.socketId);
155
+ if (socket) {
156
+ const mountedEndpoints = await this.getMountedEndpointInfo();
157
+ //-- notify all channels
158
+ this.logger.debug("Notify channels");
159
+ for (const channelInfo of socket.getChannelsInfo()) {
160
+ const currentEndpoint = mountedEndpoints.find((e) => e.endpointMetadata.url === channelInfo.name);
161
+ if (currentEndpoint) {
162
+ currentEndpoint.endpoint.controller.onChannelLeave(socket);
163
+ }
164
+ }
165
+ this.logger.debug("Calling disconnection handler");
166
+ connectionHandler?.onSocketDisconnect(socket);
167
+ this.logger.debug("Removing socket");
168
+ await this.removeSocket(socket.getId(), false);
169
+ }
170
+ break;
171
+ }
172
+ case SocketMethod.MESSAGE: {
173
+ this.logger.debug("Socket message", socketData);
174
+ const message = socketData.data;
175
+ let clientSocket = await this.getById(socketData.socketId);
176
+ if (!clientSocket) {
177
+ this.logger.debug("Socket not found", socketData.socketId);
178
+ }
179
+ else {
180
+ switch (message.type) {
181
+ case MessageType.READY:
182
+ //-- client socket ready
183
+ await clientSocket.sendRaw({
184
+ type: MessageType.READY,
185
+ data: "",
186
+ });
187
+ //-- call connection handler
188
+ await connectionHandler?.onSocketConnect(clientSocket);
189
+ break;
190
+ case MessageType.PING_PONG:
191
+ {
192
+ //-- send back pong
193
+ await clientSocket.sendRaw({
194
+ type: MessageType.PING_PONG,
195
+ data: message.data,
196
+ });
197
+ }
198
+ break;
199
+ case MessageType.CHANNEL_JOIN:
200
+ {
201
+ //-- check channels join permission
202
+ const channels = message.data;
203
+ const channelsInfo = [];
204
+ const endpoints = [];
205
+ for (const channel of channels) {
206
+ let canSend = false;
207
+ let canReceived = false;
208
+ const mountedEndpoints = await this.getMountedEndpointInfo();
209
+ const currentEndpoint = mountedEndpoints.find((e) => e.endpointMetadata.url === channel);
210
+ if (!currentEndpoint) {
211
+ continue;
212
+ }
213
+ try {
214
+ const request = new HttpRequest({
215
+ method: SocketMethod.MESSAGE,
216
+ pathName: channel,
217
+ headers: { [SOCKET_ACTION_HEADER]: SOCKET_ACTION_READ },
218
+ }, currentEndpoint.endpointMetadata);
219
+ await this.requestAuthorizer.authorize(request, currentEndpoint);
220
+ canReceived = true;
221
+ }
222
+ catch (err) {
223
+ this.logger.debug("Error in read authorize", err);
224
+ }
225
+ try {
226
+ const request = new HttpRequest({
227
+ method: SocketMethod.MESSAGE,
228
+ pathName: channel,
229
+ headers: { [SOCKET_ACTION_HEADER]: SOCKET_ACTION_WRITE },
230
+ }, currentEndpoint.endpointMetadata);
231
+ await this.requestAuthorizer.authorize(request, currentEndpoint);
232
+ canSend = true;
233
+ }
234
+ catch (err) {
235
+ this.logger.debug("Error in write authorize", err);
236
+ }
237
+ if (canSend || canReceived) {
238
+ this.logger.debug("Adding channel info", channel, canSend, canReceived);
239
+ channelsInfo.push({
240
+ name: channel,
241
+ clientToServerAllowed: canSend,
242
+ serverToClientAllowed: canReceived,
243
+ });
244
+ endpoints.push(currentEndpoint);
245
+ }
246
+ }
247
+ //-- join socket to channels
248
+ clientSocket = await this.addSocketToChannel(clientSocket.getId(), channelsInfo);
249
+ if (!clientSocket) {
250
+ this.logger.debug("Socket not found after addSocketToChannel");
251
+ }
252
+ else {
253
+ //-- send back
254
+ await Promise.all([
255
+ clientSocket.sendRaw({
256
+ type: MessageType.CHANNEL_JOIN,
257
+ data: channelsInfo.map((c) => c.name),
258
+ }),
259
+ ...endpoints.map((endpoint) => endpoint.endpoint.controller.onChannelJoin(clientSocket)),
260
+ ]);
261
+ }
262
+ }
263
+ break;
264
+ case MessageType.CHANNEL_LEAVE:
265
+ {
266
+ const channels = message.data;
267
+ //-- remove channels from socket
268
+ const mountedEndpoints = await this.getMountedEndpointInfo();
269
+ Promise.all(channels.map((channel) => () => {
270
+ const currentEndpoint = mountedEndpoints.find((e) => e.endpointMetadata.url === channel);
271
+ if (!currentEndpoint) {
272
+ return;
273
+ }
274
+ return currentEndpoint.endpoint.controller.onChannelLeave(clientSocket);
275
+ }));
276
+ //-- remove socket from channels
277
+ await this.removeSocketFromChannel(clientSocket.getId(), channels);
278
+ }
279
+ break;
280
+ case MessageType.PLAIN:
281
+ {
282
+ const channel = message.data.channel;
283
+ const data = message.data.message;
284
+ //-- check if socket is allowed to send
285
+ if (channel) {
286
+ //-- check if socket is allowed to send to channel
287
+ const allowed = clientSocket
288
+ .getChannelsInfo()
289
+ .find((info) => info.name === channel && info.clientToServerAllowed);
290
+ if (allowed) {
291
+ const mountedEndpoints = await this.getMountedEndpointInfo();
292
+ const currentEndpoint = mountedEndpoints.find((e) => e.endpointMetadata.url === channel);
293
+ if (currentEndpoint) {
294
+ const result = await currentEndpoint.endpoint.controller.onMessage(clientSocket, data);
295
+ if (result !== false) {
296
+ //-- broadcast
297
+ this.logger.debug("broadcasting to channel", channel, data);
298
+ this.broadcastToChannel(channel, data);
299
+ }
300
+ }
301
+ }
302
+ }
303
+ else {
304
+ await connectionHandler?.onMessage(clientSocket, data);
305
+ }
306
+ }
307
+ break;
308
+ default:
309
+ //-- invalid message format ignore
310
+ this.logger.debug("Invalid message format", message);
311
+ break;
312
+ }
313
+ }
314
+ break;
315
+ }
316
+ default:
317
+ return undefined;
318
+ }
319
+ }
320
+ async getMountedEndpointInfo() {
321
+ if (!this.mountedEndpointInfo) {
322
+ const injector = getServiceProvider().getInjector();
323
+ const socketController = injector.resolveMultiple(AbstractSocketController);
324
+ await injector.initInstances();
325
+ this.mountedEndpointInfo = socketController.map((controller) => {
326
+ const controllerMetadata = getObjectMetadata(controller.constructor);
327
+ return {
328
+ endpoint: {
329
+ httpMethod: SocketMethod.MESSAGE,
330
+ mount: controller.getChannelName(),
331
+ controller: controller,
332
+ handlerFunctionName: controller.onMessage.name,
333
+ },
334
+ endpointMetadata: {
335
+ httpMethod: SocketMethod.MESSAGE,
336
+ description: "Send / Receive message to / from channel",
337
+ permissionGroup: controllerMetadata.permissionGroup,
338
+ dataType: DataType.OBJECT,
339
+ url: controller.getChannelName(),
340
+ name: controller.getChannelName(),
341
+ accessConditions: [SocketReadCondition, SocketWriteCondition],
342
+ },
343
+ };
344
+ });
345
+ }
346
+ return this.mountedEndpointInfo;
347
+ }
348
+ }
@@ -0,0 +1,2 @@
1
+ export class AbstractSocketConnectionHandler {
2
+ }
@@ -2,13 +2,13 @@ import { AbstractController } from "../common/AbstractController";
2
2
  import { IServerSocket } from "./IServerSocket";
3
3
  export declare abstract class AbstractSocketController extends AbstractController {
4
4
  abstract getChannelName(): string;
5
- onChannelJoin(socket: IServerSocket): Promise<void>;
6
- onChannelLeave(socket: IServerSocket): Promise<void>;
5
+ onChannelJoin(_socket: IServerSocket): Promise<void>;
6
+ onChannelLeave(_socket: IServerSocket): Promise<void>;
7
7
  /**
8
8
  * Handle the message sent to this channel
9
9
  * @param socket the socket sending message
10
10
  * @param data the data payload being sent
11
11
  * @returns return false to prevent message broadcast
12
12
  */
13
- onMessage(socket: IServerSocket, message: any): Promise<boolean | void>;
13
+ onMessage(_socket: IServerSocket, _message: any): Promise<boolean | void>;
14
14
  }
@@ -0,0 +1,12 @@
1
+ import { AbstractController } from "../common/AbstractController";
2
+ export class AbstractSocketController extends AbstractController {
3
+ async onChannelJoin(_socket) { }
4
+ async onChannelLeave(_socket) { }
5
+ /**
6
+ * Handle the message sent to this channel
7
+ * @param socket the socket sending message
8
+ * @param data the data payload being sent
9
+ * @returns return false to prevent message broadcast
10
+ */
11
+ async onMessage(_socket, _message) { }
12
+ }
@@ -21,10 +21,10 @@ export declare class AwsSocketManager extends AbstractServerSocketManager implem
21
21
  exit(): void;
22
22
  removeSocketFromRedis(socketId: string): Promise<void>;
23
23
  physicSend(socketId: string, data: any): Promise<void>;
24
- configure(data?: any): void;
24
+ configure(_data?: any): void;
25
25
  getSocketsByChannel(channel: string): Promise<IServerSocket[]>;
26
26
  broadcastToChannel(channel: string, data: any): Promise<void>;
27
- addSocket(socketInfo: ServerSocketInfo, data: any): Promise<AbstractServerSocket>;
27
+ addSocket(socketInfo: ServerSocketInfo, _data: any): Promise<AbstractServerSocket>;
28
28
  removeSocket(socketId: string, physicRemove?: boolean): Promise<void>;
29
29
  private getSocketBySerialized;
30
30
  getById(socketId: string): Promise<AbstractServerSocket | undefined>;
@@ -0,0 +1,160 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { AbstractLogger, Initable, LogContext } from "@clairejs/core";
11
+ import aws from "aws-sdk";
12
+ import Redis from "ioredis";
13
+ import { AbstractHttpRequestHandler } from "../http/controller/AbstractHttpRequestHandler";
14
+ import { AbstractServerSocketManager } from "./AbstractServerSocketManager";
15
+ import { AbstractServerSocket } from "./AbstractServerSocket";
16
+ import { AbstractRequestAuthorizer } from "../http/auth/AbstractHttpAuthorizer";
17
+ import { AbstractPrincipalResolver } from "../common/auth/AbstractPrincipalResolver";
18
+ class ApiGatewaySocket extends AbstractServerSocket {
19
+ socketManager;
20
+ socketInfo;
21
+ apiGateway;
22
+ socketNamespace;
23
+ redisClient;
24
+ constructor(socketManager, socketInfo, apiGateway, socketNamespace, redisClient) {
25
+ super(socketInfo);
26
+ this.socketManager = socketManager;
27
+ this.socketInfo = socketInfo;
28
+ this.apiGateway = apiGateway;
29
+ this.socketNamespace = socketNamespace;
30
+ this.redisClient = redisClient;
31
+ }
32
+ async saveSocketInfo() {
33
+ await this.redisClient.hset(this.socketNamespace, this.socketInfo.id, JSON.stringify(this.socketInfo));
34
+ }
35
+ async physicSend(data) {
36
+ await this.socketManager.physicSend(this.getId(), data);
37
+ }
38
+ async disconnect(err) {
39
+ if (err) {
40
+ await this.physicSend(err);
41
+ }
42
+ await this.socketManager.removeSocket(this.getId());
43
+ }
44
+ serialize() {
45
+ return JSON.stringify(this.socketInfo);
46
+ }
47
+ }
48
+ let AwsSocketManager = class AwsSocketManager extends AbstractServerSocketManager {
49
+ authenticationRequired;
50
+ region;
51
+ socketDomainUrl;
52
+ redis;
53
+ httpRequestHandler;
54
+ principalResolver;
55
+ requestAuthorizer;
56
+ logger;
57
+ apiGatewayManagement;
58
+ constructor(authenticationRequired, region, socketDomainUrl, redis, httpRequestHandler, principalResolver, requestAuthorizer, logger) {
59
+ super(authenticationRequired, logger, redis, principalResolver, requestAuthorizer);
60
+ this.authenticationRequired = authenticationRequired;
61
+ this.region = region;
62
+ this.socketDomainUrl = socketDomainUrl;
63
+ this.redis = redis;
64
+ this.httpRequestHandler = httpRequestHandler;
65
+ this.principalResolver = principalResolver;
66
+ this.requestAuthorizer = requestAuthorizer;
67
+ this.logger = logger;
68
+ this.apiGatewayManagement = new aws.ApiGatewayManagementApi({
69
+ region,
70
+ apiVersion: "2018-11-29",
71
+ endpoint: socketDomainUrl,
72
+ });
73
+ }
74
+ async init() {
75
+ this.logger.info("AwsSocketManager init");
76
+ }
77
+ exit() {
78
+ this.redisClient.disconnect();
79
+ this.logger.info("AwsSocketManager exit");
80
+ }
81
+ async removeSocketFromRedis(socketId) {
82
+ await this.redisClient.hdel(this.getSocketInfoHashKey(), socketId);
83
+ }
84
+ async physicSend(socketId, data) {
85
+ try {
86
+ this.logger.debug(`Socket ${socketId} sending`, data);
87
+ await this.apiGatewayManagement
88
+ .postToConnection({ ConnectionId: socketId, Data: JSON.stringify(data) })
89
+ .promise();
90
+ }
91
+ catch (err) {
92
+ this.logger.debug(`Socket ${socketId} post error`, err);
93
+ //-- socket is gone
94
+ if (err.statusCode === 410) {
95
+ //-- can not send, remove staled socket
96
+ this.logger.debug(`Disconnect & remove stale socket ${socketId}`);
97
+ await this.removeSocket(socketId, false);
98
+ this.logger.debug(`Socket removed ${socketId}`);
99
+ }
100
+ throw err;
101
+ }
102
+ }
103
+ configure(_data) {
104
+ this.logger.debug("Aws socket manager configure");
105
+ }
106
+ async getSocketsByChannel(channel) {
107
+ const socketIds = await this.getSocketIdsOfChannel(channel);
108
+ if (!socketIds.length) {
109
+ return [];
110
+ }
111
+ const data = await this.redisClient.hmget(this.getSocketInfoHashKey(), ...socketIds);
112
+ return data.filter((serialized) => !!serialized).map((s) => this.getSocketBySerialized(s));
113
+ }
114
+ async broadcastToChannel(channel, data) {
115
+ //-- get all channels
116
+ const socketIds = await this.getSocketIdsOfChannel(channel);
117
+ if (socketIds.length) {
118
+ const formattedData = this.formatBroadcastData(channel, data);
119
+ socketIds.map((socketId) => this.physicSend(socketId, formattedData).catch((err) => this.logger.error(`Cannot send to socket ${socketId}`, err)));
120
+ }
121
+ }
122
+ async addSocket(socketInfo, _data) {
123
+ const socket = new ApiGatewaySocket(this, socketInfo, this.apiGatewayManagement, this.getSocketInfoHashKey(), this.redisClient);
124
+ await socket.saveSocketInfo();
125
+ return socket;
126
+ }
127
+ async removeSocket(socketId, physicRemove = true) {
128
+ this.logger.debug(`Aws socket manager remove socket: ${socketId}, ${physicRemove}`);
129
+ await super.removeSocket(socketId, physicRemove);
130
+ this.logger.debug("Super remove ok");
131
+ if (physicRemove) {
132
+ this.apiGatewayManagement
133
+ .deleteConnection({ ConnectionId: socketId })
134
+ .promise()
135
+ .catch((err) => this.logger.error("Cannot physic remove aws socket", err));
136
+ }
137
+ }
138
+ getSocketBySerialized(serialized) {
139
+ return new ApiGatewaySocket(this, JSON.parse(serialized), this.apiGatewayManagement, this.getSocketInfoHashKey(), this.redisClient);
140
+ }
141
+ async getById(socketId) {
142
+ const serialized = await this.redisClient.hget(this.getSocketInfoHashKey(), socketId);
143
+ if (!serialized) {
144
+ this.logger.error("Cannot find socket with id: " + socketId);
145
+ return undefined;
146
+ }
147
+ this.logger.debug("Serialized aws socket", serialized);
148
+ return this.getSocketBySerialized(serialized);
149
+ }
150
+ };
151
+ AwsSocketManager = __decorate([
152
+ Initable(),
153
+ LogContext(),
154
+ __metadata("design:paramtypes", [Boolean, String, String, Redis,
155
+ AbstractHttpRequestHandler,
156
+ AbstractPrincipalResolver,
157
+ AbstractRequestAuthorizer,
158
+ AbstractLogger])
159
+ ], AwsSocketManager);
160
+ export { AwsSocketManager };
@@ -0,0 +1 @@
1
+ export {};