@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.
- package/dist/config.env.js +41 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/filters/http-exception-log.filter.d.ts +4 -0
- package/dist/core/common/filters/http-exception-log.filter.js +30 -0
- package/dist/core/common/filters/http-exception-log.filter.js.map +1 -0
- package/dist/core/common/helpers/db.helper.d.ts +6 -2
- package/dist/core/common/helpers/db.helper.js +9 -4
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +3 -0
- package/dist/core/common/helpers/input.helper.js +30 -1
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/interceptors/check-security.interceptor.d.ts +5 -0
- package/dist/core/common/interceptors/check-security.interceptor.js +47 -0
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +17 -6
- package/dist/core/common/interfaces/service-options.interface.d.ts +1 -0
- package/dist/core/common/models/core-model.model.d.ts +1 -0
- package/dist/core/common/models/core-model.model.js +3 -0
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/plugins/complexity.plugin.d.ts +9 -0
- package/dist/core/common/plugins/complexity.plugin.js +47 -0
- package/dist/core/common/plugins/complexity.plugin.js.map +1 -0
- package/dist/core/common/plugins/mongoose-id.plugin.d.ts +1 -2
- package/dist/core/common/plugins/mongoose-id.plugin.js +7 -2
- package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
- package/dist/core/common/services/config.service.d.ts +4 -4
- package/dist/core/common/services/config.service.js.map +1 -1
- package/dist/core/common/services/module.service.d.ts +5 -1
- package/dist/core/common/services/module.service.js +15 -3
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.model.d.ts +4 -1
- package/dist/core/modules/auth/core-auth.model.js +12 -1
- package/dist/core/modules/auth/core-auth.model.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.d.ts +3 -1
- package/dist/core/modules/auth/core-auth.module.js +7 -2
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +22 -2
- package/dist/core/modules/auth/core-auth.resolver.js +77 -9
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/auth/guards/auth.guard.d.ts +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js +9 -4
- package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
- package/dist/core/modules/auth/guards/refresh-token.guard.d.ts +4 -0
- package/dist/core/modules/auth/guards/refresh-token.guard.js +18 -0
- package/dist/core/modules/auth/guards/refresh-token.guard.js.map +1 -0
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +1 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +1 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -1
- package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +3 -0
- package/dist/core/modules/auth/interfaces/jwt-payload.interface.d.ts +1 -1
- package/dist/core/modules/auth/services/core-auth-user.service.d.ts +3 -0
- package/dist/core/modules/auth/services/core-auth-user.service.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +23 -5
- package/dist/core/modules/auth/services/core-auth.service.js +121 -13
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.d.ts +12 -0
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js +61 -0
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js.map +1 -0
- package/dist/core/modules/auth/{jwt.strategy.d.ts → strategies/jwt.strategy.d.ts} +4 -3
- package/dist/core/modules/auth/{jwt.strategy.js → strategies/jwt.strategy.js} +12 -5
- package/dist/core/modules/auth/strategies/jwt.strategy.js.map +1 -0
- package/dist/core/modules/user/core-user.model.d.ts +2 -0
- package/dist/core/modules/user/core-user.model.js +12 -0
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +2 -2
- package/dist/core/modules/user/core-user.service.js +2 -2
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core.module.js +12 -2
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/main.js +23 -0
- package/dist/main.js.map +1 -1
- package/dist/server/modules/auth/auth.model.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.d.ts +13 -5
- package/dist/server/modules/auth/auth.resolver.js +21 -12
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/auth/auth.service.d.ts +2 -1
- package/dist/server/modules/auth/auth.service.js +7 -48
- package/dist/server/modules/auth/auth.service.js.map +1 -1
- package/dist/server/modules/file/file.module.js +3 -3
- package/dist/server/modules/file/file.module.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +1 -0
- package/dist/server/modules/user/user.model.js +19 -0
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.service.js +1 -1
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/server/server.module.js +12 -1
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +32 -27
- package/src/config.env.ts +41 -2
- package/src/core/common/filters/http-exception-log.filter.ts +27 -0
- package/src/core/common/helpers/db.helper.ts +15 -5
- package/src/core/common/helpers/input.helper.ts +49 -0
- package/src/core/common/interceptors/check-security.interceptor.ts +51 -0
- package/src/core/common/interfaces/server-options.interface.ts +75 -30
- package/src/core/common/interfaces/service-options.interface.ts +4 -0
- package/src/core/common/models/core-model.model.ts +7 -0
- package/src/core/common/plugins/complexity.plugin.ts +31 -0
- package/src/core/common/plugins/mongoose-id.plugin.js +4 -2
- package/src/core/common/services/config.service.ts +4 -4
- package/src/core/common/services/module.service.ts +26 -4
- package/src/core/modules/auth/core-auth.model.ts +15 -2
- package/src/core/modules/auth/core-auth.module.ts +8 -2
- package/src/core/modules/auth/core-auth.resolver.ts +93 -10
- package/src/core/modules/auth/guards/auth.guard.ts +12 -5
- package/src/core/modules/auth/guards/refresh-token.guard.ts +5 -0
- package/src/core/modules/auth/guards/roles.guard.ts +1 -1
- package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +3 -0
- package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +3 -0
- package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +15 -0
- package/src/core/modules/auth/interfaces/jwt-payload.interface.ts +1 -1
- package/src/core/modules/auth/services/core-auth-user.service.ts +15 -0
- package/src/core/modules/auth/services/core-auth.service.ts +216 -18
- package/src/core/modules/auth/strategies/jwt-refresh.strategy.ts +56 -0
- package/src/core/modules/auth/{jwt.strategy.ts → strategies/jwt.strategy.ts} +16 -5
- package/src/core/modules/user/core-user.model.ts +17 -1
- package/src/core/modules/user/core-user.service.ts +2 -2
- package/src/core.module.ts +14 -2
- package/src/index.ts +6 -1
- package/src/main.ts +29 -0
- package/src/server/modules/auth/auth.model.ts +1 -1
- package/src/server/modules/auth/auth.resolver.ts +26 -8
- package/src/server/modules/auth/auth.service.ts +20 -61
- package/src/server/modules/file/file.module.ts +3 -3
- package/src/server/modules/user/user.model.ts +29 -0
- package/src/server/modules/user/user.service.ts +1 -1
- package/src/server/server.module.ts +12 -1
- 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
|
|
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
|
|
64
|
-
"@nestjs/apollo": "10.
|
|
65
|
-
"@nestjs/common": "9.
|
|
66
|
-
"@nestjs/core": "9.
|
|
67
|
-
"@nestjs/graphql": "10.
|
|
68
|
-
"@nestjs/jwt": "10.0.
|
|
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.
|
|
71
|
-
"@nestjs/platform-express": "9.
|
|
72
|
-
"@nestjs/schedule": "2.
|
|
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.
|
|
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.
|
|
87
|
-
"mongoose": "6.
|
|
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
|
-
"
|
|
92
|
-
"
|
|
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.
|
|
101
|
+
"rimraf": "4.1.2",
|
|
99
102
|
"rxjs": "7.8.0"
|
|
100
103
|
},
|
|
101
104
|
"devDependencies": {
|
|
102
|
-
"@nestjs/testing": "9.
|
|
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.
|
|
110
|
+
"@types/jest": "29.4.0",
|
|
106
111
|
"@types/lodash": "4.14.191",
|
|
107
112
|
"@types/multer": "1.4.7",
|
|
108
|
-
"@types/node": "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.
|
|
113
|
-
"@typescript-eslint/parser": "5.
|
|
117
|
+
"@typescript-eslint/eslint-plugin": "5.52.0",
|
|
118
|
+
"@typescript-eslint/parser": "5.52.0",
|
|
114
119
|
"coffeescript": "2.7.0",
|
|
115
|
-
"eslint": "8.
|
|
120
|
+
"eslint": "8.34.0",
|
|
116
121
|
"eslint-config-prettier": "8.6.0",
|
|
117
122
|
"find-file-up": "2.0.1",
|
|
118
|
-
"grunt": "1.
|
|
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.
|
|
129
|
+
"jest": "29.4.2",
|
|
125
130
|
"npm-watch": "0.11.0",
|
|
126
131
|
"pm2": "5.2.2",
|
|
127
|
-
"prettier": "2.8.
|
|
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.
|
|
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: '
|
|
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-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|