@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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
### 4.1.0
|
|
2
|
+
|
|
3
|
+
[UPDATE] updated deps
|
|
4
|
+
[NEW] email - Ability to render templae to string for future usage
|
|
5
|
+
|
|
6
|
+
### 4.0.0
|
|
7
|
+
|
|
8
|
+
[BREAKING] change bcrypt encryption with scrypt
|
|
9
|
+
[BREAKING] change internal express parser to formidable parser. Affect you if external formidable is used
|
|
10
|
+
[BREAKING] should not affect any user. Changed email-templates module to internal implementation. Idea to keep dependensy list smaller
|
|
11
|
+
[BREAKING] change i18n middleware to internal one. Nothing should be affected
|
|
12
|
+
[BREAKING] now validation of request splitted between request and query
|
|
13
|
+
[BREAKING] supportedLngs option added to i18n config
|
|
14
|
+
[BREAKING] email inliner now looking for src/services/messaging/email/resources folder instead of 'build' folder.
|
|
15
|
+
|
|
16
|
+
[BREAKING] Mongoose v7. https://mongoosejs.com/docs/migrating_to_7.html
|
|
17
|
+
[BREAKING] Yup validation was updated to v1 https://github.com/jquense/yup/issues/1906
|
|
18
|
+
|
|
19
|
+
[DEPRECATED] getExpress path is deprecated. Renamed to getHttpPath
|
|
20
|
+
|
|
21
|
+
[NEW] pagination middleware
|
|
22
|
+
[NEW] requestLogger middleware. Migrated from core server to be an middleware
|
|
23
|
+
[NEW] CreateUser command
|
|
24
|
+
[NEW] custom yup validator for validate File requests
|
|
25
|
+
[UPDATE] updated deps
|
|
26
|
+
[UPDATE] openApi generator support files
|
|
27
|
+
[UPDATE] updated 18n middleware. Introduced internal cachce. Speed up of request processing up to 100%
|
|
28
|
+
[UPDATE] cache drivers to JSON support BigInt numbers
|
|
29
|
+
|
|
30
|
+
### 3.4.3
|
|
31
|
+
|
|
32
|
+
[UPDATE] updated deps
|
|
33
|
+
[FIX] fix tests for redis
|
|
34
|
+
[FIX] support in tests TEST_FOLDER_EMAILS
|
|
35
|
+
|
|
1
36
|
### 3.4.2
|
|
2
37
|
|
|
3
38
|
[UPDATE] updated deps
|
|
@@ -236,9 +271,8 @@ const someTypeSequence = await SequenceModel.getSequence('someType');
|
|
|
236
271
|
// will be 2
|
|
237
272
|
const someTypeSequence2 = await SequenceModel.getSequence('someType');
|
|
238
273
|
// will be 1 as type is another
|
|
239
|
-
const someAnotherTypeSequence =
|
|
240
|
-
'someAnotherType'
|
|
241
|
-
);
|
|
274
|
+
const someAnotherTypeSequence =
|
|
275
|
+
await SequenceModel.getSequence('someAnotherType');
|
|
242
276
|
```
|
|
243
277
|
|
|
244
278
|
#### 2.13.1
|
package/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016-2023 Andrei Lahunou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/cluster.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const cluster = require('cluster');
|
|
2
|
-
const numCPUs = require('os').cpus().length;
|
|
1
|
+
const cluster = require('node:cluster');
|
|
2
|
+
const numCPUs = require('node:os').cpus().length;
|
|
3
3
|
|
|
4
4
|
if (cluster.isMaster) {
|
|
5
5
|
// eslint-disable-next-line no-console
|
|
@@ -22,5 +22,5 @@ if (cluster.isMaster) {
|
|
|
22
22
|
});
|
|
23
23
|
} else {
|
|
24
24
|
// eslint-disable-next-line global-require
|
|
25
|
-
require('./
|
|
25
|
+
require('./index');
|
|
26
26
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const AbstractCommand = require('../modules/AbstractCommand');
|
|
2
|
+
|
|
3
|
+
// Example: node src/cli createuser --email=somemail@gmail.com --password=somePassword --roles=user,admin,someOtherRoles
|
|
4
|
+
class CreateUser extends AbstractCommand {
|
|
5
|
+
async run() {
|
|
6
|
+
const User = this.app.getModel('User');
|
|
7
|
+
const { email, password, roles } = this.args;
|
|
8
|
+
|
|
9
|
+
if (!email || !password) {
|
|
10
|
+
this.logger.error('Input validation failded');
|
|
11
|
+
this.logger.error('Please add "email" and "password" variables');
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const user = await User.create({
|
|
15
|
+
email,
|
|
16
|
+
password,
|
|
17
|
+
roles: roles?.split(','),
|
|
18
|
+
});
|
|
19
|
+
await user.generateToken();
|
|
20
|
+
|
|
21
|
+
this.logger.info(`User was created ${JSON.stringify(user, 0, 4)}`);
|
|
22
|
+
|
|
23
|
+
return user;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = CreateUser;
|
|
@@ -5,7 +5,7 @@ class Documentation extends AbstractCommand {
|
|
|
5
5
|
async run() {
|
|
6
6
|
const CM = new ControllerManager(this.app);
|
|
7
7
|
this.app.documentation = [];
|
|
8
|
-
await CM.initControllers(
|
|
8
|
+
await CM.initControllers();
|
|
9
9
|
return this.app.documentation;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const fs = require('fs').promises;
|
|
1
|
+
const fs = require('node:fs').promises;
|
|
2
2
|
const AbstractCommand = require('../modules/AbstractCommand');
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -165,6 +165,7 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
165
165
|
const routeTitle = route[Object.keys(route)[0]].name;
|
|
166
166
|
|
|
167
167
|
const routeFields = route[Object.keys(route)[0]].fields;
|
|
168
|
+
const routeQueryFields = route[Object.keys(route)[0]].queryFields;
|
|
168
169
|
|
|
169
170
|
if (!openApi.paths[routeName][methodName]) {
|
|
170
171
|
openApi.paths[routeName][methodName] = {};
|
|
@@ -202,6 +203,17 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
202
203
|
},
|
|
203
204
|
};
|
|
204
205
|
|
|
206
|
+
for (const queryField of routeQueryFields) {
|
|
207
|
+
openApi.paths[routeName][methodName].parameters.push({
|
|
208
|
+
name: queryField.name,
|
|
209
|
+
in: 'query',
|
|
210
|
+
required: queryField?.required,
|
|
211
|
+
schema: {
|
|
212
|
+
type: queryField.type,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
205
217
|
for (const routeField of routeParameters) {
|
|
206
218
|
openApi.paths[routeName][methodName].parameters.push({
|
|
207
219
|
name: routeField,
|
|
@@ -216,34 +228,36 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
216
228
|
if (routeFields.length) {
|
|
217
229
|
const groupBodyFields = {};
|
|
218
230
|
const requiredFields = [];
|
|
231
|
+
let isMultipartFormaData = false;
|
|
219
232
|
for (const field of routeFields) {
|
|
220
|
-
if (field.
|
|
233
|
+
if (field.required) {
|
|
221
234
|
requiredFields.push(field.name);
|
|
222
235
|
}
|
|
223
236
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
objectFields[objField.name] = {
|
|
229
|
-
// fields file has mixed type but openApi doesnt have this type
|
|
230
|
-
type: objField.type === 'mixed' ? 'string' : objField.type,
|
|
237
|
+
switch (field.type) {
|
|
238
|
+
case 'object':
|
|
239
|
+
groupBodyFields[field.name] = {
|
|
240
|
+
properties: {},
|
|
231
241
|
};
|
|
232
|
-
}
|
|
233
242
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
for (const objField of field.fields) {
|
|
244
|
+
groupBodyFields[field.name].properties[objField.name] = {
|
|
245
|
+
// fields file has mixed type but openApi doesnt have this type
|
|
246
|
+
type: objField.type === 'mixed' ? 'string' : objField.type,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
break;
|
|
239
251
|
|
|
240
|
-
|
|
241
|
-
groupBodyFields[field.name]
|
|
242
|
-
|
|
252
|
+
case 'array':
|
|
253
|
+
groupBodyFields[field.name] = {
|
|
254
|
+
items: {
|
|
255
|
+
type: field.innerType,
|
|
256
|
+
},
|
|
243
257
|
};
|
|
244
|
-
|
|
258
|
+
break;
|
|
245
259
|
|
|
246
|
-
|
|
260
|
+
case 'lazy':
|
|
247
261
|
groupBodyFields[field.name] = {
|
|
248
262
|
oneOf: [
|
|
249
263
|
{
|
|
@@ -254,13 +268,29 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
254
268
|
},
|
|
255
269
|
],
|
|
256
270
|
};
|
|
257
|
-
|
|
271
|
+
break;
|
|
272
|
+
|
|
273
|
+
case 'file':
|
|
274
|
+
groupBodyFields[field.name] = {
|
|
275
|
+
type: 'string',
|
|
276
|
+
format: 'binary',
|
|
277
|
+
};
|
|
278
|
+
isMultipartFormaData = true;
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
groupBodyFields[field.name] = {
|
|
282
|
+
type: field.type,
|
|
283
|
+
};
|
|
258
284
|
}
|
|
259
285
|
}
|
|
260
286
|
|
|
287
|
+
const contentType = isMultipartFormaData
|
|
288
|
+
? 'multipart/form-data'
|
|
289
|
+
: 'application/json';
|
|
290
|
+
|
|
261
291
|
openApi.paths[routeName][methodName].requestBody = {
|
|
262
292
|
content: {
|
|
263
|
-
|
|
293
|
+
[contentType]: {
|
|
264
294
|
schema: {
|
|
265
295
|
type: 'object',
|
|
266
296
|
properties: groupBodyFields,
|
|
@@ -271,7 +301,7 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
271
301
|
|
|
272
302
|
if (requiredFields.length) {
|
|
273
303
|
openApi.paths[routeName][methodName].requestBody.content[
|
|
274
|
-
|
|
304
|
+
contentType
|
|
275
305
|
].schema.required = requiredFields;
|
|
276
306
|
}
|
|
277
307
|
}
|
package/config/auth.js
CHANGED
package/config/i18n.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* You can use any options from https://www.i18next.com/overview/configuration-options
|
|
3
|
+
*/
|
|
1
4
|
module.exports = {
|
|
2
5
|
enabled: true,
|
|
3
6
|
preload: ['en', 'ru'],
|
|
7
|
+
supportedLngs: ['en', 'ru'], // should be at least one supported
|
|
4
8
|
fallbackLng: 'en',
|
|
5
9
|
saveMissing: false,
|
|
6
10
|
debug: false,
|
|
7
|
-
// https://github.com/i18next/i18next-http-middleware#detector-options
|
|
8
|
-
// in additional we have 'xLang' that detect language based on header 'xLang'
|
|
9
|
-
langDetectionOders: ['xLang'], // from order option
|
|
10
11
|
lookupQuerystring: 'lng', // string to detect language on query
|
|
11
12
|
};
|
package/config/mail.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
|
|
1
3
|
module.exports = {
|
|
2
4
|
from: 'Localhost <info@localhost>',
|
|
3
5
|
transports: {
|
|
@@ -20,6 +22,8 @@ module.exports = {
|
|
|
20
22
|
transport: process.env.EMAIL_TRANSPORT || 'smtp',
|
|
21
23
|
webResources: {
|
|
22
24
|
// https://github.com/jrit/web-resource-inliner path to find resources
|
|
23
|
-
relativeTo: '
|
|
25
|
+
relativeTo: path.resolve('src/services/messaging/email/resources'),
|
|
26
|
+
images: false,
|
|
24
27
|
},
|
|
28
|
+
globalVariablesToTemplates: {},
|
|
25
29
|
};
|
package/controllers/Home.js
CHANGED
|
@@ -12,11 +12,11 @@ class Home extends AbstractController {
|
|
|
12
12
|
|
|
13
13
|
// eslint-disable-next-line class-methods-use-this
|
|
14
14
|
async home(req, res) {
|
|
15
|
-
res.
|
|
15
|
+
return res.json({ message: 'Home' });
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// eslint-disable-next-line class-methods-use-this
|
|
19
|
-
|
|
19
|
+
getHttpPath() {
|
|
20
20
|
return '/';
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const request = require('supertest');
|
|
2
|
+
|
|
3
|
+
describe('home', () => {
|
|
4
|
+
it('can open home have', async () => {
|
|
5
|
+
expect.assertions(1);
|
|
6
|
+
const { status } = await request(global.server.app.httpServer.express).get(
|
|
7
|
+
'/',
|
|
8
|
+
);
|
|
9
|
+
expect(status).toBe(200);
|
|
10
|
+
});
|
|
11
|
+
});
|
package/controllers/index.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
const path = require('path');
|
|
1
|
+
const path = require('node:path');
|
|
2
2
|
const Base = require('../modules/Base');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Class do autoloading a http
|
|
5
|
+
* Class do autoloading a http controllers
|
|
6
6
|
*/
|
|
7
7
|
class ControllerManager extends Base {
|
|
8
8
|
constructor(app) {
|
|
9
9
|
super(app);
|
|
10
|
-
this.
|
|
10
|
+
this.controllers = {};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Load controllers
|
|
15
|
-
* @param {object} folderConfig
|
|
16
|
-
* @param {object} folderConfig.folders server folder config
|
|
17
|
-
* @param {string} folderConfig.controllers controller folder path
|
|
18
15
|
*/
|
|
19
|
-
async initControllers(
|
|
16
|
+
async initControllers() {
|
|
20
17
|
const controllersToLoad = await this.getFilesPathWithInheritance(
|
|
21
18
|
__dirname,
|
|
22
|
-
|
|
19
|
+
this.app.foldersConfig.controllers,
|
|
23
20
|
);
|
|
24
21
|
|
|
25
22
|
controllersToLoad.sort((a, b) => {
|
|
@@ -31,9 +28,12 @@ class ControllerManager extends Base {
|
|
|
31
28
|
}
|
|
32
29
|
return 0;
|
|
33
30
|
});
|
|
34
|
-
|
|
31
|
+
// const controllers = [];
|
|
35
32
|
for (const controller of controllersToLoad) {
|
|
36
|
-
//
|
|
33
|
+
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
34
|
+
// controllers.push(
|
|
35
|
+
// import(controller.path).then(({ default: ControllerModule }) => {
|
|
36
|
+
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
37
37
|
const ControllerModule = require(controller.path);
|
|
38
38
|
const contollerName = ControllerModule.name.toLowerCase();
|
|
39
39
|
let prefix = path.dirname(controller.file);
|
|
@@ -41,13 +41,13 @@ class ControllerManager extends Base {
|
|
|
41
41
|
prefix = '';
|
|
42
42
|
}
|
|
43
43
|
const controllePath = prefix
|
|
44
|
-
? `${
|
|
44
|
+
? `${prefix}/${contollerName}`
|
|
45
45
|
: contollerName;
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
46
|
+
this.controllers[controllePath] = new ControllerModule(this.app, prefix);
|
|
47
|
+
// }),
|
|
48
|
+
// );
|
|
50
49
|
}
|
|
50
|
+
// await Promise.all(controllers);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
static get loggerGroup() {
|
package/folderConfig.js
CHANGED
package/helpers/yup.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { Schema } = require('yup');
|
|
2
|
+
const formidable = require('formidable');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validator for file
|
|
6
|
+
* use as
|
|
7
|
+
* @example
|
|
8
|
+
* request: yup.object().shape({
|
|
9
|
+
* someFile: new YupFile().required(),
|
|
10
|
+
* })
|
|
11
|
+
*/
|
|
12
|
+
class YupFile extends Schema {
|
|
13
|
+
constructor() {
|
|
14
|
+
super({
|
|
15
|
+
type: 'file',
|
|
16
|
+
check: (value) => value instanceof formidable.PersistentFile,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
23
|
+
YupFile,
|
|
24
|
+
};
|
package/index.js
ADDED
package/models/User.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
const { scrypt } = require('node:crypto');
|
|
4
|
+
const { promisify } = require('node:util');
|
|
3
5
|
|
|
4
6
|
const AbstractModel = require('../modules/AbstractModel');
|
|
5
7
|
|
|
@@ -9,12 +11,12 @@ class User extends AbstractModel {
|
|
|
9
11
|
constructor(app) {
|
|
10
12
|
super(app);
|
|
11
13
|
const authConfig = this.app.getConfig('auth');
|
|
12
|
-
this.
|
|
14
|
+
this.hashRounds = authConfig.hashRounds;
|
|
13
15
|
this.saltSecret = authConfig.saltSecret;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
initHooks() {
|
|
17
|
-
this.mongooseSchema.pre('save', async function () {
|
|
19
|
+
this.mongooseSchema.pre('save', async function userPreSaveHook() {
|
|
18
20
|
if (this.isModified('password')) {
|
|
19
21
|
this.password = await this.constructor.hashPassword(this.password);
|
|
20
22
|
}
|
|
@@ -72,12 +74,9 @@ class User extends AbstractModel {
|
|
|
72
74
|
if (!data) {
|
|
73
75
|
return false;
|
|
74
76
|
}
|
|
75
|
-
const
|
|
76
|
-
String(password) + data.constructor.getSuper().saltSecret,
|
|
77
|
-
data.password,
|
|
78
|
-
);
|
|
77
|
+
const hashedPasswords = await this.hashPassword(password);
|
|
79
78
|
|
|
80
|
-
if (
|
|
79
|
+
if (data.password !== hashedPasswords) {
|
|
81
80
|
return false;
|
|
82
81
|
}
|
|
83
82
|
return data;
|
|
@@ -86,10 +85,13 @@ class User extends AbstractModel {
|
|
|
86
85
|
async generateToken() {
|
|
87
86
|
const timestamp = new Date();
|
|
88
87
|
timestamp.setDate(timestamp.getDate() + 30);
|
|
89
|
-
const
|
|
88
|
+
const scryptAsync = promisify(scrypt);
|
|
89
|
+
const data = await scryptAsync(
|
|
90
90
|
this.email + Date.now(),
|
|
91
|
-
this.constructor.getSuper().
|
|
91
|
+
this.constructor.getSuper().saltSecret,
|
|
92
|
+
this.constructor.getSuper().hashRounds,
|
|
92
93
|
);
|
|
94
|
+
const token = data.toString('base64url');
|
|
93
95
|
this.sessionTokens.push({ token, valid: timestamp });
|
|
94
96
|
await this.save();
|
|
95
97
|
return { token, valid: timestamp };
|
|
@@ -108,10 +110,13 @@ class User extends AbstractModel {
|
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
static async hashPassword(password) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
const scryptAsync = promisify(scrypt);
|
|
114
|
+
const data = await scryptAsync(
|
|
115
|
+
String(password),
|
|
116
|
+
this.getSuper().saltSecret,
|
|
117
|
+
this.getSuper().hashRounds,
|
|
114
118
|
);
|
|
119
|
+
return data.toString('base64url');
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
static async getUserByToken(token) {
|
|
@@ -130,10 +135,13 @@ class User extends AbstractModel {
|
|
|
130
135
|
static async generateUserPasswordRecoveryToken(userMongoose) {
|
|
131
136
|
const date = new Date();
|
|
132
137
|
date.setDate(date.getDate() + 14);
|
|
133
|
-
const
|
|
138
|
+
const scryptAsync = promisify(scrypt);
|
|
139
|
+
const data = await scryptAsync(
|
|
134
140
|
userMongoose.email + Date.now(),
|
|
135
|
-
userMongoose.constructor.getSuper().
|
|
141
|
+
userMongoose.constructor.getSuper().saltSecret,
|
|
142
|
+
userMongoose.constructor.getSuper().hashRounds,
|
|
136
143
|
);
|
|
144
|
+
const token = data.toString('base64url');
|
|
137
145
|
// if (err) {
|
|
138
146
|
// this.logger.error("Hash 2 error ", err);
|
|
139
147
|
// reject(err);
|
|
@@ -166,9 +174,8 @@ class User extends AbstractModel {
|
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
async sendPasswordRecoveryEmail(i18n) {
|
|
169
|
-
const passwordRecoveryToken =
|
|
170
|
-
this
|
|
171
|
-
);
|
|
177
|
+
const passwordRecoveryToken =
|
|
178
|
+
await User.generateUserPasswordRecoveryToken(this);
|
|
172
179
|
const mail = new Mailer(
|
|
173
180
|
this.constructor.getSuper().app,
|
|
174
181
|
'recovery',
|
|
@@ -184,10 +191,13 @@ class User extends AbstractModel {
|
|
|
184
191
|
static async generateUserVerificationToken(userMongoose) {
|
|
185
192
|
const date = new Date();
|
|
186
193
|
date.setDate(date.getDate() + 14);
|
|
187
|
-
const
|
|
194
|
+
const scryptAsync = promisify(scrypt);
|
|
195
|
+
const data = await scryptAsync(
|
|
188
196
|
userMongoose.email + Date.now(),
|
|
189
|
-
userMongoose.constructor.getSuper().
|
|
197
|
+
userMongoose.constructor.getSuper().saltSecret,
|
|
198
|
+
userMongoose.constructor.getSuper().hashRounds,
|
|
190
199
|
);
|
|
200
|
+
const token = data.toString('base64url');
|
|
191
201
|
// if (err) {
|
|
192
202
|
// this.logger.error("Hash 2 error ", err);
|
|
193
203
|
// reject(err);
|
|
@@ -219,16 +229,16 @@ class User extends AbstractModel {
|
|
|
219
229
|
return result;
|
|
220
230
|
}
|
|
221
231
|
|
|
222
|
-
removeVerificationToken(verificationToken) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
+
// async removeVerificationToken(verificationToken) {
|
|
233
|
+
// this.mongooseModel.updateOne(
|
|
234
|
+
// {
|
|
235
|
+
// verificationTokens: {
|
|
236
|
+
// $elemMatch: { token: String(verificationToken) },
|
|
237
|
+
// },
|
|
238
|
+
// },
|
|
239
|
+
// { $pop: { verificationTokens: 1 } },
|
|
240
|
+
// );
|
|
241
|
+
// }
|
|
232
242
|
|
|
233
243
|
async sendVerificationEmail(i18n) {
|
|
234
244
|
const verificationToken = await User.generateUserVerificationToken(this);
|