@lenne.tech/nest-server 9.0.31 → 9.2.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.
Files changed (135) hide show
  1. package/dist/config.env.js +41 -2
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/filters/http-exception-log.filter.d.ts +4 -0
  4. package/dist/core/common/filters/http-exception-log.filter.js +30 -0
  5. package/dist/core/common/filters/http-exception-log.filter.js.map +1 -0
  6. package/dist/core/common/helpers/db.helper.d.ts +6 -2
  7. package/dist/core/common/helpers/db.helper.js +9 -4
  8. package/dist/core/common/helpers/db.helper.js.map +1 -1
  9. package/dist/core/common/helpers/input.helper.d.ts +3 -0
  10. package/dist/core/common/helpers/input.helper.js +30 -1
  11. package/dist/core/common/helpers/input.helper.js.map +1 -1
  12. package/dist/core/common/interceptors/check-security.interceptor.d.ts +5 -0
  13. package/dist/core/common/interceptors/check-security.interceptor.js +47 -0
  14. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -0
  15. package/dist/core/common/interfaces/server-options.interface.d.ts +17 -6
  16. package/dist/core/common/interfaces/service-options.interface.d.ts +1 -0
  17. package/dist/core/common/models/core-model.model.d.ts +1 -0
  18. package/dist/core/common/models/core-model.model.js +3 -0
  19. package/dist/core/common/models/core-model.model.js.map +1 -1
  20. package/dist/core/common/plugins/complexity.plugin.d.ts +9 -0
  21. package/dist/core/common/plugins/complexity.plugin.js +47 -0
  22. package/dist/core/common/plugins/complexity.plugin.js.map +1 -0
  23. package/dist/core/common/plugins/mongoose-id.plugin.d.ts +1 -2
  24. package/dist/core/common/plugins/mongoose-id.plugin.js +7 -2
  25. package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
  26. package/dist/core/common/services/config.service.d.ts +4 -4
  27. package/dist/core/common/services/config.service.js.map +1 -1
  28. package/dist/core/common/services/module.service.d.ts +5 -1
  29. package/dist/core/common/services/module.service.js +15 -3
  30. package/dist/core/common/services/module.service.js.map +1 -1
  31. package/dist/core/modules/auth/core-auth.model.d.ts +4 -1
  32. package/dist/core/modules/auth/core-auth.model.js +12 -1
  33. package/dist/core/modules/auth/core-auth.model.js.map +1 -1
  34. package/dist/core/modules/auth/core-auth.module.d.ts +3 -1
  35. package/dist/core/modules/auth/core-auth.module.js +7 -2
  36. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  37. package/dist/core/modules/auth/core-auth.resolver.d.ts +22 -2
  38. package/dist/core/modules/auth/core-auth.resolver.js +77 -9
  39. package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
  40. package/dist/core/modules/auth/guards/auth.guard.d.ts +1 -1
  41. package/dist/core/modules/auth/guards/auth.guard.js +9 -4
  42. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  43. package/dist/core/modules/auth/guards/refresh-token.guard.d.ts +4 -0
  44. package/dist/core/modules/auth/guards/refresh-token.guard.js +18 -0
  45. package/dist/core/modules/auth/guards/refresh-token.guard.js.map +1 -0
  46. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  47. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +1 -0
  48. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +5 -0
  49. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -1
  50. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +1 -0
  51. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +5 -0
  52. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -1
  53. package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +3 -0
  54. package/dist/core/modules/auth/interfaces/jwt-payload.interface.d.ts +1 -1
  55. package/dist/core/modules/auth/services/core-auth-user.service.d.ts +3 -0
  56. package/dist/core/modules/auth/services/core-auth-user.service.js.map +1 -1
  57. package/dist/core/modules/auth/services/core-auth.service.d.ts +23 -5
  58. package/dist/core/modules/auth/services/core-auth.service.js +121 -13
  59. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  60. package/dist/core/modules/auth/strategies/jwt-refresh.strategy.d.ts +12 -0
  61. package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js +61 -0
  62. package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js.map +1 -0
  63. package/dist/core/modules/auth/{jwt.strategy.d.ts → strategies/jwt.strategy.d.ts} +4 -3
  64. package/dist/core/modules/auth/{jwt.strategy.js → strategies/jwt.strategy.js} +12 -5
  65. package/dist/core/modules/auth/strategies/jwt.strategy.js.map +1 -0
  66. package/dist/core/modules/user/core-user.model.d.ts +2 -0
  67. package/dist/core/modules/user/core-user.model.js +12 -0
  68. package/dist/core/modules/user/core-user.model.js.map +1 -1
  69. package/dist/core/modules/user/core-user.service.d.ts +2 -2
  70. package/dist/core/modules/user/core-user.service.js +2 -2
  71. package/dist/core/modules/user/core-user.service.js.map +1 -1
  72. package/dist/core.module.js +12 -2
  73. package/dist/core.module.js.map +1 -1
  74. package/dist/index.d.ts +6 -1
  75. package/dist/index.js +6 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/main.js +23 -0
  78. package/dist/main.js.map +1 -1
  79. package/dist/server/modules/auth/auth.model.js.map +1 -1
  80. package/dist/server/modules/auth/auth.resolver.d.ts +13 -5
  81. package/dist/server/modules/auth/auth.resolver.js +21 -12
  82. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  83. package/dist/server/modules/auth/auth.service.d.ts +2 -1
  84. package/dist/server/modules/auth/auth.service.js +7 -48
  85. package/dist/server/modules/auth/auth.service.js.map +1 -1
  86. package/dist/server/modules/file/file.module.js +3 -3
  87. package/dist/server/modules/file/file.module.js.map +1 -1
  88. package/dist/server/modules/user/user.model.d.ts +1 -0
  89. package/dist/server/modules/user/user.model.js +19 -0
  90. package/dist/server/modules/user/user.model.js.map +1 -1
  91. package/dist/server/modules/user/user.service.js +1 -1
  92. package/dist/server/modules/user/user.service.js.map +1 -1
  93. package/dist/server/server.module.js +12 -1
  94. package/dist/server/server.module.js.map +1 -1
  95. package/dist/tsconfig.build.tsbuildinfo +1 -1
  96. package/package.json +32 -27
  97. package/src/config.env.ts +41 -2
  98. package/src/core/common/filters/http-exception-log.filter.ts +27 -0
  99. package/src/core/common/helpers/db.helper.ts +15 -5
  100. package/src/core/common/helpers/input.helper.ts +49 -0
  101. package/src/core/common/interceptors/check-security.interceptor.ts +51 -0
  102. package/src/core/common/interfaces/server-options.interface.ts +75 -30
  103. package/src/core/common/interfaces/service-options.interface.ts +4 -0
  104. package/src/core/common/models/core-model.model.ts +7 -0
  105. package/src/core/common/plugins/complexity.plugin.ts +31 -0
  106. package/src/core/common/plugins/mongoose-id.plugin.js +4 -2
  107. package/src/core/common/services/config.service.ts +4 -4
  108. package/src/core/common/services/module.service.ts +26 -4
  109. package/src/core/modules/auth/core-auth.model.ts +15 -2
  110. package/src/core/modules/auth/core-auth.module.ts +8 -2
  111. package/src/core/modules/auth/core-auth.resolver.ts +93 -10
  112. package/src/core/modules/auth/guards/auth.guard.ts +12 -5
  113. package/src/core/modules/auth/guards/refresh-token.guard.ts +5 -0
  114. package/src/core/modules/auth/guards/roles.guard.ts +1 -1
  115. package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +3 -0
  116. package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +3 -0
  117. package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +15 -0
  118. package/src/core/modules/auth/interfaces/jwt-payload.interface.ts +1 -1
  119. package/src/core/modules/auth/services/core-auth-user.service.ts +15 -0
  120. package/src/core/modules/auth/services/core-auth.service.ts +216 -18
  121. package/src/core/modules/auth/strategies/jwt-refresh.strategy.ts +56 -0
  122. package/src/core/modules/auth/{jwt.strategy.ts → strategies/jwt.strategy.ts} +16 -5
  123. package/src/core/modules/user/core-user.model.ts +17 -1
  124. package/src/core/modules/user/core-user.service.ts +2 -2
  125. package/src/core.module.ts +14 -2
  126. package/src/index.ts +6 -1
  127. package/src/main.ts +29 -0
  128. package/src/server/modules/auth/auth.model.ts +1 -1
  129. package/src/server/modules/auth/auth.resolver.ts +26 -8
  130. package/src/server/modules/auth/auth.service.ts +20 -61
  131. package/src/server/modules/file/file.module.ts +3 -3
  132. package/src/server/modules/user/user.model.ts +29 -0
  133. package/src/server/modules/user/user.service.ts +1 -1
  134. package/src/server/server.module.ts +12 -1
  135. package/dist/core/modules/auth/jwt.strategy.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "9.0.31",
