@lenne.tech/nest-server 8.6.25 → 8.6.28

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 (53) hide show
  1. package/dist/config.env.js +3 -0
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/helpers/input.helper.d.ts +10 -1
  4. package/dist/core/common/helpers/input.helper.js +46 -4
  5. package/dist/core/common/helpers/input.helper.js.map +1 -1
  6. package/dist/core/common/helpers/model.helper.d.ts +4 -0
  7. package/dist/core/common/helpers/model.helper.js +10 -3
  8. package/dist/core/common/helpers/model.helper.js.map +1 -1
  9. package/dist/core/common/helpers/service.helper.d.ts +12 -0
  10. package/dist/core/common/helpers/service.helper.js +15 -6
  11. package/dist/core/common/helpers/service.helper.js.map +1 -1
  12. package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
  13. package/dist/core/common/services/config.service.d.ts +75 -5
  14. package/dist/core/common/services/config.service.js +168 -4
  15. package/dist/core/common/services/config.service.js.map +1 -1
  16. package/dist/core/common/services/email.service.js +2 -2
  17. package/dist/core/common/services/email.service.js.map +1 -1
  18. package/dist/core/common/services/mailjet.service.js +4 -4
  19. package/dist/core/common/services/mailjet.service.js.map +1 -1
  20. package/dist/core/common/services/template.service.js +1 -1
  21. package/dist/core/common/services/template.service.js.map +1 -1
  22. package/dist/core/modules/user/core-user.service.d.ts +5 -3
  23. package/dist/core/modules/user/core-user.service.js +5 -2
  24. package/dist/core/modules/user/core-user.service.js.map +1 -1
  25. package/dist/server/common/services/cron-jobs.service.d.ts +3 -1
  26. package/dist/server/common/services/cron-jobs.service.js +5 -4
  27. package/dist/server/common/services/cron-jobs.service.js.map +1 -1
  28. package/dist/server/modules/auth/auth.service.d.ts +3 -1
  29. package/dist/server/modules/auth/auth.service.js +9 -4
  30. package/dist/server/modules/auth/auth.service.js.map +1 -1
  31. package/dist/server/modules/user/user.resolver.js +1 -2
  32. package/dist/server/modules/user/user.resolver.js.map +1 -1
  33. package/dist/server/modules/user/user.service.js +5 -3
  34. package/dist/server/modules/user/user.service.js.map +1 -1
  35. package/dist/server/server.controller.js +2 -2
  36. package/dist/server/server.controller.js.map +1 -1
  37. package/dist/tsconfig.build.tsbuildinfo +1 -1
  38. package/package.json +9 -8
  39. package/src/config.env.ts +3 -0
  40. package/src/core/common/helpers/input.helper.ts +68 -4
  41. package/src/core/common/helpers/model.helper.ts +14 -3
  42. package/src/core/common/helpers/service.helper.ts +27 -6
  43. package/src/core/common/interfaces/server-options.interface.ts +6 -0
  44. package/src/core/common/services/config.service.ts +378 -12
  45. package/src/core/common/services/email.service.ts +2 -2
  46. package/src/core/common/services/mailjet.service.ts +4 -4
  47. package/src/core/common/services/template.service.ts +1 -1
  48. package/src/core/modules/user/core-user.service.ts +7 -3
  49. package/src/server/common/services/cron-jobs.service.ts +3 -3
  50. package/src/server/modules/auth/auth.service.ts +7 -3
  51. package/src/server/modules/user/user.resolver.ts +1 -2
  52. package/src/server/modules/user/user.service.ts +5 -3
  53. package/src/server/server.controller.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.6.25",
3
+ "version": "8.6.28",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
@@ -82,7 +82,7 @@
82
82
  "light-my-request": "5.0.0",
83
83
  "lodash": "4.17.21",
84
84
  "mongodb": "4.7.0",
85
- "mongoose": "6.4.1",
85
+ "mongoose": "6.4.2",
86
86
  "mongoose-gridfs": "1.3.0",
87
87
  "multer": "1.4.5-lts.1",
88
88
  "node-mailjet": "5.0.1",
@@ -91,6 +91,7 @@
91
91
  "passport": "0.6.0",
92
92
  "passport-jwt": "4.0.0",
