@adaptivestone/framework 3.4.3 → 4.1.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/CHANGELOG.md +37 -3
- package/LICENCE +21 -0
- package/cluster.js +3 -3
- package/commands/CreateUser.js +27 -0
- package/commands/Documentation.js +1 -1
- package/commands/GetOpenApiJson.js +53 -23
- package/commands/migration/Create.js +2 -2
- package/config/auth.js +1 -1
- package/config/i18n.js +4 -3
- package/config/mail.js +5 -1
- package/controllers/Home.js +2 -2
- package/controllers/Home.test.js +11 -0
- package/controllers/index.js +15 -15
- package/folderConfig.js +1 -1
- package/helpers/yup.js +24 -0
- package/index.js +8 -0
- package/models/User.js +40 -30
- package/models/User.test.js +68 -18
- package/modules/AbstractController.js +144 -208
- package/modules/AbstractModel.js +2 -1
- package/modules/Base.js +3 -2
- package/modules/BaseCli.js +6 -2
- package/package.json +20 -16
- package/server.d.ts +1 -1
- package/server.js +25 -8
- package/services/cache/Cache.d.ts +3 -3
- package/services/cache/Cache.js +17 -3
- package/services/documentation/DocumentationGenerator.js +171 -0
- package/services/http/HttpServer.js +16 -96
- package/services/http/middleware/AbstractMiddleware.js +20 -0
- package/services/http/middleware/GetUserByToken.js +4 -0
- package/services/http/middleware/I18n.js +119 -0
- package/services/http/middleware/I18n.test.js +77 -0
- package/services/http/middleware/Pagination.js +56 -0
- package/services/http/middleware/PrepareAppInfo.test.js +22 -0
- package/services/http/middleware/{Middlewares.test.js → RateLimiter.test.js} +1 -1
- package/services/http/middleware/RequestLogger.js +22 -0
- package/services/http/middleware/RequestParser.js +36 -0
- package/services/messaging/email/index.js +162 -42
- package/services/messaging/email/resources/.gitkeep +1 -0
- package/services/validate/ValidateService.js +161 -0
- package/services/validate/ValidateService.test.js +105 -0
- package/services/validate/drivers/AbstractValidator.js +37 -0
- package/services/validate/drivers/CustomValidator.js +52 -0
- package/services/validate/drivers/YupValidator.js +103 -0
- package/tests/setup.js +2 -0
- package/services/messaging/email/templates/emptyTemplate/style.less +0 -0
- package/services/messaging/email/templates/password/html.handlebars +0 -13
- package/services/messaging/email/templates/password/style.less +0 -0
- package/services/messaging/email/templates/password/subject.handlebars +0 -1
- package/services/messaging/email/templates/password/text.handlebars +0 -1
- package/services/messaging/email/templates/verification/style.less +0 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=18.0.0"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -15,9 +15,14 @@
|
|
|
15
15
|
"dev": "nodemon ./index.js",
|
|
16
16
|
"prod": "nodemon ./cluster.js",
|
|
17
17
|
"test": "jest --coverage=true",
|
|
18
|
-
"
|
|
18
|
+
"prettier": "prettier --check '**/*.(js|jsx|ts|tsx|json|css|scss|md)'",
|
|
19
|
+
"lint": "eslint '**/*.js'",
|
|
20
|
+
"lint:fix": "eslint '**/*.js' --fix",
|
|
21
|
+
"codestyle": "npm run prettier && npm run lint",
|
|
19
22
|
"prepare": "husky install",
|
|
20
|
-
"cli": "node cliCommand"
|
|
23
|
+
"cli": "node cliCommand",
|
|
24
|
+
"benchmark": "h2load -n 10000 -c 50 -p 'http/1.1' http://localhost:3300/",
|
|
25
|
+
"benchmark2": "h2load -n 10000 -c 50 https://localhost:3300/"
|
|
21
26
|
},
|
|
22
27
|
"jest": {
|
|
23
28
|
"setupFilesAfterEnv": [
|
|
@@ -34,18 +39,18 @@
|
|
|
34
39
|
"author": "Andrey Logunov",
|
|
35
40
|
"license": "MIT",
|
|
36
41
|
"dependencies": {
|
|
37
|
-
"bcrypt": "^5.0.1",
|
|
38
42
|
"cors": "^2.8.5",
|
|
39
43
|
"deepmerge": "^4.2.2",
|
|
40
44
|
"dotenv": "^16.0.0",
|
|
41
|
-
"email-templates": "^10.0.0",
|
|
42
45
|
"express": "^4.17.1",
|
|
43
|
-
"
|
|
46
|
+
"formidable": "^2.1.1",
|
|
47
|
+
"html-to-text": "^9.0.3",
|
|
48
|
+
"i18next": "^23.2.8",
|
|
44
49
|
"i18next-chained-backend": "^4.0.0",
|
|
45
50
|
"i18next-fs-backend": "^2.0.0",
|
|
46
|
-
"
|
|
51
|
+
"juice": "^9.0.0",
|
|
47
52
|
"minimist": "^1.2.5",
|
|
48
|
-
"mongoose": "^
|
|
53
|
+
"mongoose": "^7.0.0",
|
|
49
54
|
"nodemailer": "^6.6.3",
|
|
50
55
|
"nodemailer-sendmail-transport": "^1.0.2",
|
|
51
56
|
"nodemailer-stub-transport": "^1.1.0",
|
|
@@ -54,20 +59,19 @@
|
|
|
54
59
|
"redis": "^4.3.1",
|
|
55
60
|
"winston": "^3.3.3",
|
|
56
61
|
"winston-transport-sentry-node": "^2.0.0",
|
|
57
|
-
"yup": "^0.
|
|
62
|
+
"yup": "^1.0.0"
|
|
58
63
|
},
|
|
59
64
|
"devDependencies": {
|
|
60
65
|
"eslint": "^8.0.0",
|
|
61
66
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
62
|
-
"eslint-config-prettier": "^
|
|
63
|
-
"eslint-plugin-import": "^2.23.4",
|
|
67
|
+
"eslint-config-prettier": "^9.0.0",
|
|
64
68
|
"eslint-plugin-jest": "^27.0.0",
|
|
65
69
|
"husky": "^8.0.0",
|
|
66
70
|
"jest": "^29.0.0",
|
|
67
|
-
"lint-staged": "^
|
|
71
|
+
"lint-staged": "^14.0.0",
|
|
68
72
|
"mongodb-memory-server": "^8.0.2",
|
|
69
|
-
"nodemon": "^
|
|
70
|
-
"prettier": "^
|
|
73
|
+
"nodemon": "^3.0.1",
|
|
74
|
+
"prettier": "^3.0.0",
|
|
71
75
|
"supertest": "^6.1.4"
|
|
72
76
|
},
|
|
73
77
|
"lint-staged": {
|
package/server.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ declare class Server {
|
|
|
19
19
|
getModel: Server['getModel'];
|
|
20
20
|
runCliCommand: Server['runCliCommand'];
|
|
21
21
|
updateConfig: Server['updateConfig'];
|
|
22
|
-
foldersConfig: Server['config'];
|
|
22
|
+
foldersConfig: Server['config']['folders'];
|
|
23
23
|
events: EventEmitter;
|
|
24
24
|
get cache(): Server['cacheService'];
|
|
25
25
|
httpServer: null;
|
package/server.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/* eslint-disable
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const EventEmitter = require('node:events');
|
|
3
|
+
|
|
2
4
|
require('dotenv').config();
|
|
3
5
|
const merge = require('deepmerge');
|
|
4
|
-
const EventEmitter = require('events');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Main framework class.
|
|
@@ -50,16 +51,27 @@ class Server {
|
|
|
50
51
|
* @returns {Promise}
|
|
51
52
|
*/
|
|
52
53
|
async startServer(callbackBefore404 = async () => Promise.resolve()) {
|
|
53
|
-
|
|
54
|
-
const
|
|
54
|
+
// eslint-disable-next-line global-require
|
|
55
|
+
const HttpServer = require('./services/http/HttpServer');
|
|
56
|
+
// eslint-disable-next-line global-require
|
|
57
|
+
const ControllerManager = require('./controllers/index');
|
|
58
|
+
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
59
|
+
// const [{ default: HttpServer }, { default: ControllerManager }] =
|
|
60
|
+
// await Promise.all([
|
|
61
|
+
// // eslint-disable-next-line import/extensions
|
|
62
|
+
// import('./services/http/HttpServer.js'), // Speed optimisation
|
|
63
|
+
// // eslint-disable-next-line import/extensions
|
|
64
|
+
// import('./controllers/index.js'), // Speed optimisation
|
|
65
|
+
// ]);
|
|
66
|
+
|
|
55
67
|
this.addErrorHandling();
|
|
56
68
|
|
|
57
69
|
// TODO config
|
|
58
|
-
this.app.httpServer = new HttpServer(this.app
|
|
70
|
+
this.app.httpServer = new HttpServer(this.app);
|
|
59
71
|
|
|
60
72
|
this.app.controllerManager = new ControllerManager(this.app);
|
|
61
73
|
|
|
62
|
-
await this.app.controllerManager.initControllers(
|
|
74
|
+
await this.app.controllerManager.initControllers();
|
|
63
75
|
await callbackBefore404();
|
|
64
76
|
this.app.httpServer.add404Page();
|
|
65
77
|
}
|
|
@@ -70,7 +82,7 @@ class Server {
|
|
|
70
82
|
// eslint-disable-next-line class-methods-use-this
|
|
71
83
|
addErrorHandling() {
|
|
72
84
|
process.on('uncaughtException', console.error);
|
|
73
|
-
process.on('unhandledRejection',
|
|
85
|
+
process.on('unhandledRejection', (reason, p) => {
|
|
74
86
|
console.log(
|
|
75
87
|
'Possibly Unhandled Rejection at: Promise ',
|
|
76
88
|
p,
|
|
@@ -166,7 +178,11 @@ class Server {
|
|
|
166
178
|
*/
|
|
167
179
|
async runCliCommand(commandName, args) {
|
|
168
180
|
if (!this.cli) {
|
|
169
|
-
|
|
181
|
+
// eslint-disable-next-line import/extensions
|
|
182
|
+
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
183
|
+
// const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
|
|
184
|
+
// eslint-disable-next-line global-require
|
|
185
|
+
const BaseCli = require('./modules/BaseCli');
|
|
170
186
|
this.cli = new BaseCli(this);
|
|
171
187
|
}
|
|
172
188
|
return this.cli.run(commandName, args);
|
|
@@ -178,6 +194,7 @@ class Server {
|
|
|
178
194
|
*/
|
|
179
195
|
getCache() {
|
|
180
196
|
if (!this.cacheService) {
|
|
197
|
+
// eslint-disable-next-line global-require
|
|
181
198
|
const Cache = require('./services/cache/Cache'); // Speed optimisation
|
|
182
199
|
this.cacheService = new Cache(this.app);
|
|
183
200
|
}
|
|
@@ -11,7 +11,7 @@ declare class Cache extends Base {
|
|
|
11
11
|
* Function return new key with added namespace
|
|
12
12
|
* @param key key to add namespace
|
|
13
13
|
*/
|
|
14
|
-
getKeyWithNameSpace(key:
|
|
14
|
+
getKeyWithNameSpace(key: string): string;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Get value from cache. Set and get if not eists
|
|
@@ -20,7 +20,7 @@ declare class Cache extends Base {
|
|
|
20
20
|
* @param storeTime how long we should store value on cache
|
|
21
21
|
*/
|
|
22
22
|
getSetValue(
|
|
23
|
-
key:
|
|
23
|
+
key: string,
|
|
24
24
|
onNotFound: () => Promise<any>,
|
|
25
25
|
storeTime: number,
|
|
26
26
|
): Promise<any>;
|
|
@@ -29,7 +29,7 @@ declare class Cache extends Base {
|
|
|
29
29
|
* Remove key from cache
|
|
30
30
|
* @param key key to remove
|
|
31
31
|
*/
|
|
32
|
-
removeKey(key:
|
|
32
|
+
removeKey(key: string): Promise<number>;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export = Cache;
|
package/services/cache/Cache.js
CHANGED
|
@@ -66,13 +66,27 @@ class Cache extends Base {
|
|
|
66
66
|
return Promise.reject(e);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
this.redisClient.setEx(
|
|
69
|
+
this.redisClient.setEx(
|
|
70
|
+
key,
|
|
71
|
+
storeTime,
|
|
72
|
+
JSON.stringify(result, (jsonkey, value) =>
|
|
73
|
+
typeof value === 'bigint' ? `${value}n` : value,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
70
76
|
} else {
|
|
71
77
|
this.logger.verbose(
|
|
72
|
-
`getSetValueFromCache FROM CACHE key ${key}, value ${result
|
|
78
|
+
`getSetValueFromCache FROM CACHE key ${key}, value ${result.substring(
|
|
79
|
+
0,
|
|
80
|
+
100,
|
|
81
|
+
)}`,
|
|
73
82
|
);
|
|
74
83
|
try {
|
|
75
|
-
result = JSON.parse(result)
|
|
84
|
+
result = JSON.parse(result, (jsonkey, value) => {
|
|
85
|
+
if (typeof value === 'string' && /^\d+n$/.test(value)) {
|
|
86
|
+
return BigInt(value.slice(0, value.length - 1));
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
});
|
|
76
90
|
} catch (e) {
|
|
77
91
|
this.logger.warn(
|
|
78
92
|
'Not able to parse json from redis cache. That can be a normal in case you store string here',
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const Base = require('../../modules/Base');
|
|
2
|
+
const ValidateService = require('../validate/ValidateService');
|
|
3
|
+
|
|
4
|
+
class DocumentationGenerator extends Base {
|
|
5
|
+
static processingFields(fieldsByRoute) {
|
|
6
|
+
const fields = [];
|
|
7
|
+
if (!fieldsByRoute) {
|
|
8
|
+
return fields;
|
|
9
|
+
}
|
|
10
|
+
const entries = Object.entries(fieldsByRoute);
|
|
11
|
+
entries.forEach(([key, value]) => {
|
|
12
|
+
const field = {};
|
|
13
|
+
field.name = key;
|
|
14
|
+
field.type = value.type;
|
|
15
|
+
if (value.exclusiveTests) {
|
|
16
|
+
field.required = value.exclusiveTests.required;
|
|
17
|
+
}
|
|
18
|
+
if (value?.innerType) {
|
|
19
|
+
field.innerType = value?.innerType?.type;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (value.fields) {
|
|
23
|
+
field.fields = [];
|
|
24
|
+
// eslint-disable-next-line no-shadow
|
|
25
|
+
const entries = Object.entries(value.fields);
|
|
26
|
+
// eslint-disable-next-line no-shadow
|
|
27
|
+
entries.forEach(([key, value]) => {
|
|
28
|
+
field.fields.push({
|
|
29
|
+
name: key,
|
|
30
|
+
type: value.type,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
fields.push(field);
|
|
35
|
+
});
|
|
36
|
+
return fields;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static selectUniqueFields(fields) {
|
|
40
|
+
return Array.from(
|
|
41
|
+
new Map(fields.map((item) => [item.name, item])).values(),
|
|
42
|
+
).reduce((uniqueArray, item) => {
|
|
43
|
+
const existingItem = uniqueArray.find(
|
|
44
|
+
(uniqueItem) => uniqueItem.name === item.name,
|
|
45
|
+
);
|
|
46
|
+
if (!existingItem) {
|
|
47
|
+
uniqueArray.push(item);
|
|
48
|
+
} else if (item.required) {
|
|
49
|
+
existingItem.required = true;
|
|
50
|
+
}
|
|
51
|
+
return uniqueArray;
|
|
52
|
+
}, []);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static groupFieldsFromSchemas(schemas) {
|
|
56
|
+
const result = [];
|
|
57
|
+
schemas.forEach((schema) => {
|
|
58
|
+
const convertedSchema = new ValidateService(this.app, schema).validator;
|
|
59
|
+
|
|
60
|
+
for (const [key, value] of Object.entries(
|
|
61
|
+
convertedSchema?.fieldsInJsonFormat,
|
|
62
|
+
)) {
|
|
63
|
+
result.push({
|
|
64
|
+
name: key,
|
|
65
|
+
type: value.type,
|
|
66
|
+
required: value.required,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static convertDataToDocumentationElement(
|
|
75
|
+
controllerName,
|
|
76
|
+
routesInfo,
|
|
77
|
+
middlewaresInfo,
|
|
78
|
+
routeMiddlewaresReg,
|
|
79
|
+
) {
|
|
80
|
+
return {
|
|
81
|
+
contollerName: controllerName,
|
|
82
|
+
routesInfo: routesInfo.map((route) => {
|
|
83
|
+
const middlewareQueryParams = ValidateService.getMiddlewareParams(
|
|
84
|
+
middlewaresInfo,
|
|
85
|
+
routeMiddlewaresReg,
|
|
86
|
+
{
|
|
87
|
+
method: route.method.toLowerCase(),
|
|
88
|
+
path: route.fullPath,
|
|
89
|
+
},
|
|
90
|
+
).query;
|
|
91
|
+
|
|
92
|
+
const middlewareRequestParams = ValidateService.getMiddlewareParams(
|
|
93
|
+
middlewaresInfo,
|
|
94
|
+
routeMiddlewaresReg,
|
|
95
|
+
{
|
|
96
|
+
method: route.method.toLowerCase(),
|
|
97
|
+
path: route.fullPath,
|
|
98
|
+
},
|
|
99
|
+
).request;
|
|
100
|
+
|
|
101
|
+
const queryParams = this.groupFieldsFromSchemas(middlewareQueryParams);
|
|
102
|
+
|
|
103
|
+
const requestParams = this.groupFieldsFromSchemas(
|
|
104
|
+
middlewareRequestParams,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
[route.fullPath]: {
|
|
109
|
+
method: route.method,
|
|
110
|
+
name: route.name,
|
|
111
|
+
description: route?.description,
|
|
112
|
+
fields: this.selectUniqueFields([
|
|
113
|
+
...this.processingFields(route.fields),
|
|
114
|
+
...requestParams,
|
|
115
|
+
]),
|
|
116
|
+
queryFields: this.selectUniqueFields([
|
|
117
|
+
...this.processingFields(route.queryFields),
|
|
118
|
+
...queryParams,
|
|
119
|
+
]),
|
|
120
|
+
routeMiddlewares: routeMiddlewaresReg
|
|
121
|
+
.map((middleware) => {
|
|
122
|
+
const routeFullPath = route.fullPath.toUpperCase();
|
|
123
|
+
const middlewareFullPath = middleware.fullPath.toUpperCase();
|
|
124
|
+
if (
|
|
125
|
+
route.method.toLowerCase() ===
|
|
126
|
+
middleware.method.toLowerCase() &&
|
|
127
|
+
(middlewareFullPath === routeFullPath ||
|
|
128
|
+
middlewareFullPath === `${routeFullPath}*`)
|
|
129
|
+
) {
|
|
130
|
+
return {
|
|
131
|
+
name: middleware.name,
|
|
132
|
+
params: middleware.params,
|
|
133
|
+
authParams: middleware.authParams,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
})
|
|
138
|
+
.filter(Boolean),
|
|
139
|
+
controllerMiddlewares: [
|
|
140
|
+
...new Set(
|
|
141
|
+
middlewaresInfo
|
|
142
|
+
.filter((middleware) => {
|
|
143
|
+
const routeFullPath = route.fullPath.toUpperCase();
|
|
144
|
+
const middlewareFullPath =
|
|
145
|
+
middleware.fullPath.toUpperCase();
|
|
146
|
+
const middlewareFullPathWithSliced = middleware.fullPath
|
|
147
|
+
.toUpperCase()
|
|
148
|
+
.slice(0, -1);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
middlewareFullPath === routeFullPath ||
|
|
152
|
+
middlewareFullPath === `${routeFullPath}*` ||
|
|
153
|
+
routeFullPath?.indexOf(middlewareFullPathWithSliced) !==
|
|
154
|
+
-1
|
|
155
|
+
);
|
|
156
|
+
})
|
|
157
|
+
.map(({ name, params, authParams }) => ({
|
|
158
|
+
name,
|
|
159
|
+
params,
|
|
160
|
+
authParams,
|
|
161
|
+
})),
|
|
162
|
+
),
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = DocumentationGenerator;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
const http = require('node:http');
|
|
2
|
+
const path = require('node:path');
|
|
1
3
|
const express = require('express');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const path = require('path');
|
|
4
4
|
const cors = require('cors');
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const BackendFS = require('i18next-fs-backend');
|
|
9
|
-
const Backend = require('i18next-chained-backend');
|
|
6
|
+
const RequestLoggerMiddleware = require('./middleware/RequestLogger');
|
|
7
|
+
const I18nMiddleware = require('./middleware/I18n');
|
|
10
8
|
const PrepareAppInfoMiddleware = require('./middleware/PrepareAppInfo');
|
|
9
|
+
const RequestParserMiddleware = require('./middleware/RequestParser');
|
|
11
10
|
|
|
12
11
|
const Base = require('../../modules/Base');
|
|
13
12
|
|
|
@@ -15,25 +14,19 @@ const Base = require('../../modules/Base');
|
|
|
15
14
|
* HTTP server based on Express
|
|
16
15
|
*/
|
|
17
16
|
class HttpServer extends Base {
|
|
18
|
-
constructor(app
|
|
17
|
+
constructor(app) {
|
|
19
18
|
super(app);
|
|
20
19
|
this.express = express();
|
|
20
|
+
this.express.disable('x-powered-by');
|
|
21
21
|
this.express.set('views', [
|
|
22
|
-
|
|
22
|
+
this.app.foldersConfig.views,
|
|
23
23
|
path.join(__dirname, '../../views'),
|
|
24
24
|
]);
|
|
25
25
|
this.express.set('view engine', 'pug');
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
res.on('finish', () => {
|
|
31
|
-
const duration = Date.now() - startTime;
|
|
32
|
-
this.logger.info(`Finished ${text}. Duration ${duration} ms`);
|
|
33
|
-
});
|
|
34
|
-
next();
|
|
35
|
-
});
|
|
36
|
-
this.enableI18N(folderConfig);
|
|
26
|
+
|
|
27
|
+
this.express.use(new PrepareAppInfoMiddleware(this.app).getMiddleware());
|
|
28
|
+
this.express.use(new RequestLoggerMiddleware(this.app).getMiddleware());
|
|
29
|
+
this.express.use(new I18nMiddleware(this.app).getMiddleware());
|
|
37
30
|
|
|
38
31
|
const httpConfig = this.app.getConfig('http');
|
|
39
32
|
this.express.use(
|
|
@@ -41,17 +34,16 @@ class HttpServer extends Base {
|
|
|
41
34
|
origin: httpConfig.corsDomains,
|
|
42
35
|
}),
|
|
43
36
|
); // todo whitelist
|
|
44
|
-
this.express.use(express.
|
|
45
|
-
this.express.use(express.json({ limit: '50mb' }));
|
|
46
|
-
this.express.use(express.static(folderConfig.folders.public));
|
|
37
|
+
this.express.use(express.static(this.app.foldersConfig.public));
|
|
47
38
|
this.express.use(express.static('./public'));
|
|
48
39
|
|
|
49
|
-
this.express.use(new
|
|
40
|
+
this.express.use(new RequestParserMiddleware(this.app).getMiddleware());
|
|
50
41
|
|
|
51
42
|
// As exprress will check numbersof arguments
|
|
52
43
|
// eslint-disable-next-line no-unused-vars
|
|
53
44
|
this.express.use((err, req, res, next) => {
|
|
54
45
|
// error handling
|
|
46
|
+
// eslint-disable-next-line no-console
|
|
55
47
|
console.error(err.stack);
|
|
56
48
|
// TODO
|
|
57
49
|
res.status(500).send('Something broke!');
|
|
@@ -79,85 +71,13 @@ class HttpServer extends Base {
|
|
|
79
71
|
);
|
|
80
72
|
}
|
|
81
73
|
|
|
82
|
-
/**
|
|
83
|
-
* Enable support for i18n
|
|
84
|
-
* @param {object} folderConfig config
|
|
85
|
-
* @param {object} folderConfig.folders folder config
|
|
86
|
-
* @param {string} folderConfig.folders.config path to folder with config files
|
|
87
|
-
* @param {string} folderConfig.folders.models path to folder with moidels files
|
|
88
|
-
* @param {string} folderConfig.folders.controllers path to folder with controllers files
|
|
89
|
-
* @param {string} folderConfig.folders.views path to folder with view files
|
|
90
|
-
* @param {string} folderConfig.folders.public path to folder with public files
|
|
91
|
-
* @param {string} folderConfig.folders.locales path to folder with locales files
|
|
92
|
-
* @param {string} folderConfig.folders.emails path to folder with emails files
|
|
93
|
-
*/
|
|
94
|
-
enableI18N(folderConfig) {
|
|
95
|
-
const I18NConfig = this.app.getConfig('i18n');
|
|
96
|
-
if (!I18NConfig.enabled) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const lngDetector = new i18nextMiddleware.LanguageDetector();
|
|
100
|
-
lngDetector.addDetector({
|
|
101
|
-
name: 'xLang',
|
|
102
|
-
// eslint-disable-next-line no-unused-vars
|
|
103
|
-
lookup: (req, res, options) => {
|
|
104
|
-
const lng = req.get('X-Lang');
|
|
105
|
-
if (lng) {
|
|
106
|
-
return lng;
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
},
|
|
110
|
-
// eslint-disable-next-line no-unused-vars
|
|
111
|
-
cacheUserLanguage: (req, res, lng, options) => {},
|
|
112
|
-
});
|
|
113
|
-
this.logger.info('Enabling i18n support');
|
|
114
|
-
i18next
|
|
115
|
-
.use(Backend)
|
|
116
|
-
.use(lngDetector)
|
|
117
|
-
.init({
|
|
118
|
-
backend: {
|
|
119
|
-
backends: [
|
|
120
|
-
BackendFS,
|
|
121
|
-
// BackendFS,
|
|
122
|
-
],
|
|
123
|
-
backendOptions: [
|
|
124
|
-
// {
|
|
125
|
-
// loadPath: __dirname + '/../../locales/{{lng}}/{{ns}}.json',
|
|
126
|
-
// addPath: __dirname + '/../../locales/{{lng}}/{{ns}}.missing.json'
|
|
127
|
-
// },
|
|
128
|
-
{
|
|
129
|
-
loadPath: `${folderConfig.folders.locales}/{{lng}}/{{ns}}.json`,
|
|
130
|
-
addPath: `${folderConfig.folders.locales}/{{lng}}/{{ns}}.missing.json`,
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
},
|
|
134
|
-
fallbackLng: I18NConfig.fallbackLng,
|
|
135
|
-
preload: I18NConfig.preload,
|
|
136
|
-
saveMissing: I18NConfig.saveMissing,
|
|
137
|
-
debug: I18NConfig.debug,
|
|
138
|
-
detection: {
|
|
139
|
-
// caches: ['cookie'],
|
|
140
|
-
order: I18NConfig.langDetectionOders || ['xLang'],
|
|
141
|
-
lookupQuerystring: I18NConfig.lookupQuerystring,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
this.express.use(i18nextMiddleware.handle(i18next));
|
|
145
|
-
this.express.use((req, res, next) => {
|
|
146
|
-
// f ix ru-Ru, en-US, etc
|
|
147
|
-
if (res.locals.language.length !== 2) {
|
|
148
|
-
[res.locals.language] = res.locals.language.split('-');
|
|
149
|
-
}
|
|
150
|
-
next();
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
74
|
/**
|
|
155
75
|
* Add handle for 404 error
|
|
156
76
|
*/
|
|
157
77
|
add404Page() {
|
|
158
78
|
this.express.use((req, res) => {
|
|
159
79
|
// error handling
|
|
160
|
-
res.status(404).
|
|
80
|
+
res.status(404).json({ message: '404' });
|
|
161
81
|
});
|
|
162
82
|
}
|
|
163
83
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const yup = require('yup');
|
|
1
2
|
const Base = require('../../../modules/Base');
|
|
2
3
|
|
|
3
4
|
class AbstractMiddleware extends Base {
|
|
@@ -14,6 +15,25 @@ class AbstractMiddleware extends Base {
|
|
|
14
15
|
return [];
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
// eslint-disable-next-line class-methods-use-this
|
|
19
|
+
get relatedQueryParameters() {
|
|
20
|
+
// For example yup.object().shape({page: yup.number().required(),limit: yup.number()})
|
|
21
|
+
return yup.object().shape({});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line class-methods-use-this
|
|
25
|
+
get relatedRequestParameters() {
|
|
26
|
+
// For example yup.object().shape({page: yup.number().required(),limit: yup.number()})
|
|
27
|
+
return yup.object().shape({});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get relatedReqParameters() {
|
|
31
|
+
return {
|
|
32
|
+
request: this.relatedRequestParameters,
|
|
33
|
+
query: this.relatedQueryParameters,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
17
37
|
async middleware(req, res, next) {
|
|
18
38
|
this.logger.warn('Middleware is not implemented');
|
|
19
39
|
next();
|
|
@@ -17,6 +17,10 @@ class GetUserByToken extends AbstractMiddleware {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
async middleware(req, res, next) {
|
|
20
|
+
if (req.appInfo.user) {
|
|
21
|
+
this.logger.warn('You call GetUserByToken more then once');
|
|
22
|
+
return next();
|
|
23
|
+
}
|
|
20
24
|
let { token } = req.body;
|
|
21
25
|
this.logger.verbose(
|
|
22
26
|
`GetUserByToken token in BODY ${token}. Token if Authorization header ${req.get(
|