3
+ "version": "9.2.0",
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",
@@ -60,78 +60,83 @@
60
60
  "node": ">= 16.13.0"
61
61
  },
62
62
  "dependencies": {
63
- "@apollo/gateway": "2.2.3",
64
- "@nestjs/apollo": "10.1.7",
65
- "@nestjs/common": "9.2.1",
66
- "@nestjs/core": "9.2.1",
67
- "@nestjs/graphql": "10.1.7",
68
- "@nestjs/jwt": "10.0.1",
63
+ "@apollo/gateway": "2.3.2",
64
+ "@nestjs/apollo": "10.2.0",
65
+ "@nestjs/common": "9.3.9",
66
+ "@nestjs/core": "9.3.9",
67
+ "@nestjs/graphql": "10.2.0",
68
+ "@nestjs/jwt": "10.0.2",
69
69
  "@nestjs/mongoose": "9.2.1",
70
- "@nestjs/passport": "9.0.0",
71
- "@nestjs/platform-express": "9.2.1",
72
- "@nestjs/schedule": "2.1.0",
70
+ "@nestjs/passport": "9.0.3",
71
+ "@nestjs/platform-express": "9.3.9",
72
+ "@nestjs/schedule": "2.2.0",
73
73
  "apollo-server-core": "3.11.1",
74
74
  "apollo-server-express": "3.11.1",
75
75
  "bcrypt": "5.1.0",
76
76
  "class-transformer": "0.5.1",
77
77
  "class-validator": "0.14.0",
78
+ "compression": "1.7.4",
79
+ "cookie-parser": "1.4.6",
78
80
  "ejs": "3.1.8",
79
81
  "graphql": "16.6.0",
82
+ "graphql-query-complexity": "0.12.0",
80
83
  "graphql-subscriptions": "2.0.0",
81
84
  "graphql-upload": "15.0.2",
82
85
  "js-sha256": "0.9.0",
83
- "json-to-graphql-query": "2.2.4",
86
+ "json-to-graphql-query": "2.2.5",
84
87
  "light-my-request": "5.8.0",
85
88
  "lodash": "4.17.21",
86
- "mongodb": "4.13.0",
87
- "mongoose": "6.8.4",
89
+ "mongodb": "4.14.0",
90
+ "mongoose": "6.9.1",
88
91
  "mongoose-gridfs": "1.3.0",
89
- "multer-gridfs-storage": "5.0.2",
90
92
  "multer": "1.4.5-lts.1",
91
- "node-mailjet": "6.0.1",
92
- "nodemailer": "6.9.0",
93
+ "multer-gridfs-storage": "5.0.2",
94
+ "node-mailjet": "6.0.2",
95
+ "nodemailer": "6.9.1",
93
96
  "nodemon": "2.0.20",
94
97
  "passport": "0.6.0",
95
98
  "passport-jwt": "4.0.1",
96
99
  "reflect-metadata": "0.1.13",
97
100
  "rfdc": "1.3.0",
98
- "rimraf": "4.1.1",
101
+ "rimraf": "4.1.2",
99
102
  "rxjs": "7.8.0"
100
103
  },