93
93
  "reflect-metadata": "0.1.13",
94
+ "rfdc": "1.3.0",
94
95
  "rimraf": "3.0.2",
95
96
  "rxjs": "7.5.5"
96
97
  },
@@ -101,14 +102,14 @@
101
102
  "@types/jest": "28.1.4",
102
103
  "@types/lodash": "4.14.182",
103
104
  "@types/multer": "1.4.7",
104
- "@types/node": "18.0.0",
105
+ "@types/node": "18.0.1",
105
106
  "@types/nodemailer": "6.4.4",
106
107
  "@types/passport": "1.0.9",
107
108
  "@types/supertest": "2.0.12",
108
- "@typescript-eslint/eslint-plugin": "5.30.0",
109
- "@typescript-eslint/parser": "5.30.0",
109
+ "@typescript-eslint/eslint-plugin": "5.30.5",
110
+ "@typescript-eslint/parser": "5.30.5",
110
111
  "coffeescript": "2.7.0",
111
- "eslint": "8.18.0",
112
+ "eslint": "8.19.0",
112
113
  "eslint-config-prettier": "8.5.0",
113
114
  "find-file-up": "2.0.1",
114
115
  "grunt": "1.5.3",
@@ -121,10 +122,10 @@
121
122
  "pm2": "5.2.0",
122
123
  "prettier": "2.7.1",
123
124
  "pretty-quick": "3.1.3",
124
- "supertest": "6.2.3",
125
+ "supertest": "6.2.4",
125
126
  "ts-jest": "28.0.5",
126
127
  "ts-morph": "15.1.0",
127
- "ts-node": "10.8.1",
128
+ "ts-node": "10.8.2",
128
129
  "tsconfig-paths": "4.0.0",
129
130
  "typescript": "4.7.4"
130
131
  },
package/src/config.env.ts CHANGED
@@ -55,6 +55,7 @@ const config: { [env: string]: IServerOptions } = {
55
55
  uri: 'mongodb://localhost/nest-server-dev',
56
56
  },
57
57
  port: 3000,
