@adaptivestone/framework 5.0.0-beta.1 → 5.0.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/Cli.js +3 -4
  3. package/cluster.js +0 -2
  4. package/commands/CreateUser.js +31 -0
  5. package/commands/DropIndex.js +14 -5
  6. package/commands/GenerateRandomBytes.js +2 -0
  7. package/commands/GetOpenApiJson.js +13 -2
  8. package/commands/SyncIndexes.js +1 -0
  9. package/commands/migration/Create.js +21 -10
  10. package/commands/migration/Migrate.js +1 -0
  11. package/controllers/Auth.js +20 -29
  12. package/eslint.config.js +68 -0
  13. package/folderConfig.js +0 -1
  14. package/helpers/files.js +1 -4
  15. package/helpers/logger.js +0 -1
  16. package/helpers/yup.js +2 -4
  17. package/jsconfig.json +4 -4
  18. package/models/Lock.js +107 -0
  19. package/models/User.js +23 -6
  20. package/modules/AbstractCommand.js +25 -1
  21. package/modules/AbstractController.js +0 -7
  22. package/modules/AbstractModel.js +21 -9
  23. package/modules/Base.js +3 -2
  24. package/modules/BaseCli.js +82 -9
  25. package/package.json +20 -16
  26. package/server.d.ts +9 -7
  27. package/server.js +44 -10
  28. package/services/cache/Cache.d.ts +1 -1
  29. package/services/cache/Cache.js +3 -3
  30. package/services/http/HttpServer.js +6 -13
  31. package/services/http/middleware/AbstractMiddleware.js +3 -3
  32. package/services/http/middleware/I18n.js +1 -1
  33. package/services/http/middleware/Pagination.js +4 -4
  34. package/services/http/middleware/RateLimiter.js +2 -2
  35. package/services/messaging/email/templates/.gitkeep +0 -0
  36. package/services/validate/ValidateService.js +4 -4
  37. package/services/validate/drivers/AbstractValidator.js +2 -2
  38. package/services/validate/drivers/CustomValidator.js +2 -2
  39. package/services/validate/drivers/YupValidator.js +3 -3
  40. package/tests/setup.js +8 -6
  41. package/tests/setupVitest.js +9 -7
  42. package/types/ICommandArguments.d.ts +41 -0
  43. package/types/TFoldersConfig.d.ts +7 -4
  44. package/vitest.config.js +4 -3
  45. package/config/mail.js +0 -29
  46. package/services/messaging/email/index.js +0 -217
  47. package/services/messaging/email/templates/emptyTemplate/html.pug +0 -9
  48. package/services/messaging/email/templates/emptyTemplate/subject.pug +0 -1
  49. package/services/messaging/email/templates/emptyTemplate/text.pug +0 -1
  50. package/services/messaging/index.js +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ ### 5.0.0-beta.9
2
+
3
+ [BREAKING] move email module to separate package @adaptivestone/framework-module-email. Please use it if you want to send emails
4
+ [NEW] app now contains 'frameworkFolder' folder the framework located. Mostly for modules usage
5
+ [BREAKING] remove VIEWS folders at all. Should not afffect any user as this was not used internally
6
+ [UPDATE] update typing
7
+ [UPDATE] change redis -> @redis/client as we are using only client from pakage
8
+ [BREAKING] removed noidemailer-sendmail-transport. Not needed anymore and not recommended to use as well
9
+
10
+ ### 5.0.0-beta.8
11
+
12
+ [UPDATE] update deps
13
+ [NEW] Lock model for working locks via mongoDB
14
+
15
+ ### 5.0.0-beta.7
16
+
17
+ [UPDATE] update deps
18
+ [UPDATE] change vitest shutdown behavior as mongo driver v6.13 change befaviur that affect us (MongoClient.close now closes any outstanding cursors)
19
+
20
+ ### 5.0.0-beta.5
21
+
22
+ [BREAKING] remove minimist CLI parsing and replace it by commandArguments parser
23
+ [UPDATE] migrated from eslint-plugin-import to eslint-plugin-import-x
24
+
25
+ ### 5.0.0-beta.5
26
+
27
+ [UPDATE] migrate to eslint 9 and away from aibnb styles (they are abonded)
28
+
29
+ ### 5.0.0-beta.4
30
+
31
+ [NEW] on shutdown event now after timeout we are forcing to shutdown
32
+
33
+ ### 5.0.0-beta.2
34
+
35
+ [UPDATE] update deps
36
+ [NEW] add ability to skip mongo model init in CLI env
37
+ [NEW] now each mongo connection on CLI have own name and inslude command name there too (getMongoConnectionName in command)
38
+
1
39
  ### 5.0.0-beta.1