101
104
  "devDependencies": {
102
- "@nestjs/testing": "9.2.1",
105
+ "@nestjs/testing": "9.3.9",
106
+ "@types/compression": "1.7.2",
107
+ "@types/cookie-parser": "1.4.3",
103
108
  "@types/cron": "2.0.0",
104
109
  "@types/ejs": "3.1.1",
105
- "@types/jest": "29.2.6",
110
+ "@types/jest": "29.4.0",
106
111
  "@types/lodash": "4.14.191",
107
112
  "@types/multer": "1.4.7",
108
- "@types/node": "18.11.18",
113
+ "@types/node": "18.13.0",
109
114
  "@types/nodemailer": "6.4.7",
110
115
  "@types/passport": "1.0.11",
111
116
  "@types/supertest": "2.0.12",
112
- "@typescript-eslint/eslint-plugin": "5.48.2",
113
- "@typescript-eslint/parser": "5.48.2",
117
+ "@typescript-eslint/eslint-plugin": "5.52.0",
118
+ "@typescript-eslint/parser": "5.52.0",
114
119
  "coffeescript": "2.7.0",
115
- "eslint": "8.32.0",
120
+ "eslint": "8.34.0",
116
121
  "eslint-config-prettier": "8.6.0",
117
122
  "find-file-up": "2.0.1",
118
- "grunt": "1.5.3",
123
+ "grunt": "1.6.1",
119
124
  "grunt-bg-shell": "2.3.3",
120
125
  "grunt-contrib-clean": "2.0.1",
121
126
  "grunt-contrib-watch": "1.1.0",
122
127
  "grunt-sync": "0.8.2",
123
128
  "husky": "8.0.3",
124
- "jest": "29.3.1",
129
+ "jest": "29.4.2",
125
130
  "npm-watch": "0.11.0",
126
131
  "pm2": "5.2.2",
127
- "prettier": "2.8.3",
132
+ "prettier": "2.8.4",
128
133
  "pretty-quick": "3.1.3",
129
134
  "supertest": "6.3.3",
130
135
  "ts-jest": "29.0.5",
131
136
  "ts-morph": "17.0.1",
132
137
  "ts-node": "10.9.1",
133
138
  "tsconfig-paths": "4.1.2",
134
- "typescript": "4.9.4",
139
+ "typescript": "4.9.5",
135
140
  "yalc": "1.0.0-pre.53"
136
141
  },