58
+ sha256: true,
58
59
  staticAssets: {
59
60
  path: join(__dirname, '..', 'public'),
60
61
  options: { prefix: '' },
@@ -105,6 +106,7 @@ const config: { [env: string]: IServerOptions } = {
105
106
  uri: 'mongodb://localhost/nest-server-dev',
106
107
  },
107
108
  port: 3000,
109
+ sha256: true,
108
110
  staticAssets: {
109
111
  path: join(__dirname, '..', 'public'),
110
112
  options: { prefix: '' },
@@ -155,6 +157,7 @@ const config: { [env: string]: IServerOptions } = {
155
157
  uri: 'mongodb://localhost/nest-server-prod',
156
158
  },
157
159
  port: 3000,
160
+ sha256: true,
158
161
  staticAssets: {
159
162
  path: join(__dirname, '..', 'public'),
160
163
  options: { prefix: '' },
@@ -3,6 +3,7 @@ import { plainToInstance } from 'class-transformer';
3
3
  import { validate } from 'class-validator';
4
4
  import { ValidatorOptions } from 'class-validator/types/validation/ValidatorOptions';
5
5
  import * as _ from 'lodash';
6
+ import * as rfdc from 'rfdc';
6
7
  import { checkRestricted } from '../decorators/restricted.decorator';
7
8
  import { ProcessType } from '../enums/process-type.enum';
8
9
  import { RoleEnum } from '../enums/role.enum';
@@ -196,7 +197,7 @@ export function assignPlain(target: Record<any, any>, ...args: Record<any, any>[
196
197
  ? // Return item if not an object
197
198
  item
198
199
  : // Return cloned record with undefined properties removed
199
- filterProperties(JSON.parse(JSON.stringify(item)), (prop) => prop !== undefined)
200
+ filterProperties(clone(item, { circles: false }), (prop) => prop !== undefined)
200
201
  )
201
202
  );
202
203
  }
@@ -297,6 +298,44 @@ export async function check(
297
298
  return value;
298
299
  }
299
300
 
301
+ /**
302
+ * Clone object
303
+ * @param object Any object
304
+ * @param options Finetuning of rfdc cloning
305
+ * @param options.proto Copy prototype properties as well as own properties into the new object.
306
+ * It's marginally faster to allow enumerable properties on the prototype to be copied into the
307
+ * cloned object (not onto it's prototype, directly onto the object).
308
+ * @param options.circles Keeping track of circular references will slow down performance with an additional 25% overhead.
309
+ * Even if an object doesn't have any circular references, the tracking overhead is the cost.
310
+ * By default if an object with a circular reference is passed to rfdc, it will throw
311
+ * (similar to how JSON.stringify would throw). Use the circles option to detect and preserve
312
+ * circular references in the object. If performance is important, try removing the circular
313
+ * reference from the object (set to undefined) and then add it back manually after cloning
314
+ * instead of using this option.
315
+ */
316
+ export function clone(object: any, options?: { proto?: boolean; circles?: boolean }) {
317
+ const config = {
318
+ proto: false,
319
+ circles: true,
320
+ ...options,
321
+ };
322
+ try {
323
+ return rfdc(config)(object);
324
+ } catch (e) {
325
+ console.info(e);
326
+ if (object.circle) {
327
+ try {
328
+ return rfdc({ ...config, ...{ circles: true } })(object);
329
+ } catch (e) {
330
+ console.info(e);
331
+ return _.clone(object);
332
+ }
333
+ } else {
334
+ return _.clone(object);
335
+ }
336
+ }
337
+ }
338
+
300
339
  /**
301
340
  * Combines objects to a new single plain object and ignores undefined
302
341
  */
@@ -304,6 +343,19 @@ export function combinePlain(...args: Record<any, any>[]): any {
304
343
  return assignPlain({}, ...args);
305
344
  }
306
345
 
346
+ /**
347
+ * Get deep frozen object
348
+ */
349
+ export function deepFreeze(object: any) {
350
+ if (typeof object !== 'object') {
351
+ return object;
352
+ }
353
+ for (const [key, value] of Object.entries(object)) {
354
+ object[key] = deepFreeze(value);
355
+ }
356
+ return Object.freeze(object);
357
+ }
358
+
307
359
  /**
308
360
  * Standard error function
309
361
  */
@@ -327,7 +379,6 @@ export function filterProperties<T = Record<string, any>>(
327
379
 
328
380
  /**
329
381
  * Get plain copy of object
330
- * @param element
331
382
  */
332
383
  export function getPlain(object: any) {
333
384
  return JSON.parse(JSON.stringify(object));
@@ -512,11 +563,24 @@ export function match(expression: any, cases: Record<any, any>): any {
512
563
  /**
513
564
  * Map values into specific type
514
565
  */
515
- export function mapClass<T>(values: Partial<T>, ctor: new () => T, cloneDeep = true): T {
566
+ export function mapClass<T>(
567
+ values: Partial<T>,
568
+ ctor: new () => T,
569
+ options?: {
570
+ cloneDeep?: boolean;
571
+ circle?: boolean;
572
+ proto?: boolean;
573
+ }
574
+ ): T {
575
+ const config = {
576
+ cloneDeep: true,
577
+ circles: false,
578
+ proto: false,
579
+ };
516
580
  const instance = new ctor();
517
581
 
518
582
  return Object.keys(instance).reduce((obj, key) => {
519
- obj[key] = cloneDeep ? _.cloneDeep(values[key]) : values[key];
583
+ obj[key] = config.cloneDeep ? clone(values[key], { circles: config.circles, proto: config.proto }) : values[key];
520
584
  return obj;
521
585
  }, instance);
522
586
  }
@@ -1,6 +1,6 @@
1
1
  import { plainToInstance } from 'class-transformer';
2
- import * as _ from 'lodash';
3
2
  import { Types } from 'mongoose';
3
+ import { clone } from './input.helper';
4
4
 
5
5
  /**
6
6
  * Helper class for models
@@ -63,15 +63,19 @@ export function prepareMap<T = Record<string, any>>(
63
63
  target: T,
64
64
  options: {
65
65
  cloneDeep?: boolean;
66
+ circles?: boolean;
66
67
  funcAllowed?: boolean;
67
68
  mapId?: boolean;
69
+ proto?: boolean;
68
70
  } = {}
69
71
  ): Partial<T> | Record<string, any> {
70
72
  // Set config
71
73
  const config = {
72
74
  cloneDeep: true,
75
+ circles: true,
73
76
  funcAllowed: false,
74
77
  mapId: false,
78
+ proto: false,
75
79
  ...options,
76
80
  };
77
81
 
@@ -85,7 +89,10 @@ export function prepareMap<T = Record<string, any>>(
85
89
  source[key] !== undefined &&
86
90
  (config.funcAllowed || typeof (source[key] !== 'function'))
87
91
  ) {
88
- result[key] = source[key] !== 'function' && config.cloneDeep ? _.cloneDeep(source[key]) : source[key];
92
+ result[key] =
93
+ source[key] !== 'function' && config.cloneDeep
94
+ ? clone(source[key], { circles: config.circles, proto: config.proto })
95
+ : source[key];
89
96
  } else if (key === 'id' && !config.mapId) {
90
97
  result['id'] = source[key];
91
98
  }
@@ -102,21 +109,25 @@ export function map<T = Record<string, any>>(
102
109
  target: T,
103
110
  options: {
104
111
  cloneDeep?: boolean;
112
+ circles?: boolean;
105
113
  funcAllowed?: boolean;
106
114
  mapId?: boolean;
115
+ proto?: boolean;
107
116
  } = {}
108
117
  ): T {
109
118
  // Set config
110
119
  const config = {
111
120
  cloneDeep: true,
121
+ circles: false,
112
122
  funcAllowed: false,
113
123
  mapId: false,
124
+ proto: false,
114
125
  ...options,
115
126
  };
116
127
 
117
128
  // Check source
118
129
  if (!source || typeof source !== 'object' || Array.isArray(source)) {
119
- return config.cloneDeep ? _.cloneDeep(target) : target;
130
+ return config.cloneDeep ? clone(target, { circles: config.circles, proto: config.proto }) : target;
120
131
  }
121
132
 
122
133
  // Prepare source
@@ -9,6 +9,8 @@ import { PrepareInputOptions } from '../interfaces/prepare-input-options.interfa
9
9
  import { PrepareOutputOptions } from '../interfaces/prepare-output-options.interface';
10
10
  import { ResolveSelector } from '../interfaces/resolve-selector.interface';
11
11
  import { ServiceOptions } from '../interfaces/service-options.interface';
12
+ import { ConfigService } from '../services/config.service';
13
+ import { clone } from './input.helper';
12
14
 
13
15
  /**
14
16
  * Helper class for services
@@ -23,10 +25,13 @@ export default class ServiceHelper {
23
25
  currentUser: { [key: string]: any; id: string },
24
26
  options: {
25
27
  [key: string]: any;
28
+ checkRoles?: boolean;
26
29
  create?: boolean;
27
30
  clone?: boolean;
28
31
  getNewArray?: boolean;
29
32
  removeUndefined?: boolean;
33
+ sha256?: boolean;
34
+ targetModel?: new (...args: any[]) => T;
30
35
  } = {}
31
36
  ): Promise<T> {
32
37
  return prepareInput(input, currentUser, options);
@@ -41,6 +46,8 @@ export default class ServiceHelper {
41
46
  [key: string]: any;
42
47
  clone?: boolean;
43
48
  getNewArray?: boolean;
49
+ objectIdsToStrings?: boolean;
50
+ removeSecrets?: boolean;
44
51
  removeUndefined?: boolean;
45
52
  targetModel?: new (...args: any[]) => T;
46
53
  } = {}
@@ -58,10 +65,13 @@ export async function prepareInput<T = any>(
58
65
  options: {
59
66
  [key: string]: any;
60
67
  checkRoles?: boolean;
68
+ circles?: boolean;
61
69
  create?: boolean;
62
70
  clone?: boolean;
63
71
  getNewArray?: boolean;
72
+ proto?: boolean;
64
73
  removeUndefined?: boolean;
74
+ sha256?: boolean;
65
75
  targetModel?: new (...args: any[]) => T;
66
76
  } = {}
67
77
  ): Promise<T> {
@@ -69,9 +79,12 @@ export async function prepareInput<T = any>(
69
79
  const config = {
70
80
  checkRoles: false,
71
81
  clone: false,
82
+ circles: false,
72
83
  create: false,
73
84
  getNewArray: false,
85
+ proto: false,
74
86
  removeUndefined: true,
87
+ sha256: ConfigService.configFastButReadOnly.sha256,
75
88
  ...options,
76
89
  };
77
90
 
@@ -97,7 +110,7 @@ export async function prepareInput<T = any>(
97
110
  if ((input as Record<string, any>).mapDeep && typeof (input as any).mapDeep === 'function') {
98
111
  input = await Object.getPrototypeOf(input).mapDeep(input);
99
112
  } else {
100
- input = _.cloneDeep(input);
113
+ input = clone(input, { circles: config.circles, proto: config.proto });
101
114
  }
102
115
  }
103
116
 
@@ -133,9 +146,9 @@ export async function prepareInput<T = any>(
133
146
  if ((input as any).password) {
134
147
  // Check if the password was transmitted encrypted
135
148
  // If not, the password is encrypted to enable future encrypted and unencrypted transmissions
136
- (input as any).password = /^[a-f0-9]{64}$/i.test((input as any).password)
137
- ? (input as any).password
138
- : sha256((input as any).password);
149
+ if (config.sha256 && !/^[a-f0-9]{64}$/i.test((input as any).password)) {
150
+ (input as any).password = sha256((input as any).password);
151
+ }
139
152
 
140
153
  // Hash password
141
154
  (input as any).password = await bcrypt.hash((input as any).password, 10);
@@ -163,8 +176,10 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
163
176
  options: {
164
177
  [key: string]: any;
165
178
  clone?: boolean;
179
+ circles?: boolean;
166
180
  getNewArray?: boolean;
167
181
  objectIdsToStrings?: boolean;
182
+ proto?: boolean;
168
183
  removeSecrets?: boolean;
169
184
  removeUndefined?: boolean;
170
185
  targetModel?: new (...args: any[]) => T;
@@ -173,8 +188,10 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
173
188
  // Configuration
174
189
  const config = {
175
190
  clone: false,
191
+ circles: false,
176
192
  getNewArray: false,
177
193
  objectIdsToStrings: true,
194
+ proto: false,
178
195
  removeSecrets: true,
179
196
  removeUndefined: false,
180
197
  targetModel: undefined,
@@ -203,7 +220,7 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
203
220
  if (output.mapDeep && typeof output.mapDeep === 'function') {
204
221
  output = await Object.getPrototypeOf(output).mapDeep(output);
205
222
  } else {
206
- output = _.cloneDeep(output);
223
+ output = clone(output, { circles: config.circles, proto: config.proto });
207
224
  }
208
225
  }
209
226
 
@@ -258,9 +275,11 @@ export function prepareServiceOptions(
258
275
  serviceOptions: ServiceOptions,
259
276
  options?: {
260
277
  clone?: boolean;
278
+ circles?: boolean;
261
279
  inputType?: any;
262
280
  outputType?: any;
263
281
  subFieldSelection?: string;
282
+ proto?: boolean;
264
283
  prepareInput?: PrepareInputOptions;
265
284
  prepareOutput?: PrepareOutputOptions;
266
285
  }
@@ -268,12 +287,14 @@ export function prepareServiceOptions(
268
287
  // Set default values
269
288
  const config = {
270
289
  clone: true,
290
+ circles: true,
291
+ proto: false,
271
292
  ...options,
272
293
  };
273
294
 
274
295
  // Clone
275
296
  if (serviceOptions && config.clone) {
276
- serviceOptions = _.cloneDeep(serviceOptions);
297
+ serviceOptions = clone(serviceOptions, { circles: config.circles, proto: config.proto });
277
298
  }
278
299
 
279
300
  // Init if not exists
@@ -156,6 +156,12 @@ export interface IServerOptions {
156
156
  path?: string;
157
157
  };
158
158
 
159
+ /**
160
+ * Whether to enable verification and automatic encryption for received passwords that are not in sha256 format
161
+ * default = false, sha256 format check: /^[a-f0-9]{64}$/i
162
+ */
163
+ sha256?: boolean;
164
+
159
165
  /**
160
166
  * Templates
161
167
  */