@wabot-dev/framework 0.1.0 → 0.2.0-beta.10
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/dist/src/addon/auth/api-key/@apiKeyHandshakeGuard.js +16 -0
- package/dist/src/addon/auth/api-key/ApiKey.js +29 -34
- package/dist/src/addon/auth/api-key/ApiKeyGuardMiddleware.js +3 -6
- package/dist/src/addon/auth/api-key/{ApiKeyConnectionGuardMiddleware.js → ApiKeyHandshakeGuardMiddleware.js} +5 -5
- package/dist/src/addon/auth/api-key/ApiKeyRepository.js +7 -17
- package/dist/src/addon/auth/api-key/PgApiKeyRepository.js +39 -6
- package/dist/src/addon/auth/api-key/RemoteApiKeyRepository.js +22 -14
- package/dist/src/addon/auth/jwt/@jwtHandshakeGuard.js +16 -0
- package/dist/src/addon/auth/jwt/Jwt.js +7 -9
- package/dist/src/addon/auth/jwt/{JwtConnectionGuardMiddleware.js → JwtHandshakeGuardMiddleware.js} +4 -4
- package/dist/src/addon/auth/jwt/JwtRefreshToken.js +40 -35
- package/dist/src/addon/auth/jwt/JwtRefreshTokenRepository.js +6 -0
- package/dist/src/addon/auth/jwt/JwtSigner.js +1 -1
- package/dist/src/addon/auth/jwt/PgJwtRefreshTokenRepository.js +27 -0
- package/dist/src/addon/chat-controller/socket/@socket.js +1 -1
- package/dist/src/addon/chat-controller/socket/SocketChannel.js +64 -29
- package/dist/src/addon/chat-controller/socket/SocketChannelConfig.js +6 -4
- package/dist/src/core/auth/Auth.js +6 -0
- package/dist/src/core/mapper/Mapper.js +22 -6
- package/dist/src/core/validation/core/validateModel.js +37 -4
- package/dist/src/core/validation/{validate.js → validateAndTransform.js} +2 -2
- package/dist/src/feature/chat-controller/runChatControllers.js +3 -1
- package/dist/src/feature/rest-controller/runRestControllers.js +2 -2
- package/dist/src/feature/socket-controller/metadata/@handshakeMiddlewares.js +16 -0
- package/dist/src/feature/socket-controller/metadata/{@socketEvent.js → @onSocketEvent.js} +2 -2
- package/dist/src/feature/socket-controller/metadata/SocketControllerMetadataStore.js +12 -34
- package/dist/src/feature/socket-controller/runSocketControllers.js +96 -78
- package/dist/src/index.d.ts +131 -129
- package/dist/src/index.js +8 -9
- package/package.json +4 -5
- package/dist/src/addon/auth/api-key/@apiKeyConnectionGuard.js +0 -16
- package/dist/src/addon/auth/jwt/@jwtConnectionGuard.js +0 -16
- package/dist/src/feature/socket-controller/metadata/@connectionMiddleware.js +0 -16
- package/dist/src/feature/socket-controller/metadata/@socketConnection.js +0 -18
|
@@ -1,65 +1,100 @@
|
|
|
1
1
|
import { __decorate, __metadata } from 'tslib';
|
|
2
2
|
import { injectable } from '../../../core/injection/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { handshakeMiddlewares } from '../../../feature/socket-controller/metadata/@handshakeMiddlewares.js';
|
|
4
|
+
import { socketController } from '../../../feature/socket-controller/metadata/@socketController.js';
|
|
5
|
+
import { onSocketEvent } from '../../../feature/socket-controller/metadata/@onSocketEvent.js';
|
|
6
|
+
import '../../../feature/socket-controller/metadata/SocketControllerMetadataStore.js';
|
|
7
|
+
import { runSocketControllers } from '../../../feature/socket-controller/runSocketControllers.js';
|
|
8
|
+
import { Socket } from 'socket.io';
|
|
4
9
|
import { SocketChannelConfig } from './SocketChannelConfig.js';
|
|
10
|
+
import '../../../core/validation/metadata/ValidationMetadataStore.js';
|
|
11
|
+
import { isNotEmpty } from '../../../core/validation/validators/is-not-empty/@isNotEmpty.js';
|
|
12
|
+
import { isString } from '../../../core/validation/validators/is-string/@isString.js';
|
|
5
13
|
|
|
6
14
|
var SocketChannel_1;
|
|
15
|
+
class SocketChannelReceivedMessage {
|
|
16
|
+
chatId;
|
|
17
|
+
senderName;
|
|
18
|
+
text;
|
|
19
|
+
}
|
|
20
|
+
__decorate([
|
|
21
|
+
isString(),
|
|
22
|
+
isNotEmpty(),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], SocketChannelReceivedMessage.prototype, "chatId", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
isString(),
|
|
27
|
+
isNotEmpty(),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], SocketChannelReceivedMessage.prototype, "senderName", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
isString(),
|
|
32
|
+
isNotEmpty(),
|
|
33
|
+
__metadata("design:type", String)
|
|
34
|
+
], SocketChannelReceivedMessage.prototype, "text", void 0);
|
|
7
35
|
let SocketChannel = SocketChannel_1 = class SocketChannel {
|
|
8
36
|
config;
|
|
9
|
-
socketServerProvider;
|
|
10
37
|
callBack = null;
|
|
11
|
-
|
|
12
|
-
constructor(config
|
|
38
|
+
controller = null;
|
|
39
|
+
constructor(config) {
|
|
13
40
|
this.config = config;
|
|
14
|
-
this.
|
|
15
|
-
this.server = this.socketServerProvider.getSocketServer();
|
|
41
|
+
this.configController();
|
|
16
42
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
43
|
+
configController() {
|
|
44
|
+
const channel = this;
|
|
45
|
+
let SocketChannelController = class SocketChannelController {
|
|
46
|
+
onMessage(message, socket) {
|
|
47
|
+
if (!channel.callBack)
|
|
48
|
+
return;
|
|
23
49
|
const trimmedInput = message.text.trim();
|
|
24
50
|
if (!trimmedInput) {
|
|
25
51
|
return;
|
|
26
52
|
}
|
|
27
|
-
if (!message.chatId || !message.userId || !message.senderName) {
|
|
28
|
-
socket.emit(this.config.channel, {
|
|
29
|
-
error: 'Invalid message format. chatId, userId, and senderName are required.',
|
|
30
|
-
});
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
53
|
const chatConnection = {
|
|
34
54
|
id: message.chatId,
|
|
35
55
|
chatType: 'PRIVATE',
|
|
36
56
|
channelName: SocketChannel_1.name,
|
|
37
57
|
};
|
|
38
|
-
|
|
39
|
-
return;
|
|
40
|
-
this.callBack({
|
|
58
|
+
channel.callBack({
|
|
41
59
|
chatConnection,
|
|
42
60
|
message: {
|
|
43
|
-
text:
|
|
61
|
+
text: message.text,
|
|
44
62
|
senderName: message.senderName,
|
|
45
63
|
},
|
|
46
64
|
reply: (message) => {
|
|
47
|
-
socket.emit(
|
|
65
|
+
socket.emit('message', message);
|
|
48
66
|
},
|
|
49
67
|
authInfo: socket.data.authInfo,
|
|
50
68
|
setAuthInfo: (authInfo) => {
|
|
51
69
|
socket.data.authInfo = authInfo;
|
|
52
70
|
},
|
|
53
71
|
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
__decorate([
|
|
75
|
+
onSocketEvent('message'),
|
|
76
|
+
__metadata("design:type", Function),
|
|
77
|
+
__metadata("design:paramtypes", [SocketChannelReceivedMessage, Socket]),
|
|
78
|
+
__metadata("design:returntype", void 0)
|
|
79
|
+
], SocketChannelController.prototype, "onMessage", null);
|
|
80
|
+
SocketChannelController = __decorate([
|
|
81
|
+
socketController(channel.config.namespace),
|
|
82
|
+
handshakeMiddlewares(channel.config.handshakeMidlewares ?? [])
|
|
83
|
+
], SocketChannelController);
|
|
84
|
+
this.controller = SocketChannelController;
|
|
85
|
+
}
|
|
86
|
+
listen(callback) {
|
|
87
|
+
this.callBack = callback;
|
|
88
|
+
}
|
|
89
|
+
connect() {
|
|
90
|
+
if (!this.controller)
|
|
91
|
+
return;
|
|
92
|
+
runSocketControllers([this.controller]);
|
|
57
93
|
}
|
|
58
94
|
};
|
|
59
95
|
SocketChannel = SocketChannel_1 = __decorate([
|
|
60
96
|
injectable(),
|
|
61
|
-
__metadata("design:paramtypes", [SocketChannelConfig
|
|
62
|
-
SocketServerProvider])
|
|
97
|
+
__metadata("design:paramtypes", [SocketChannelConfig])
|
|
63
98
|
], SocketChannel);
|
|
64
99
|
|
|
65
|
-
export { SocketChannel };
|
|
100
|
+
export { SocketChannel, SocketChannelReceivedMessage };
|
|
@@ -2,14 +2,16 @@ import { __decorate, __metadata } from 'tslib';
|
|
|
2
2
|
import { injectable } from '../../../core/injection/index.js';
|
|
3
3
|
|
|
4
4
|
let SocketChannelConfig = class SocketChannelConfig {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
namespace;
|
|
6
|
+
handshakeMidlewares;
|
|
7
|
+
constructor(namespace, handshakeMidlewares) {
|
|
8
|
+
this.namespace = namespace;
|
|
9
|
+
this.handshakeMidlewares = handshakeMidlewares;
|
|
8
10
|
}
|
|
9
11
|
};
|
|
10
12
|
SocketChannelConfig = __decorate([
|
|
11
13
|
injectable(),
|
|
12
|
-
__metadata("design:paramtypes", [String])
|
|
14
|
+
__metadata("design:paramtypes", [String, Array])
|
|
13
15
|
], SocketChannelConfig);
|
|
14
16
|
|
|
15
17
|
export { SocketChannelConfig };
|
|
@@ -4,6 +4,7 @@ import { scoped, Lifecycle } from '../injection/index.js';
|
|
|
4
4
|
|
|
5
5
|
let Auth = class Auth {
|
|
6
6
|
authInfo = null;
|
|
7
|
+
overrided = false;
|
|
7
8
|
require() {
|
|
8
9
|
if (!this.authInfo) {
|
|
9
10
|
throw new CustomError({ message: 'Unauthorized', httpCode: 401 });
|
|
@@ -18,13 +19,18 @@ let Auth = class Auth {
|
|
|
18
19
|
}
|
|
19
20
|
override(authInfo) {
|
|
20
21
|
this.authInfo = authInfo;
|
|
22
|
+
this.overrided = true;
|
|
21
23
|
}
|
|
22
24
|
clear() {
|
|
23
25
|
this.authInfo = null;
|
|
26
|
+
this.overrided = true;
|
|
24
27
|
}
|
|
25
28
|
isAssigned() {
|
|
26
29
|
return this.authInfo !== null;
|
|
27
30
|
}
|
|
31
|
+
wasOverrided() {
|
|
32
|
+
return this.overrided;
|
|
33
|
+
}
|
|
28
34
|
};
|
|
29
35
|
Auth = __decorate([
|
|
30
36
|
scoped(Lifecycle.ContainerScoped)
|
|
@@ -2,9 +2,9 @@ import { Storable } from '../storable/Storable.js';
|
|
|
2
2
|
import { CustomError } from '../error/CustomError.js';
|
|
3
3
|
import '../injection/index.js';
|
|
4
4
|
import '../validation/metadata/ValidationMetadataStore.js';
|
|
5
|
-
import {
|
|
5
|
+
import { validateAndTransform } from '../validation/validateAndTransform.js';
|
|
6
6
|
|
|
7
|
-
function deepCopyWithStorable(obj) {
|
|
7
|
+
function deepCopyWithStorable(obj, visited = new WeakMap()) {
|
|
8
8
|
if (obj === null || typeof obj !== 'object') {
|
|
9
9
|
return obj;
|
|
10
10
|
}
|
|
@@ -12,22 +12,38 @@ function deepCopyWithStorable(obj) {
|
|
|
12
12
|
return obj.getTime();
|
|
13
13
|
}
|
|
14
14
|
if (obj instanceof Storable) {
|
|
15
|
-
|
|
15
|
+
const dataCopy = deepCopyWithStorable(obj['data'], visited);
|
|
16
|
+
const result = { ...dataCopy };
|
|
17
|
+
// Handle getters from prototype
|
|
18
|
+
const proto = Object.getPrototypeOf(obj);
|
|
19
|
+
const descriptors = Object.getOwnPropertyDescriptors(proto);
|
|
20
|
+
for (const [key, descriptor] of Object.entries(descriptors)) {
|
|
21
|
+
if (typeof descriptor.get === 'function') {
|
|
22
|
+
try {
|
|
23
|
+
const value = obj[key];
|
|
24
|
+
result[key] = deepCopyWithStorable(value, visited);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Silently ignore getters that throw
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
16
32
|
}
|
|
17
33
|
if (Array.isArray(obj)) {
|
|
18
|
-
return obj.map((item) => deepCopyWithStorable(item));
|
|
34
|
+
return obj.map((item) => deepCopyWithStorable(item, visited));
|
|
19
35
|
}
|
|
20
36
|
const copy = {};
|
|
21
37
|
for (const key in obj) {
|
|
22
38
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
23
|
-
copy[key] = deepCopyWithStorable(obj[key]);
|
|
39
|
+
copy[key] = deepCopyWithStorable(obj[key], visited);
|
|
24
40
|
}
|
|
25
41
|
}
|
|
26
42
|
return copy;
|
|
27
43
|
}
|
|
28
44
|
class Mapper {
|
|
29
45
|
map(data, ctor) {
|
|
30
|
-
const validationResult =
|
|
46
|
+
const validationResult = validateAndTransform(deepCopyWithStorable(data), ctor);
|
|
31
47
|
if (validationResult.error) {
|
|
32
48
|
throw new CustomError({
|
|
33
49
|
httpCode: 500,
|
|
@@ -4,19 +4,52 @@ function validateModel(value, info) {
|
|
|
4
4
|
}
|
|
5
5
|
let propertiesErrors = {};
|
|
6
6
|
let resultValue = new info.modelConstructor();
|
|
7
|
+
const properties = [];
|
|
8
|
+
const getters = [];
|
|
7
9
|
for (const propertyName in info.properties) {
|
|
10
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(resultValue), propertyName);
|
|
11
|
+
if (descriptor?.get) {
|
|
12
|
+
getters.push(propertyName);
|
|
13
|
+
}
|
|
14
|
+
if (!descriptor?.set && !descriptor?.get) {
|
|
15
|
+
properties.push(propertyName);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const propertyName of properties) {
|
|
8
19
|
const propertyInfo = info.properties[propertyName];
|
|
9
20
|
const propertyValidators = propertyInfo.validators ?? [];
|
|
10
|
-
|
|
21
|
+
let currentPropValue = propertyInfo.isOptional
|
|
11
22
|
? (value[propertyName] ?? resultValue[propertyName])
|
|
12
23
|
: value[propertyName];
|
|
13
|
-
if (
|
|
24
|
+
if (currentPropValue == null && propertyInfo.isOptional) {
|
|
14
25
|
resultValue[propertyName] = undefined;
|
|
15
26
|
continue;
|
|
16
27
|
}
|
|
17
28
|
for (let propertyValidatorInfo of propertyValidators) {
|
|
18
|
-
const propertyValidatorResult = propertyValidatorInfo.validator(
|
|
19
|
-
|
|
29
|
+
const propertyValidatorResult = propertyValidatorInfo.validator(currentPropValue, propertyValidatorInfo.validatorOptions);
|
|
30
|
+
if (propertyValidatorResult.error) {
|
|
31
|
+
let propertyErrors = propertiesErrors[propertyName];
|
|
32
|
+
if (!propertyErrors) {
|
|
33
|
+
propertyErrors = [];
|
|
34
|
+
propertiesErrors[propertyName] = propertyErrors;
|
|
35
|
+
}
|
|
36
|
+
propertyErrors.push(propertyValidatorResult.error.description);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
currentPropValue = propertyValidatorResult.value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
resultValue[propertyName] = currentPropValue;
|
|
43
|
+
}
|
|
44
|
+
for (const propertyName of getters) {
|
|
45
|
+
const propertyInfo = info.properties[propertyName];
|
|
46
|
+
const propertyValidators = propertyInfo.validators ?? [];
|
|
47
|
+
let propValue = resultValue[propertyName];
|
|
48
|
+
if (propValue == null && propertyInfo.isOptional) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (let propertyValidatorInfo of propertyValidators) {
|
|
52
|
+
const propertyValidatorResult = propertyValidatorInfo.validator(propValue, propertyValidatorInfo.validatorOptions);
|
|
20
53
|
if (propertyValidatorResult.error) {
|
|
21
54
|
let propertyErrors = propertiesErrors[propertyName];
|
|
22
55
|
if (!propertyErrors) {
|
|
@@ -2,10 +2,10 @@ import { container } from '../injection/index.js';
|
|
|
2
2
|
import { ValidationMetadataStore } from './metadata/ValidationMetadataStore.js';
|
|
3
3
|
import { validateModel } from './core/validateModel.js';
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function validateAndTransform(value, modelConstructor) {
|
|
6
6
|
const metadataStore = container.resolve(ValidationMetadataStore);
|
|
7
7
|
const info = metadataStore.getModelValidatorsInfo(modelConstructor);
|
|
8
8
|
return validateModel(value, info);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export {
|
|
11
|
+
export { validateAndTransform };
|
|
@@ -69,7 +69,9 @@ function runChatControllers(controllers) {
|
|
|
69
69
|
channelMessage.reply(message);
|
|
70
70
|
if (channelMessage.setAuthInfo) {
|
|
71
71
|
const auth = chatContainer.resolve(Auth);
|
|
72
|
-
|
|
72
|
+
if (auth.wasOverrided()) {
|
|
73
|
+
channelMessage.setAuthInfo(auth['authInfo'] || undefined);
|
|
74
|
+
}
|
|
73
75
|
}
|
|
74
76
|
},
|
|
75
77
|
};
|
|
@@ -2,7 +2,7 @@ import { CustomError } from '../../core/error/CustomError.js';
|
|
|
2
2
|
import { container } from '../../core/injection/index.js';
|
|
3
3
|
import { Logger } from '../../core/logger/Logger.js';
|
|
4
4
|
import '../../core/validation/metadata/ValidationMetadataStore.js';
|
|
5
|
-
import {
|
|
5
|
+
import { validateAndTransform } from '../../core/validation/validateAndTransform.js';
|
|
6
6
|
import { ExpressProvider } from '../express/ExpressProvider.js';
|
|
7
7
|
import { json, urlencoded } from 'express';
|
|
8
8
|
import path__default from 'path';
|
|
@@ -51,7 +51,7 @@ function runRestControllers(controllers) {
|
|
|
51
51
|
}
|
|
52
52
|
defaultArgFound = true;
|
|
53
53
|
if (typeof paramType === 'function') {
|
|
54
|
-
const { value, error } =
|
|
54
|
+
const { value, error } = validateAndTransform(buildRequest(req), paramType);
|
|
55
55
|
if (error) {
|
|
56
56
|
throw new CustomError({ httpCode: 400, message: error.description, info: error });
|
|
57
57
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SocketControllerMetadataStore } from './SocketControllerMetadataStore.js';
|
|
2
|
+
import { container } from '../../../core/injection/index.js';
|
|
3
|
+
|
|
4
|
+
function handshakeMiddlewares(middlewares) {
|
|
5
|
+
return function (target) {
|
|
6
|
+
const store = container.resolve(SocketControllerMetadataStore);
|
|
7
|
+
for (const mw of middlewares) {
|
|
8
|
+
store.saveHandshakeMiddlewareMetadata({
|
|
9
|
+
controllerConstructor: target.constructor,
|
|
10
|
+
middlewareConstructor: mw,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { handshakeMiddlewares };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { container } from '../../../core/injection/index.js';
|
|
2
2
|
import { SocketControllerMetadataStore } from './SocketControllerMetadataStore.js';
|
|
3
3
|
|
|
4
|
-
function
|
|
4
|
+
function onSocketEvent(config) {
|
|
5
5
|
return function (target, propertyKey) {
|
|
6
6
|
const functionName = propertyKey.toString();
|
|
7
7
|
const paramsTypes = Reflect.getMetadata('design:paramtypes', target, functionName);
|
|
@@ -15,4 +15,4 @@ function socketEvent(config) {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export {
|
|
18
|
+
export { onSocketEvent };
|
|
@@ -3,19 +3,11 @@ import { singleton } from '../../../core/injection/index.js';
|
|
|
3
3
|
|
|
4
4
|
let SocketControllerMetadataStore = class SocketControllerMetadataStore {
|
|
5
5
|
socketControllers = new Map();
|
|
6
|
-
socketConnections = new Map();
|
|
7
6
|
socketEvents = new Map();
|
|
8
|
-
|
|
7
|
+
handshakeMiddlewares = new Map();
|
|
9
8
|
saveControllerMetadata(controllerMetadata) {
|
|
10
9
|
this.socketControllers.set(controllerMetadata.controllerConstructor, controllerMetadata);
|
|
11
10
|
}
|
|
12
|
-
saveSocketConnectionMetadata(socketConnectionMetadata) {
|
|
13
|
-
let controllerConnections = this.socketConnections.get(socketConnectionMetadata.controllerConstructor);
|
|
14
|
-
if (!controllerConnections) {
|
|
15
|
-
this.socketConnections.set(socketConnectionMetadata.controllerConstructor, (controllerConnections = new Map()));
|
|
16
|
-
}
|
|
17
|
-
controllerConnections.set(socketConnectionMetadata.functionName, socketConnectionMetadata);
|
|
18
|
-
}
|
|
19
11
|
saveSocketEventMetadata(socketEventMetadata) {
|
|
20
12
|
let controllerEvents = this.socketEvents.get(socketEventMetadata.controllerConstructor);
|
|
21
13
|
if (!controllerEvents) {
|
|
@@ -23,39 +15,25 @@ let SocketControllerMetadataStore = class SocketControllerMetadataStore {
|
|
|
23
15
|
}
|
|
24
16
|
controllerEvents.set(socketEventMetadata.functionName, socketEventMetadata);
|
|
25
17
|
}
|
|
26
|
-
|
|
27
|
-
let controllerMiddlewares = this.
|
|
18
|
+
saveHandshakeMiddlewareMetadata(handshakeMetadata) {
|
|
19
|
+
let controllerMiddlewares = this.handshakeMiddlewares.get(handshakeMetadata.controllerConstructor);
|
|
28
20
|
if (!controllerMiddlewares) {
|
|
29
|
-
this.
|
|
21
|
+
this.handshakeMiddlewares.set(handshakeMetadata.controllerConstructor, (controllerMiddlewares = []));
|
|
30
22
|
}
|
|
31
|
-
|
|
32
|
-
if (!methodMiddlewares) {
|
|
33
|
-
controllerMiddlewares.set(middlewareMetadata.functionName, (methodMiddlewares = []));
|
|
34
|
-
}
|
|
35
|
-
methodMiddlewares.unshift(middlewareMetadata);
|
|
23
|
+
controllerMiddlewares.unshift(handshakeMetadata);
|
|
36
24
|
}
|
|
37
|
-
|
|
25
|
+
getSocketControllerInfo(controllerConstructor) {
|
|
38
26
|
const controller = this.socketControllers.get(controllerConstructor);
|
|
39
27
|
if (!controller) {
|
|
40
28
|
throw new Error(`${controllerConstructor.name} should be decorated with @socketController`);
|
|
41
29
|
}
|
|
42
|
-
const
|
|
30
|
+
const handShakeMiddlewares = this.handshakeMiddlewares.get(controllerConstructor);
|
|
43
31
|
const events = this.socketEvents.get(controllerConstructor) ?? new Map();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...connection,
|
|
50
|
-
events: (() => {
|
|
51
|
-
const connectionNamespace = connection.config?.namespace;
|
|
52
|
-
return [...events.values()].filter((x) => x.config?.namespace === connectionNamespace);
|
|
53
|
-
})(),
|
|
54
|
-
connectionMiddlewares: this.connectionMiddlewares
|
|
55
|
-
.get(connection.controllerConstructor)
|
|
56
|
-
?.get(connection.functionName) ?? [],
|
|
57
|
-
controller: this.socketControllers.get(connection.controllerConstructor),
|
|
58
|
-
}));
|
|
32
|
+
return {
|
|
33
|
+
controller,
|
|
34
|
+
events,
|
|
35
|
+
handShakeMiddlewares,
|
|
36
|
+
};
|
|
59
37
|
}
|
|
60
38
|
};
|
|
61
39
|
SocketControllerMetadataStore = __decorate([
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CustomError } from '../../core/error/CustomError.js';
|
|
2
2
|
import { container } from '../../core/injection/index.js';
|
|
3
|
-
import path__default from 'path';
|
|
4
3
|
import { Logger } from '../../core/logger/Logger.js';
|
|
5
|
-
import { SocketServerProvider } from '../socket/SocketServerProvider.js';
|
|
6
|
-
import { CustomError } from '../../core/error/CustomError.js';
|
|
7
4
|
import '../../core/validation/metadata/ValidationMetadataStore.js';
|
|
8
|
-
import {
|
|
5
|
+
import { validateAndTransform } from '../../core/validation/validateAndTransform.js';
|
|
6
|
+
import { Socket } from 'socket.io';
|
|
7
|
+
import { SocketServerProvider } from '../socket/SocketServerProvider.js';
|
|
8
|
+
import { SocketControllerMetadataStore } from './metadata/SocketControllerMetadataStore.js';
|
|
9
9
|
|
|
10
10
|
function runSocketControllers(controllers) {
|
|
11
11
|
const logger = new Logger('wabot:socket');
|
|
@@ -13,84 +13,102 @@ function runSocketControllers(controllers) {
|
|
|
13
13
|
const socketServerProvider = container.resolve(SocketServerProvider);
|
|
14
14
|
const socketServer = socketServerProvider.getSocketServer();
|
|
15
15
|
controllers.forEach((controller) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const middlewares = connection.connectionMiddlewares.map((x) => connectionContainer.resolve(x.middlewareConstructor));
|
|
27
|
-
for (const middleware of middlewares) {
|
|
28
|
-
await middleware.handle(socket, connectionContainer);
|
|
29
|
-
}
|
|
30
|
-
socket.data.connectionContainer = connectionContainer;
|
|
31
|
-
next();
|
|
16
|
+
const controllerInfo = metadataStore.getSocketControllerInfo(controller);
|
|
17
|
+
const namespace = `/${controllerInfo.controller.config?.namespace ?? ''}`;
|
|
18
|
+
logger.info(`config connection to ${namespace}`);
|
|
19
|
+
const namespaceServer = socketServer.of(namespace);
|
|
20
|
+
namespaceServer.use(async (socket, next) => {
|
|
21
|
+
const connectionContainer = container.createChildContainer();
|
|
22
|
+
try {
|
|
23
|
+
const middlewares = controllerInfo.handShakeMiddlewares?.map((x) => connectionContainer.resolve(x.middlewareConstructor)) ?? [];
|
|
24
|
+
for (const middleware of middlewares) {
|
|
25
|
+
await middleware.handle(socket, connectionContainer);
|
|
32
26
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
socket.data.connectionContainer = connectionContainer;
|
|
28
|
+
next();
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
next(err);
|
|
32
|
+
connectionContainer.dispose();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const eventListener = async (controllerInstance, socket, event, req, callback) => {
|
|
36
|
+
logger.trace(`received '${event.config.event}' event on '${namespace}'`);
|
|
37
|
+
const paramsValues = [];
|
|
38
|
+
try {
|
|
39
|
+
if (event.paramsTypes.length > 2) {
|
|
40
|
+
throw new CustomError({
|
|
41
|
+
httpCode: 400,
|
|
42
|
+
message: 'the socket event handler should have max 2 parameters: (req, socket)',
|
|
43
|
+
});
|
|
36
44
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
connection.events.forEach((event) => {
|
|
44
|
-
logger.trace(`config listener to '${event.config.event}' event on '${namespace}'`);
|
|
45
|
-
socket.on(event.config.event, async (req, callback) => {
|
|
46
|
-
logger.trace(`received '${event.config.event}' event on '${namespace}'`);
|
|
47
|
-
try {
|
|
48
|
-
const reqType = event.paramsTypes[0];
|
|
49
|
-
if (typeof reqType !== 'function') {
|
|
50
|
-
throw new CustomError({
|
|
51
|
-
httpCode: 400,
|
|
52
|
-
message: 'Unable to validate request',
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
const { value, error } = validate(req, reqType);
|
|
56
|
-
if (error) {
|
|
57
|
-
throw new CustomError({
|
|
58
|
-
httpCode: 400,
|
|
59
|
-
message: error.description,
|
|
60
|
-
info: error,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const out = await controllerInstance[event.functionName].apply(controllerInstance, [value, socket]);
|
|
64
|
-
callback(out);
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
logger.error(err);
|
|
68
|
-
if (err instanceof Error) {
|
|
69
|
-
const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
|
|
70
|
-
const { httpCode, ...info } = keys.reduce((acc, key) => {
|
|
71
|
-
acc[key] = err[key];
|
|
72
|
-
return acc;
|
|
73
|
-
}, {});
|
|
74
|
-
if (typeof callback === 'function') {
|
|
75
|
-
callback({ error: { ...info, message: err.message, stack: err.stack } });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
if (typeof callback === 'function') {
|
|
80
|
-
callback({ error: { message: 'Unspected error' } });
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
45
|
+
if (event.paramsTypes[0] !== Socket) {
|
|
46
|
+
const reqType = event.paramsTypes[0];
|
|
47
|
+
if (typeof reqType !== 'function') {
|
|
48
|
+
throw new CustomError({
|
|
49
|
+
httpCode: 400,
|
|
50
|
+
message: 'Unable to validate request',
|
|
84
51
|
});
|
|
85
|
-
}
|
|
86
|
-
|
|
52
|
+
}
|
|
53
|
+
const { value, error } = validateAndTransform(req, reqType);
|
|
54
|
+
if (error) {
|
|
55
|
+
throw new CustomError({
|
|
56
|
+
httpCode: 400,
|
|
57
|
+
message: error.description,
|
|
58
|
+
info: error,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
paramsValues.push(value);
|
|
62
|
+
}
|
|
63
|
+
paramsValues.push(socket);
|
|
64
|
+
const out = await controllerInstance[event.functionName].apply(controllerInstance, paramsValues);
|
|
65
|
+
if (typeof callback === 'function') {
|
|
66
|
+
callback(out);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
logger.error(err);
|
|
71
|
+
if (err instanceof Error) {
|
|
72
|
+
const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
|
|
73
|
+
const { httpCode, ...info } = keys.reduce((acc, key) => {
|
|
74
|
+
acc[key] = err[key];
|
|
75
|
+
return acc;
|
|
76
|
+
}, {});
|
|
77
|
+
if (typeof callback === 'function') {
|
|
78
|
+
callback({ error: { ...info, message: err.message, stack: err.stack } });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
if (typeof callback === 'function') {
|
|
83
|
+
callback({ error: { message: 'Unspected error' } });
|
|
84
|
+
}
|
|
87
85
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
namespaceServer.on('connection', async (socket) => {
|
|
89
|
+
logger.trace(`connection on '${namespace}'`);
|
|
90
|
+
const connectionContainer = socket.data.connectionContainer;
|
|
91
|
+
try {
|
|
92
|
+
const controllerInstance = connectionContainer.resolve(controllerInfo.controller.controllerConstructor);
|
|
93
|
+
controllerInfo.events.forEach((event) => {
|
|
94
|
+
logger.trace(`config listener to '${event.config.event}' event on '${namespace}'`);
|
|
95
|
+
if (event.config.event === 'connection') {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
socket.on(event.config.event, async (req, callback) => {
|
|
99
|
+
await eventListener(controllerInstance, socket, event, req, callback);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
const connectionEvent = controllerInfo.events.get('connection');
|
|
103
|
+
if (connectionEvent) {
|
|
104
|
+
await eventListener(controllerInstance, socket, connectionEvent, null, null);
|
|
92
105
|
}
|
|
93
|
-
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
logger.error(err);
|
|
109
|
+
socket.disconnect();
|
|
110
|
+
connectionContainer.dispose();
|
|
111
|
+
}
|
|
94
112
|
});
|
|
95
113
|
});
|
|
96
114
|
socketServerProvider.listen();
|