137
142
  "overrides": {
package/src/config.env.ts CHANGED
@@ -12,6 +12,8 @@ const config: { [env: string]: IServerOptions } = {
12
12
  // ===========================================================================
13
13
  local: {
14
14
  automaticObjectIdFiltering: true,
15
+ compression: true,
16
+ cookies: false,
15
17
  cronJobs: {
16
18
  sayHello: {
17
19
  cronTime: CronExpression.EVERY_10_SECONDS,
@@ -52,16 +54,27 @@ const config: { [env: string]: IServerOptions } = {
52
54
  debug: true,
53
55
  introspection: true,
54
56
  },
57
+ maxComplexity: 20,
55
58
  },
56
59
  jwt: {
57
- secret: 'SECRET_OR_PRIVATE_KEY_DEV',
60
+ secret: 'SECRET_OR_PRIVATE_KEY_LOCAL',
61
+ signInOptions: {
62
+ expiresIn: '15m',
63
+ },
64
+ refresh: {
65
+ secret: 'SECRET_OR_PRIVATE_KEY_LOCAL_REFRESH',
66
+ signInOptions: {
67
+ expiresIn: '7d',
68
+ },
69
+ },
58
70
  },
59
71
  loadLocalConfig: false,
72
+ logExceptions: true,
60
73
  mongoose: {
61
74
  collation: {
62
75
  locale: 'de',
63
76
  },
64
- uri: 'mongodb://127.0.0.1/nest-server-dev',
77
+ uri: 'mongodb://127.0.0.1/nest-server-local',
65
78
  },
66
79
  port: 3000,
67
80
  sha256: true,
@@ -80,6 +93,8 @@ const config: { [env: string]: IServerOptions } = {
80
93
  // ===========================================================================
81
94
  development: {
82
95
  automaticObjectIdFiltering: true,
96
+ compression: true,
97
+ cookies: false,
83
98
  email: {
84
99
  smtp: {
85
100
  auth: {
@@ -111,11 +126,22 @@ const config: { [env: string]: IServerOptions } = {
111
126
  debug: true,
112
127
  introspection: true,
113
128
  },
129
+ maxComplexity: 20,
114
130
  },
115
131
  jwt: {
116
132
  secret: 'SECRET_OR_PRIVATE_KEY_DEV',
133
+ signInOptions: {
134
+ expiresIn: '15m',
135
+ },
136
+ refresh: {
137
+ secret: 'SECRET_OR_PRIVATE_KEY_DEV_REFRESH',
138
+ signInOptions: {
139
+ expiresIn: '7d',
140
+ },
141
+ },
117
142
  },
118
143
  loadLocalConfig: false,
144
+ logExceptions: true,
119
145
  mongoose: {
120
146
  collation: {
121
147
  locale: 'de',
@@ -139,6 +165,8 @@ const config: { [env: string]: IServerOptions } = {
139
165
  // ===========================================================================
140
166
  production: {
141
167
  automaticObjectIdFiltering: true,
168
+ compression: true,
169
+ cookies: false,
142
170
  email: {
143
171
  smtp: {
144
172
  auth: {
@@ -170,11 +198,22 @@ const config: { [env: string]: IServerOptions } = {
170
198
  debug: false,
171
199
  introspection: true,
172
200
  },
201
+ maxComplexity: 20,
173
202
  },
174
203
  jwt: {
175
204
  secret: 'SECRET_OR_PRIVATE_KEY_PROD',
205
+ signInOptions: {
206
+ expiresIn: '15m',
207
+ },
208
+ refresh: {
209
+ secret: 'SECRET_OR_PRIVATE_KEY_PROD_REFRESH',
210
+ signInOptions: {
211
+ expiresIn: '7d',
212
+ },
213
+ },
176
214
  },
177
215
  loadLocalConfig: false,
216
+ logExceptions: true,
178
217
  mongoose: {
179
218
  collation: {
180
219
  locale: 'de',
@@ -0,0 +1,27 @@
1
+ import { ArgumentsHost, Catch, ContextType, ExceptionFilter, HttpException } from '@nestjs/common';
2
+ import { Response } from 'express';
3
+
4
+ @Catch(HttpException)
5
+ export class HttpExceptionLogFilter implements ExceptionFilter {
6
+ /**
7
+ * Log exception
8
+ */
9
+ catch(exception: HttpException, host: ArgumentsHost) {
10
+ // Log
11
+ console.debug(exception.stack);
12
+
13
+ // GraphQL
14
+ const type = host.getType() as ContextType | 'graphql';
15
+ if (type === 'graphql') {
16
+ return exception;
17
+ }
18
+
19
+ // REST
20
+ const ctx = host.switchToHttp();
21
+ const res = ctx.getResponse<Response>();
22
+ const status = exception.getStatus();
23
+ res.status(status).json({
24
+ ...exception,
25
+ });
26
+ }
27
+ }
@@ -6,6 +6,7 @@ import { CoreModel } from '../models/core-model.model';
6
6
  import { FieldSelection } from '../types/field-selection.type';
7
7
  import { IdsType } from '../types/ids.type';
8
8
  import { StringOrObjectId } from '../types/string-or-object-id.type';
9
+ import { removePropertiesDeep } from './input.helper';
9
10
 
10
11
  // =====================================================================================================================
11
12
  // Export functions
@@ -350,7 +351,7 @@ export function getPopulatOptionsFromSelections(selectionNodes: readonly Selecti
350
351
  // Check for subfields
351
352
  if (node.selectionSet?.selections?.length) {
352
353
  for (const innerNode of node.selectionSet.selections as FieldNode[]) {
353
- // Subfiled is a primitive
354
+ // Subfield is a primitive
354
355
  if (!innerNode.selectionSet?.selections?.length) {
355
356
  option.select ? option.select.push(innerNode.name.value) : (option.select = [innerNode.name.value]);
356
357
  }
@@ -477,10 +478,14 @@ export async function popAndMap<T extends CoreModel>(
477
478
  queryOrDocument: Query<any, any> | Document | Document[],
478
479
  populate: FieldSelection,
479
480
  modelClass: new (...args: any[]) => T,
480
- mongooseModel?: Model<any>
481
+ mongooseModel?: Model<any>,
482
+ options?: {
483
+ ignoreSelections?: boolean;
484
+ }
481
485
  ): Promise<T | T[]> {
482
486
  let result;
483
487
  let populateOptions: PopulateOptions[] = [];
488
+ const ignoreSelections = options?.ignoreSelections;
484
489
  if (populate) {
485
490
  if (
486
491
  Array.isArray(populate) &&
@@ -511,13 +516,13 @@ export async function popAndMap<T extends CoreModel>(
511
516
  } else {
512
517
  // Process documents
513
518
  if (Array.isArray(queryOrDocument)) {
514
- await setPopulates(queryOrDocument, populateOptions, mongooseModel);
519
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
515
520
  result = queryOrDocument.map((item) => (modelClass as any).map(item));
516
521
  }
517
522
 
518
523
  // Process document
519
524
  else {
520
- await setPopulates(queryOrDocument, populateOptions, mongooseModel);
525
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
521
526
  result = (modelClass as any).map(queryOrDocument);
522
527
  }
523
528
  }
@@ -562,7 +567,8 @@ export function removeIds(source: any[], ids: StringOrObjectId | StringOrObjectI
562
567
  export async function setPopulates<T = Query<any, any> | Document>(
563
568
  queryOrDocument: T,
564
569
  populateOptions: string[] | PopulateOptions[] | (string | PopulateOptions)[],
565
- mongooseModel: Model<any>
570
+ mongooseModel: Model<any>,
571
+ options?: { ignoreSelections: boolean }
566
572
  ): Promise<T> {
567
573
  // Check parameters
568
574
  if (!populateOptions?.length || !queryOrDocument) {
@@ -580,6 +586,10 @@ export async function setPopulates<T = Query<any, any> | Document>(
580
586
  });
581
587
  }
582
588
 
589
+ if (options?.ignoreSelections) {
590
+ removePropertiesDeep(populateOptions, ['select']);
591
+ }
592
+
583
593
  // Query => Chaining
584
594
  if (queryOrDocument instanceof Query) {
585
595
  for (const options of populateOptions) {
@@ -718,3 +718,52 @@ export function prepareServiceOptionsForCreate(serviceOptions: any) {
718
718
  }
719
719
  return serviceOptions;
720
720
  }
721
+
722
+ /**
723
+ * Remove properties deep
724
+ */
725
+ export function removePropertiesDeep(
726
+ data: any,
727
+ properties: string[],
728
+ options?: {
729
+ processedObjects?: WeakMap<new () => any, boolean>;
730
+ }
731
+ ): any {
732
+ // Set options
733
+ const { processedObjects } = {
734
+ processedObjects: new WeakMap(),
735
+ ...options,
736
+ };
737
+
738
+ // Check for falsifiable values
739
+ if (!data) {
740
+ return data;
741
+ }
742
+
743
+ // Prevent circular processing
744
+ else if (typeof data === 'object') {
745
+ if (processedObjects.get(data)) {
746
+ return data;
747
+ }
748
+ processedObjects.set(data, true);
749
+ }
750
+
751
+ // Process array
752
+ if (Array.isArray(data)) {
753
+ return data.map((item) => removePropertiesDeep(item, properties, { processedObjects }));
754
+ }
755
+
756
+ // Process object
757
+ if (typeof data === 'object') {
758
+ for (const prop of properties) {
759
+ delete data[prop];
760
+ }
761
+ for (const [key, value] of Object.entries(data)) {
762
+ data[key] = removePropertiesDeep(value, properties, { processedObjects });
763
+ }
764
+ return data;
765
+ }
766
+
767
+ // Process others
768
+ return data;
769
+ }
@@ -0,0 +1,51 @@
1
+ import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import { Observable } from 'rxjs';
3
+ import { map } from 'rxjs/operators';
4
+ import { AuthResolver } from '../../../server/modules/auth/auth.resolver';
5
+ import { getContextData } from '../helpers/context.helper';
6
+ import { processDeep } from '../helpers/input.helper';
7
+
8
+ /**
9
+ * Verification of all outgoing data via securityCheck
10
+ */
11
+ @Injectable()
12
+ export class CheckSecurityInterceptor implements NestInterceptor {
13
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
14
+ // Get current user
15
+ const user = getContextData(context)?.currentUser;
16
+
17
+ // Set force mode for sign in and sign up
18
+ let force = false;
19
+ if (!user) {
20
+ if (context.getClass() === AuthResolver) {
21
+ force = true;
22
+ }
23
+ }
24
+
25
+ // Check response
26
+ return next.handle().pipe(
27
+ map((data) => {
28
+ // Check data
29
+ if (data && typeof data === 'object' && typeof data.securityCheck === 'function') {
30
+ return data.securityCheck(user, force);
31
+ }
32
+
33
+ // Check if data is writeable (e.g. objects from direct access to json files via http are not writable)
34
+ if (data && typeof data === 'object') {
35
+ const writeable = !Object.keys(data).find((key) => !Object.getOwnPropertyDescriptor(data, key).writable);
36
+ if (!writeable) {
37
+ return data;
38
+ }
39
+ }
40
+
41
+ // Check deep
42
+ return processDeep(data, (item) => {
43
+ if (!item || typeof item !== 'object' || typeof item.securityCheck !== 'function') {
44
+ return item;
45
+ }
46
+ return item.securityCheck(user, force);
47
+ });
48
+ })
49
+ );
50
+ }
51
+ }
@@ -1,15 +1,57 @@
1
1
  import { ApolloDriverConfig } from '@nestjs/apollo';
2
2
  import { GqlModuleAsyncOptions } from '@nestjs/graphql';
3
3
  import { JwtModuleOptions } from '@nestjs/jwt';
4
+ import { JwtSignOptions } from '@nestjs/jwt/dist/interfaces';
4
5
  import { MongooseModuleOptions } from '@nestjs/mongoose';
5
6
  import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
6
7
  import { CronExpression } from '@nestjs/schedule';
8
+ import compression from 'compression';
7
9
  import { CollationOptions } from 'mongodb';
8
10
  import * as SMTPTransport from 'nodemailer/lib/smtp-transport';
9
11
  import { Falsy } from '../types/falsy.type';
10
12
  import { CronJobConfig } from './cron-job-config.interface';
11
13
  import { MailjetOptions } from './mailjet-options.interface';
12
14
 
15
+ /**
16
+ * Interface for JWT configuration (main and refresh)
17
+ */
18
+ export interface IJwt {
19
+ /**
20
+ * Private key
21
+ */
22
+ privateKey?: string;
23
+
24
+ /**
25
+ * Public key
26
+ */
27
+ publicKey?: string;
28
+
29
+ /**
30
+ * Secret to encrypt the JWT
31
+ */
32
+ secret?: string;
33
+
34
+ /**
35
+ * JWT Provider
36
+ * See https://github.com/mikenicholson/passport-jwt/blob/master/README.md#configure-strategy
37
+ */
38
+ secretOrKeyProvider?: (
39
+ request: Record<string, any>,
40
+ rawJwtToken: string,
41
+ done: (err: any, secret: string) => any
42
+ ) => any;
43
+
44
+ /**
45
+ * Alias of secret (for backwards compatibility)
46
+ */
47
+ secretOrPrivateKey?: string;
48
+
49
+ /**
50
+ * SignIn Options like expiresIn
51
+ */
52
+ signInOptions?: JwtSignOptions;
53
+ }
54
+
13
55
  /**
14
56
  * Options for the server
15
57
  */
@@ -21,6 +63,18 @@ export interface IServerOptions {
21
63
  */
22
64
  automaticObjectIdFiltering?: boolean;
23
65
 
66
+ /**
67
+ * Whether to use the compression middleware package to enable gzip compression.
68
+ * See: https://docs.nestjs.com/techniques/compression
69
+ */
70
+ compression?: boolean | compression.CompressionOptions;
71
+
72
+ /**
73
+ * Whether to use cookies for authentication handling
74
+ * See: https://docs.nestjs.com/techniques/cookies
75
+ */
76
+ cookies?: boolean;
77
+
24
78
  /**
25
79
  * Cron jobs configuration object with the name of the cron job function as key
26
80
  * and the cron expression or config as value
@@ -105,46 +159,32 @@ export interface IServerOptions {
105
159
  */
106
160
  enableSubscriptionAuth?: boolean;
107
161
 
162
+ /**
163
+ * Maximum complexity of GraphQL requests
164
+ */
165
+ maxComplexity?: number;
166
+
108
167
  /**
109
168
  * Module options (forRootAsync)
110
169
  */
111
170
  options?: GqlModuleAsyncOptions;
112
171
  };
113
172
 
173
+ /**
174
+ * Ignore selections in fieldSelection
175
+ * [ConfigService must be integrated in ModuleService]
176
+ * If falsy (default): select and populate information in fieldSelection will be respected
177
+ * If truly: select fields will be ignored and only populate fields in fieldSelection will be respected
178
+ */
179
+ ignoreSelectionsForPopulate?: boolean;
180
+
114
181
  /**
115
182
  * Configuration of JavaScript Web Token (JWT) module
116
183
  */
117
184
  jwt?: {
118
- /**
119
- * Private key
120
- */
121
- privateKey?: string;
122
-
123
- /**
124
- * Public key
125
- */
126
- publicKey?: string;
127
-
128
- /**
129
- * Secret to encrypt the JWT
130
- */
131
- secret?: string;
132
-
133
- /**
134
- * JWT Provider
135
- * See https://github.com/mikenicholson/passport-jwt/blob/master/README.md#configure-strategy
136
- */
137
- secretOrKeyProvider?: (
138
- request: Record<string, any>,
139
- rawJwtToken: string,
140
- done: (err: any, secret: string) => any
141
- ) => any;
142
-
143
- /**
144
- * Alias of secret (for backwards compatibility)
145
- */
146
- secretOrPrivateKey?: string;
147
- } & JwtModuleOptions;
185
+ refresh?: IJwt;
186
+ } & IJwt &
187
+ JwtModuleOptions;
148
188
 
149
189
  /**
150
190
  * Load local configuration
@@ -154,6 +194,11 @@ export interface IServerOptions {
154
194
  */
155
195
  loadLocalConfig?: boolean | string;
156
196
 
197
+ /**
198
+ * Log exceptions (for better debugging)
199
+ */
200
+ logExceptions?: boolean;
201
+
157
202
  /**
158
203
  * Configuration for Mongoose
159
204
  */
@@ -53,6 +53,10 @@ export interface ServiceOptions {
53
53
  processFieldSelection?: {
54
54
  model?: new (...args: any[]) => any;
55
55
  dbModel?: Model<any>;
56
+ // Ignore selections in fieldSelection and populate
57
+ // If falsy (default, if not set in env.config): select and populate information in fieldSelection and populate will be respected
58
+ // If truly: select fields will be ignored and only populate fields in fieldSelection and populate will be respected
59
+ ignoreSelections?: boolean;
56
60
  };
57
61
 
58
62
  // Prepare input configuration:
@@ -125,4 +125,11 @@ export abstract class CoreModel {
125
125
  };
126
126
  return this.map(data, config);
127
127
  }
128
+
129
+ /**
130
+ * Verification of the user's rights to access the properties of this object
131
+ */
132
+ securityCheck(user: any, force?: boolean): this {
133
+ return this;
134
+ }
128
135
  }
@@ -0,0 +1,31 @@
1
+ import { GraphQLSchemaHost } from '@nestjs/graphql';
2
+ import { Plugin } from '@nestjs/apollo';
3
+ import { ApolloServerPlugin, GraphQLRequestListener } from 'apollo-server-plugin-base';
4
+ import { GraphQLError } from 'graphql';
5
+ import { fieldExtensionsEstimator, getComplexity, simpleEstimator } from 'graphql-query-complexity';
6
+ import { ConfigService } from '../services/config.service';
7
+
8
+ @Plugin()
9
+ export class ComplexityPlugin implements ApolloServerPlugin {
10
+ constructor(private gqlSchemaHost: GraphQLSchemaHost, private configService: ConfigService) {}
11
+
12
+ async requestDidStart(): Promise<GraphQLRequestListener> {
13
+ const maxComplexity: number = this.configService.getFastButReadOnly('graphQl.maxComplexity');
14
+ const { schema } = this.gqlSchemaHost;
15
+
16
+ return {
17
+ async didResolveOperation({ request, document }) {
18
+ const complexity = getComplexity({
19
+ schema,
20
+ operationName: request.operationName,
21
+ query: document,
22
+ variables: request.variables,
23
+ estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
24
+ });
25
+ if (maxComplexity !== undefined && complexity > maxComplexity) {
26
+ throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: ${maxComplexity}`);
27
+ }
28
+ },
29
+ };
30
+ }
31
+ }
@@ -1,4 +1,4 @@
1
- module.exports = function mongooseIdPlugin(schema, options) {
1
+ export function mongooseIdPlugin(schema, options) {
2
2
  schema.post(['find', 'findOne', 'save', 'deleteOne'], function (docs) {
3
3
  if (!Array.isArray(docs)) {
4
4
  docs = [docs];
@@ -10,4 +10,6 @@ module.exports = function mongooseIdPlugin(schema, options) {
10
10
  }
11
11
  }
12
12
  });
13
- };
13
+ }
14
+
15
+ module.exports = mongooseIdPlugin;
@@ -129,28 +129,28 @@ export class ConfigService {
129
129
  /**
130
130
  * Get readable and writable deep-cloned property from configuration
131
131
  */
132
- get(key: string, defaultValue: any = undefined) {
132
+ get<T = any>(key: string, defaultValue: any = undefined): T {
133
133
  return ConfigService.get(key, defaultValue);
134
134
  }
135
135
 
136
136
  /**
137
137
  * Get readable and writable deep-cloned property from configuration
138
138
  */
139
- static get(key: string, defaultValue: any = undefined) {
139
+ static get<T = any>(key: string, defaultValue: any = undefined): T {
140
140
  return clone(_.get(ConfigService._configSubject$.getValue(), key, defaultValue), { circles: false });
141
141
  }
142
142
 
143
143
  /**
144
144
  * Get faster but read-ony deep-frozen property from configuration
145
145
  */
146
- getFastButReadOnly(key: string, defaultValue: any = undefined) {
146
+ getFastButReadOnly<T = any>(key: string, defaultValue: any = undefined): T {
147
147
  return ConfigService.getFastButReadOnly(key, defaultValue);
148
148
  }
149
149
 
150
150
  /**
151
151
  * Get faster but read-ony deep-frozen property from configuration
152
152
  */
153
- static getFastButReadOnly(key: string, defaultValue: any = undefined) {
153
+ static getFastButReadOnly<T = any>(key: string, defaultValue: any = undefined): T {
154
154
  return _.get(ConfigService._frozenConfigSubject$.getValue(), key, defaultValue);
155
155
  }
156
156