2
40
 
3
41
  [UPDATE] update deps
@@ -452,7 +490,7 @@ await doc.populate([
452
490
  [UPDATE] update deps
453
491
  [UPDATE] winston console transport now using timestapms
454
492
  [UPDATE] PrepareAppInfo middleware now a global one. Do not need to include it on every controller
455
- [NEW] Request anso works with req.query, but req.body have bigger priority
493
+ [NEW] Request also works with req.query, but req.body have bigger priority
456
494
 
457
495
  ### 2.18.0
458
496
 
package/Cli.js CHANGED
@@ -1,4 +1,3 @@
1
- import parseArgs from 'minimist';
2
1
  import mongoose from 'mongoose';
3
2
  import BaseCli from './modules/BaseCli.js';
4
3
  import Server from './server.js';
@@ -8,14 +7,14 @@ class Cli extends BaseCli {
8
7
  mongoose.set('autoIndex', false); // we do not need create indexes on CLI.
9
8
  const server = new Server(serverConfig);
10
9
  super(server);
11
- this.args = parseArgs(process.argv.slice(3));
12
10
  }
13
11
 
14
12
  async run() {
15
- await this.server.init();
13
+ await this.server.init({ isSkipModelInit: true, isSkipModelLoading: true });
16
14
  const command = process.argv[2]?.toLowerCase();
17
- await super.run(command, this.args);
15
+ await super.run(command);
18
16
  this.app.events.emit('shutdown');
17
+ return true;
19
18
  }
20
19
  }
21
20
 
package/cluster.js CHANGED
@@ -4,7 +4,6 @@ import { cpus } from 'node:os';
4
4
  const numCPUs = cpus().length;
5
5
 
6
6
  if (cluster.isPrimary) {
7
- // eslint-disable-next-line no-console
8
7
  console.log(`Master ${process.pid} is running`);
9
8
  // Fork workers.
10
9
  for (let i = 0; i < numCPUs; i += 1) {
@@ -12,7 +11,6 @@ if (cluster.isPrimary) {
12
11
  }
13
12
 
14
13
  cluster.on('exit', (worker, code, signal) => {
15
- // eslint-disable-next-line no-console
16
14
  console.log(
17
15
  `Worker \x1B[45m ${
18
16
  worker.process.pid
@@ -6,6 +6,37 @@ class CreateUser extends AbstractCommand {
6
6
  return 'Create user in a database';
7
7
  }
8
8
 
9
+ /**
10
+ * You able to add command arguments for parsing there.
11
+ * @returns {import("../types/ICommandArguments.js").ICommandArguments}
12
+ */
13
+ static get commandArguments() {
14
+ return {
15
+ id: {
16
+ type: 'string',
17
+ description: 'User id to find user',
18
+ },
19
+ email: {
20
+ type: 'string',
21
+ description: 'User id to find/create user',
22
+ },
23
+ password: {
24
+ type: 'string',
25
+ description: 'New password for user',
26
+ },
27
+ roles: {
28
+ type: 'string',
29
+ description:
30
+ 'User roles comma separated string (--roles=user,admin,someOtherRoles)',
31
+ },
32
+ update: {
33
+ type: 'boolean',
34
+ default: false,
35
+ description: 'Update user if it exists',
36
+ },
37
+ };
38
+ }
39
+
9
40
  async run() {
10
41
  const User = this.app.getModel('User');
11
42
  const { id, email, password, roles, update } = this.args;
@@ -5,12 +5,21 @@ class DropIndex extends AbstractCommand {
5
5
  return 'Drop indexes of model';
6
6
  }
7
7
 
8
- async run() {
9
- if (!this.args.model) {
10
- this.logger.error('Please provide model name as "--model=BestUserModel"');
11
- return false;
12
- }
8
+ /**
9
+ * You able to add command arguments for parsing there.
10
+ * @returns {import("../types/ICommandArguments.js").ICommandArguments}
11
+ */
12
+ static get commandArguments() {
13
+ return {
14
+ model: {
15
+ type: 'string',
16
+ description: 'Model name',
17
+ required: true,
18
+ },
19
+ };
20
+ }
13
21
 
22
+ async run() {
14
23
  const Model = this.app.getModel(this.args.model);
15
24
 
16
25
  if (!Model) {
@@ -6,6 +6,8 @@ class GenerateRandomBytes extends AbstractCommand {
6
6
  return 'Generate random bytes ising randomBytes from node:crypto';
7
7
  }
8
8
 
9
+ static isShouldInitModels = false;
10
+
9
11
  async run() {
10
12
  const sizes = [16, 32, 64, 128, 256];
11
13
  for (const size of sizes) {
@@ -8,6 +8,18 @@ class GetOpenApiJson extends AbstractCommand {
8
8
  static get description() {
9
9
  return 'Generate documentation (openApi) ';
10
10
  }
11
+ /**
12
+ * You able to add command arguments for parsing there.
13
+ * @returns {import("../types/ICommandArguments.js").ICommandArguments}
14
+ */
15
+ static get commandArguments() {
16
+ return {
17
+ output: {
18
+ type: 'string',
19
+ description: 'Output file path',
20
+ },
21
+ };
22
+ }
11
23
 
12
24
  async run() {
13
25
  const { myDomain } = this.app.getConfig('http');
@@ -18,7 +30,7 @@ class GetOpenApiJson extends AbstractCommand {
18
30
 
19
31
  try {
20
32
  jsonFile = JSON.parse(await fs.readFile(jsonFile, 'utf8'));
21
- } catch (e) {
33
+ } catch {
22
34
  this.logger.error(
23
35
  'No npm package detected. Please start this command via NPM as it depends on package.json',
24
36
  );
@@ -123,7 +135,6 @@ class GetOpenApiJson extends AbstractCommand {
123
135
 
124
136
  let routeName = Object.keys(route)[0];
125
137
  if (routeName === '/') {
126
- // eslint-disable-next-line no-continue
127
138
  continue;
128
139
  }
129
140
 
@@ -29,6 +29,7 @@ class SyncIndexes extends AbstractCommand {
29
29
  this.logger.info(`Model - ${modelName} NO removed indexes`);
30
30
  }
31
31
  }
32
+ return true;
32
33
  }
33
34
 
34
35
  static get description() {
@@ -7,28 +7,39 @@ class CreateMigration extends AbstractCommand {
7
7
  return 'Create new migration';
8
8
  }
9
9
 
10
+ /**
11
+ * You able to add command arguments for parsing there.
12
+ * @returns {import("../../types/ICommandArguments.js").ICommandArguments}
13
+ */
14
+ static get commandArguments() {
15
+ return {
16
+ name: {
17
+ type: 'string',
18
+ description: 'Migration name',
19
+ required: true,
20
+ },
21
+ };
22
+ }
23
+
10
24
  async run() {
11
- if (!this.args.name) {
12
- return this.logger.error(
13
- 'Please provide migration name with key "--name={someName}"',
14
- );
15
- }
16
25
  if (this.args.name.match(/^\d/)) {
17
- return this.logger.error('Command cant start from nubmer');
26
+ this.logger.error('Command cant start from nubmer');
27
+ return false;
18
28
  }
19
- const fileName = `${Date.now()}_${this.constructor.camelSentence(
29
+ const fileName = `${Date.now()}_${CreateMigration.camelSentence(
20
30
  this.args.name,
21
31
  )}.js`;
22
32
 
23
- const fileContent = this.constructor.getTemplate(
24
- this.constructor.camelSentence(this.args.name),
33
+ const fileContent = CreateMigration.getTemplate(
34
+ CreateMigration.camelSentence(this.args.name),
25
35
  );
26
36
 
27
37
  await fs.writeFile(
28
38
  path.join(this.app.foldersConfig.migrations, fileName),
29
39
  fileContent,
30
40
  );
31
- return this.logger.info(`Migration created ${fileName}`);
41
+ this.logger.info(`Migration created ${fileName}`);
42
+ return true;
32
43
  }
33
44
 
34
45
  static camelSentence(str) {
@@ -49,6 +49,7 @@ class Migrate extends AbstractCommand {
49
49
  this.logger.info(
50
50
  `=== Migration Finished. Migrated ${migrations.length} files ===`,
51
51
  );
52
+ return true;
52
53
  }
53
54
  }
54
55
 
@@ -1,4 +1,4 @@
1
- import yup from 'yup';
1
+ import { object, string } from 'yup';
2
2
  import AbstractController from '../modules/AbstractController.js';
3
3
  import GetUserByToken from '../services/http/middleware/GetUserByToken.js';
4
4
  import RateLimiter from '../services/http/middleware/RateLimiter.js';
@@ -9,58 +9,52 @@ class Auth extends AbstractController {
9
9
  post: {
10
10
  '/login': {
11
11
  handler: this.postLogin,
12
- request: yup.object().shape({
13
- email: yup.string().email().required('auth.emailProvided'), // if not provided then error will be generated
14
- password: yup.string().required('auth.passwordProvided'), // possible to provide values from translation
12
+ request: object().shape({
13
+ email: string().email().required('auth.emailProvided'), // if not provided then error will be generated
14
+ password: string().required('auth.passwordProvided'), // possible to provide values from translation
15
15
  }),
16
16
  },
17
17
  '/register': {
18
18
  handler: this.postRegister,
19
- request: yup.object().shape({
20
- email: yup
21
- .string()
19
+ request: object().shape({
20
+ email: string()
22
21
  .email('auth.emailValid')
23
22
  .required('auth.emailProvided'),
24
- password: yup
25
- .string()
23
+ password: string()
26
24
  .matches(
27
25
  /^[a-zA-Z0-9!@#$%ˆ^&*()_+\-{}[\]<>]+$/,
28
26
  'auth.passwordValid',
29
27
  )
30
28
  .required('auth.passwordProvided'),
31
- nickName: yup
32
- .string()
33
- .matches(/^[a-zA-Z0-9_\-.]+$/, 'auth.nickNameValid'),
34
- firstName: yup.string(),
35
- lastName: yup.string(),
29
+ nickName: string().matches(
30
+ /^[a-zA-Z0-9_\-.]+$/,
31
+ 'auth.nickNameValid',
32
+ ),
33
+ firstName: string(),
34
+ lastName: string(),
36
35
  }),
37
36
  },
38
37
  '/logout': this.postLogout,
39
38
  '/verify': this.verifyUser,
40
39
  '/send-recovery-email': {
41
40
  handler: this.sendPasswordRecoveryEmail,
42
- request: yup
43
- .object()
44
- .shape({ email: yup.string().email().required() }),
41
+ request: object().shape({ email: string().email().required() }),
45
42
  },
46
43
  '/recover-password': {
47
44
  handler: this.recoverPassword,
48
- request: yup.object().shape({
49
- password: yup
50
- .string()
45
+ request: object().shape({
46
+ password: string()
51
47
  .matches(
52
48
  /^[a-zA-Z0-9!@#$%ˆ^&*()_+\-{}[\]<>]+$/,
53
49
  'auth.passwordValid',
54
50
  )
55
51
  .required(),
56
- passwordRecoveryToken: yup.string().required(),
52
+ passwordRecoveryToken: string().required(),
57
53
  }),
58
54
  },
59
55
  '/send-verification': {
60
56
  handler: this.sendVerification,
61
- request: yup
62
- .object()
63
- .shape({ email: yup.string().email().required() }),
57
+ request: object().shape({ email: string().email().required() }),
64
58
  },
65
59
  },
66
60
  };
@@ -113,12 +107,9 @@ class Auth extends AbstractController {
113
107
 
114
108
  const { isAuthWithVefificationFlow } = this.app.getConfig('auth');
115
109
  if (isAuthWithVefificationFlow) {
116
- const answer = await user.sendVerificationEmail(req.i18n).catch((e) => {
110
+ await user.sendVerificationEmail(req.i18n).catch((e) => {
117
111
  this.logger.error(e);
118
112
  });
119
- if (!answer) {
120
- return res.status(500).json();
121
- }
122
113
  }
123
114
  return res.status(201).json();
124
115
  }
@@ -136,7 +127,7 @@ class Auth extends AbstractController {
136
127
  user = await User.getUserByVerificationToken(
137
128
  req.query.verification_token,
138
129
  );
139
- } catch (e) {
130
+ } catch {
140
131
  return res.status(400).json({
141
132
  message: req.i18n.t('email.alreadyVerifiedOrWrongToken'),
142
133
  });
@@ -0,0 +1,68 @@
1
+ import globals from 'globals';
2
+ import pluginJs from '@eslint/js';
3
+ import importPlugin from 'eslint-plugin-import-x';
4
+ import vitest from '@vitest/eslint-plugin';
5
+ import eslintConfigPrettier from 'eslint-config-prettier';
6
+ import prettierPlugin from 'eslint-plugin-prettier/recommended';
7
+
8
+ /** @type {import('eslint').Linter.Config[]} */
9
+ // @ts-ignore
10
+ export default [
11
+ pluginJs.configs.recommended,
12
+ importPlugin.flatConfigs.recommended,
13
+ eslintConfigPrettier,
14
+ prettierPlugin,
15
+ {
16
+ languageOptions: {
17
+ sourceType: 'module',
18
+ ecmaVersion: 'latest',
19
+ globals: {
20
+ ...globals.es2023,
21
+ ...globals.node,
22
+ },
23
+ },
24
+ },
25
+ {
26
+ rules: {
27
+ 'no-await-in-loop': 'error',
28
+ 'no-param-reassign': 'error',
29
+ 'class-methods-use-this': 'error',
30
+ 'no-shadow': 'error',
31
+ 'prefer-const': 'error',
32
+ 'import-x/no-extraneous-dependencies': ['error'],
33
+ 'import-x/first': ['error'],
34
+ camelcase: ['error', { properties: 'never', ignoreDestructuring: false }],
35
+ 'prefer-destructuring': [
36
+ 'error',
37
+ {
38
+ VariableDeclarator: {
39
+ array: false,
40
+ object: true,
41
+ },
42
+ AssignmentExpression: {
43
+ array: true,
44
+ object: false,
45
+ },
46
+ },
47
+ {
48
+ enforceForRenamedProperties: false,
49
+ },
50
+ ],
51
+ 'no-plusplus': 'error',
52
+ 'consistent-return': 'error',
53
+ 'no-return-await': 'error',
54
+ 'arrow-body-style': 'error',
55
+ 'dot-notation': 'error',
56
+ curly: 'error',
57
+ },
58
+ },
59
+ {
60
+ files: ['**/*.test.js'],
61
+ plugins: {
62
+ vitest,
63
+ },
64
+ rules: {
65
+ ...vitest.configs.recommended.rules,
66
+ },
67
+ },
68
+ ];
package/folderConfig.js CHANGED
@@ -5,7 +5,6 @@ export default {
5
5
  config: path.resolve('./config'),
6
6
  models: path.resolve('./models'),
7
7
  controllers: path.resolve('./controllers'),
8
- views: path.resolve('./views'),
9
8
  locales: path.resolve('./locales'),
10
9
  emails: path.resolve('./services/messaging/email/templates'),
11
10
  commands: path.resolve('./commands'),
package/helpers/files.js CHANGED
@@ -73,7 +73,4 @@ const getFilesPathWithInheritance = async ({
73
73
  return filesToLoad;
74
74
  };
75
75
 
76
- export {
77
- // eslint-disable-next-line import/prefer-default-export
78
- getFilesPathWithInheritance,
79
- };
76
+ export { getFilesPathWithInheritance };
package/helpers/logger.js CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable no-console */
2
1
  const levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'];
3
2
 
4
3
  const consoleLogger = (level, message) => {
package/helpers/yup.js CHANGED
@@ -13,12 +13,10 @@ class YupFile extends Schema {
13
13
  constructor() {
14
14
  super({
15
15
  type: 'file',
16
+ // @ts-ignore
16
17
  check: (value) => value.every((item) => item instanceof PersistentFile),
17
18
  });
18
19
  }
19
20
  }
20
21
 
21
- export {
22
- // eslint-disable-next-line import/prefer-default-export
23
- YupFile,
24
- };
22
+ export { YupFile };
package/jsconfig.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "module": "node16",
4
- "target": "ES2022",
5
- "moduleResolution": "node16",
3
+ "module": "NodeNext",
4
+ "target": "ES2024",
5
+ "moduleResolution": "nodenext",
6
6
  "checkJs": true
7
7
  },
8
- "exclude": ["node_modules"]
8
+ "exclude": ["node_modules", "coverage"]
9
9
  }
package/models/Lock.js ADDED
@@ -0,0 +1,107 @@
1
+ import AbstractModel from '../modules/AbstractModel.js';
2
+
3
+ class Lock extends AbstractModel {
4
+ initHooks() {
5
+ this.mongooseSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
6
+ }
7
+
8
+ // eslint-disable-next-line class-methods-use-this
9
+ get modelSchema() {
10
+ return {
11
+ _id: { type: String, required: true },
12
+ expiredAt: {
13
+ type: Date,
14
+ },
15
+ };
16
+ }
17
+
18
+ /**
19
+ * acquire lock based on lock name
20
+ * @param {string} name
21
+ * @param {number} [ttlSeconds=30]
22
+ * @returns {Promise<boolean>}
23
+ */
24
+ static async acquireLock(name, ttlSeconds = 30) {
25
+ try {
26
+ await this.create({
27
+ _id: name,
28
+ expiredAt: new Date(Date.now() + ttlSeconds * 1000),
29
+ });
30
+ } catch (error) {
31
+ if (error.code !== 11000) {
32
+ // not a duplicate leys
33
+ throw error;
34
+ }
35
+ return false;
36
+ }
37
+ return true;
38
+ }
39
+
40
+ /**
41
+ * release lock based on lock name
42
+ * @param {string} name
43
+ * @returns {Promise<boolean>}
44
+ */
45
+ static async releaseLock(name) {
46
+ const res = await this.deleteOne({ _id: name });
47
+ if (res.acknowledged && res.deletedCount) {
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+
53
+ /**
54
+ * wait lock based on lock name
55
+ * @param {string} name
56
+ * @returns {Promise}
57
+ */
58
+ static async waitForUnlock(name) {
59
+ const res = await this.findOne({ _id: name });
60
+ if (!res) {
61
+ return Promise.resolve();
62
+ }
63
+
64
+ return new Promise((resolve) => {
65
+ const stream = this.watch([
66
+ { $match: { operationType: 'delete', 'documentKey._id': name } },
67
+ ]);
68
+ stream.on('change', () => {
69
+ stream.close();
70
+ resolve();
71
+ });
72
+ });
73
+ }
74
+
75
+ /**
76
+ * get lock remaining time based on lock name
77
+ * @param {string} name
78
+ * @returns {Promise<{ttl: number}>}
79
+ */
80
+ static async getLockData(name) {
81
+ const res = await this.findOne({ _id: name });
82
+ if (!res) {
83
+ return { ttl: 0 };
84
+ }
85
+ return { ttl: res.expiredAt.getTime() - Date.now() };
86
+ }
87
+
88
+ /**
89
+ * get lock remaining time based on lock name
90
+ * @param {string[]} names
91
+ * @returns {Promise<{name: string, ttl: number}[]>}
92
+ */
93
+ static async getLocksData(names) {
94
+ const res = await this.find({ _id: { $in: names } });
95
+ const lockMap = new Map(res.map((lock) => [lock._id, lock]));
96
+
97
+ return names.map((name) => {
98
+ const lock = lockMap.get(name);
99
+ return {
100
+ name,
101
+ ttl: lock ? lock.expiredAt.getTime() - Date.now() : 0,
102
+ };
103
+ });
104
+ }
105
+ }
106
+
107
+ export default Lock;
package/models/User.js CHANGED
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-param-reassign */
2
-
3
1
  import { scrypt } from 'node:crypto';
4
2
 
5
3
  import { promisify } from 'node:util';
@@ -16,6 +14,7 @@ class User extends AbstractModel {
16
14
  initHooks() {
17
15
  this.mongooseSchema.pre('save', async function userPreSaveHook() {
18
16
  if (this.isModified('password')) {
17
+ // @ts-ignore
19
18
  this.password = await this.constructor.hashPassword(this.password);
20
19
  }
21
20
  });
@@ -174,9 +173,18 @@ class User extends AbstractModel {
174
173
  async sendPasswordRecoveryEmail(i18n) {
175
174
  const passwordRecoveryToken =
176
175
  await User.generateUserPasswordRecoveryToken(this);
176
+ let Mailer;
177
177
  // speed optimisation
178
- const Mailer = (await import('../services/messaging/email/index.js'))
179
- .default;
178
+ try {
179
+ // @ts-ignore
180
+ // eslint-disable-next-line import-x/no-unresolved
181
+ Mailer = (await import('@adaptivestone/framework-module-email')).default;
182
+ } catch {
183
+ const error =
184
+ 'Mailer not found. Please install @adaptivestone/framework-module-email in order to use it';
185
+ this.getSuper().logger.error(error);
186
+ return false;
187
+ }
180
188
 
181
189
  const mail = new Mailer(
182
190
  this.getSuper().app,
@@ -245,8 +253,17 @@ class User extends AbstractModel {
245
253
  async sendVerificationEmail(i18n) {
246
254
  const verificationToken = await User.generateUserVerificationToken(this);
247
255
  // speed optimisation
248
- const Mailer = (await import('../services/messaging/email/index.js'))
249
- .default;
256
+ let Mailer;
257
+ try {
258
+ // @ts-ignore
259
+ // eslint-disable-next-line import-x/no-unresolved
260
+ Mailer = (await import('@adaptivestone/framework-module-email')).default;
261
+ } catch {
262
+ const error =
263
+ 'Mailer not found. Please install @adaptivestone/framework-module-email in order to use it';
264
+ this.getSuper().logger.error(error);
265
+ return false;
266
+ }
250
267
  const mail = new Mailer(
251
268
  this.getSuper().app,
252
269
  'verification',