@lenne.tech/nest-server 8.4.0 → 8.6.1
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/core/common/decorators/restricted.decorator.d.ts +3 -2
- package/dist/core/common/decorators/restricted.decorator.js +29 -8
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +1 -1
- package/dist/core/common/helpers/db.helper.js +29 -5
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +12 -0
- package/dist/core/common/helpers/service.helper.js +42 -3
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +8 -0
- package/dist/core/common/interfaces/prepare-input-options.interface.js +3 -0
- package/dist/core/common/interfaces/prepare-input-options.interface.js.map +1 -0
- package/dist/core/common/interfaces/prepare-output-options.interface.d.ts +7 -0
- package/dist/core/common/interfaces/prepare-output-options.interface.js +3 -0
- package/dist/core/common/interfaces/prepare-output-options.interface.js.map +1 -0
- package/dist/core/common/interfaces/service-options.interface.d.ts +5 -15
- package/dist/core/common/services/module.service.d.ts +1 -1
- package/dist/core/common/services/module.service.js +14 -8
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +34 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +34 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/auth/auth.model.js +2 -2
- package/dist/server/modules/auth/auth.model.js.map +1 -1
- package/dist/server/modules/auth/auth.module.js +7 -2
- package/dist/server/modules/auth/auth.module.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.d.ts +8 -3
- package/dist/server/modules/auth/auth.resolver.js +33 -10
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/auth/auth.service.d.ts +15 -0
- package/dist/server/modules/auth/auth.service.js +71 -0
- package/dist/server/modules/auth/auth.service.js.map +1 -0
- package/dist/server/modules/auth/inputs/auth-sign-in.input.d.ts +3 -0
- package/dist/server/modules/auth/inputs/auth-sign-in.input.js +18 -0
- package/dist/server/modules/auth/inputs/auth-sign-in.input.js.map +1 -0
- package/dist/server/modules/auth/inputs/auth-sign-up.input.d.ts +5 -0
- package/dist/server/modules/auth/inputs/auth-sign-up.input.js +34 -0
- package/dist/server/modules/auth/inputs/auth-sign-up.input.js.map +1 -0
- package/dist/server/modules/user/user.resolver.js +12 -11
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.js +0 -4
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/core/common/decorators/restricted.decorator.ts +50 -14
- package/src/core/common/helpers/db.helper.ts +27 -5
- package/src/core/common/helpers/service.helper.ts +72 -2
- package/src/core/common/interfaces/prepare-input-options.interface.ts +11 -0
- package/src/core/common/interfaces/prepare-output-options.interface.ts +10 -0
- package/src/core/common/interfaces/service-options.interface.ts +7 -17
- package/src/core/common/services/module.service.ts +17 -9
- package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +18 -0
- package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +18 -0
- package/src/index.ts +4 -0
- package/src/server/modules/auth/auth.model.ts +5 -5
- package/src/server/modules/auth/auth.module.ts +13 -2
- package/src/server/modules/auth/auth.resolver.ts +30 -12
- package/src/server/modules/auth/auth.service.ts +83 -0
- package/src/server/modules/auth/inputs/auth-sign-in.input.ts +10 -0
- package/src/server/modules/auth/inputs/auth-sign-up.input.ts +18 -0
- package/src/server/modules/user/user.resolver.ts +12 -11
- package/src/server/modules/user/user.service.ts +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.1",
|
|
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",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"prettier": "2.6.2",
|
|
113
113
|
"pretty-quick": "3.1.3",
|
|
114
114
|
"supertest": "6.2.3",
|
|
115
|
-
"ts-jest": "28.0.
|
|
115
|
+
"ts-jest": "28.0.2",
|
|
116
116
|
"ts-morph": "14.0.0",
|
|
117
117
|
"ts-node": "10.7.0",
|
|
118
118
|
"tsconfig-paths": "4.0.0",
|
|
@@ -9,6 +9,10 @@ import { RequireAtLeastOne } from '../types/required-at-least-one.type';
|
|
|
9
9
|
* Restricted meta key
|
|
10
10
|
*/
|
|
11
11
|
const restrictedMetaKey = Symbol('restricted');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Restricted type
|
|
15
|
+
*/
|
|
12
16
|
export type RestrictedType = (
|
|
13
17
|
| string
|
|
14
18
|
| RequireAtLeastOne<
|
|
@@ -31,14 +35,17 @@ export type RestrictedType = (
|
|
|
31
35
|
* properties of the processed item, which is specified by the value of `memberOf`
|
|
32
36
|
* Via processType the restriction can be set for input or output only
|
|
33
37
|
*/
|
|
34
|
-
export const Restricted = (...rolesOrMember: RestrictedType): PropertyDecorator => {
|
|
38
|
+
export const Restricted = (...rolesOrMember: RestrictedType): ClassDecorator & PropertyDecorator => {
|
|
35
39
|
return Reflect.metadata(restrictedMetaKey, rolesOrMember);
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
|
-
* Get restricted
|
|
43
|
+
* Get restricted data for (property of) object
|
|
40
44
|
*/
|
|
41
|
-
export const getRestricted = (object: unknown, propertyKey
|
|
45
|
+
export const getRestricted = (object: unknown, propertyKey?: string): RestrictedType => {
|
|
46
|
+
if (!propertyKey) {
|
|
47
|
+
return Reflect.getMetadata(restrictedMetaKey, object);
|
|
48
|
+
}
|
|
42
49
|
return Reflect.getMetadata(restrictedMetaKey, object, propertyKey);
|
|
43
50
|
};
|
|
44
51
|
|
|
@@ -49,11 +56,18 @@ export const getRestricted = (object: unknown, propertyKey: string): RestrictedT
|
|
|
49
56
|
export const checkRestricted = (
|
|
50
57
|
data: any,
|
|
51
58
|
user: { id: any; hasRole: (roles: string[]) => boolean },
|
|
52
|
-
options: {
|
|
59
|
+
options: {
|
|
60
|
+
dbObject?: any;
|
|
61
|
+
ignoreUndefined?: boolean;
|
|
62
|
+
processType?: ProcessType;
|
|
63
|
+
removeUndefinedFromResultArray?: boolean;
|
|
64
|
+
throwError?: boolean;
|
|
65
|
+
} = {},
|
|
53
66
|
processedObjects: any[] = []
|
|
54
67
|
) => {
|
|
55
68
|
const config = {
|
|
56
69
|
ignoreUndefined: true,
|
|
70
|
+
removeUndefinedFromResultArray: true,
|
|
57
71
|
throwError: true,
|
|
58
72
|
...options,
|
|
59
73
|
};
|
|
@@ -72,18 +86,15 @@ export const checkRestricted = (
|
|
|
72
86
|
// Array
|
|
73
87
|
if (Array.isArray(data)) {
|
|
74
88
|
// Check array items
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Object
|
|
79
|
-
for (const propertyKey of Object.keys(data)) {
|
|
80
|
-
// Check undefined
|
|
81
|
-
if (data[propertyKey] === undefined && config.ignoreUndefined) {
|
|
82
|
-
continue;
|
|
89
|
+
let result = data.map((item) => checkRestricted(item, user, config, processedObjects));
|
|
90
|
+
if (!config.throwError && config.removeUndefinedFromResultArray) {
|
|
91
|
+
result = result.filter((item) => item !== undefined);
|
|
83
92
|
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
// Check function
|
|
97
|
+
const validateRestricted = (restricted) => {
|
|
87
98
|
let valid = true;
|
|
88
99
|
if (restricted?.length) {
|
|
89
100
|
valid = false;
|
|
@@ -110,6 +121,7 @@ export const checkRestricted = (
|
|
|
110
121
|
// Check roles
|
|
111
122
|
if (
|
|
112
123
|
user?.hasRole(roles) ||
|
|
124
|
+
(user?.id && roles.includes(RoleEnum.S_USER)) ||
|
|
113
125
|
(roles.includes(RoleEnum.S_CREATOR) && getIncludedIds(config.dbObject?.createdBy, user))
|
|
114
126
|
) {
|
|
115
127
|
valid = true;
|
|
@@ -161,6 +173,30 @@ export const checkRestricted = (
|
|
|
161
173
|
}
|
|
162
174
|
}
|
|
163
175
|
}
|
|
176
|
+
return valid;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Check object
|
|
180
|
+
const objectRestrictions = getRestricted(data.constructor);
|
|
181
|
+
const objectIsValid = validateRestricted(objectRestrictions);
|
|
182
|
+
if (!objectIsValid) {
|
|
183
|
+
// Throw error
|
|
184
|
+
if (config.throwError) {
|
|
185
|
+
throw new UnauthorizedException('The current user has no access rights for ' + data.constructor?.name);
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check properties of object
|
|
191
|
+
for (const propertyKey of Object.keys(data)) {
|
|
192
|
+
// Check undefined
|
|
193
|
+
if (data[propertyKey] === undefined && config.ignoreUndefined) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Check restricted
|
|
198
|
+
const restricted = getRestricted(data, propertyKey);
|
|
199
|
+
const valid = validateRestricted(restricted);
|
|
164
200
|
|
|
165
201
|
// Check rights
|
|
166
202
|
if (valid) {
|
|
@@ -275,19 +275,41 @@ export function getElementsViaIds<T = any>(
|
|
|
275
275
|
/**
|
|
276
276
|
* Get populate options from GraphQL resolve info
|
|
277
277
|
*/
|
|
278
|
-
export function getPopulateOptions(info: GraphQLResolveInfo,
|
|
278
|
+
export function getPopulateOptions(info: GraphQLResolveInfo, dotSeparatedSelect?: string): PopulateOptions[] {
|
|
279
279
|
const result = [];
|
|
280
280
|
|
|
281
281
|
if (!info?.fieldNodes?.length) {
|
|
282
282
|
return result;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
if (dotSeparatedSelect?.length) {
|
|
286
|
+
for (const fieldNode of info.fieldNodes) {
|
|
287
|
+
const selects = dotSeparatedSelect.split('.');
|
|
288
|
+
const fieldNodeName = selects.shift();
|
|
289
|
+
if (fieldNode?.name?.value === fieldNodeName && fieldNode?.selectionSet?.selections?.length) {
|
|
290
|
+
if (!selects.length) {
|
|
291
|
+
return getPopulatOptionsFromSelections(fieldNode.selectionSet.selections);
|
|
292
|
+
} else {
|
|
293
|
+
let selections = fieldNode.selectionSet.selections;
|
|
294
|
+
do {
|
|
295
|
+
if (!selections?.length) {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
const select = selects.shift();
|
|
299
|
+
for (const selection of selections) {
|
|
300
|
+
if ((selection as FieldNode)?.name?.value === select) {
|
|
301
|
+
selections = (selection as FieldNode)?.selectionSet?.selections;
|
|
302
|
+
if (!selects.length) {
|
|
303
|
+
return getPopulatOptionsFromSelections(selections);
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} while (selects.length);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
288
311
|
}
|
|
289
312
|
}
|
|
290
|
-
|
|
291
313
|
return result;
|
|
292
314
|
}
|
|
293
315
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { UnauthorizedException } from '@nestjs/common';
|
|
2
2
|
import * as bcrypt from 'bcrypt';
|
|
3
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
4
|
import * as _ from 'lodash';
|
|
4
5
|
import { RoleEnum } from '../enums/role.enum';
|
|
6
|
+
import { PrepareInputOptions } from '../interfaces/prepare-input-options.interface';
|
|
7
|
+
import { PrepareOutputOptions } from '../interfaces/prepare-output-options.interface';
|
|
8
|
+
import { ResolveSelector } from '../interfaces/resolve-selector.interface';
|
|
9
|
+
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* Helper class for services
|
|
@@ -54,6 +59,7 @@ export async function prepareInput<T = any>(
|
|
|
54
59
|
clone?: boolean;
|
|
55
60
|
getNewArray?: boolean;
|
|
56
61
|
removeUndefined?: boolean;
|
|
62
|
+
targetModel?: new (...args: any[]) => T;
|
|
57
63
|
} = {}
|
|
58
64
|
): Promise<T> {
|
|
59
65
|
// Configuration
|
|
@@ -86,6 +92,15 @@ export async function prepareInput<T = any>(
|
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
// Map input if target model exist
|
|
96
|
+
if (config.targetModel && !(input instanceof config.targetModel)) {
|
|
97
|
+
if ((config.targetModel as any)?.map) {
|
|
98
|
+
input = await (config.targetModel as any).map(input);
|
|
99
|
+
} else {
|
|
100
|
+
input = plainToInstance(config.targetModel, input);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
89
104
|
// Remove undefined properties to avoid unwanted overwrites
|
|
90
105
|
if (config.removeUndefined) {
|
|
91
106
|
Object.keys(input).forEach((key) => input[key] === undefined && delete input[key]);
|
|
@@ -171,8 +186,12 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
|
|
|
171
186
|
}
|
|
172
187
|
|
|
173
188
|
// Map output if target model exist
|
|
174
|
-
if (config.targetModel) {
|
|
175
|
-
|
|
189
|
+
if (config.targetModel && !(output instanceof config.targetModel)) {
|
|
190
|
+
if ((config.targetModel as any)?.map) {
|
|
191
|
+
output = await (config.targetModel as any).map(output);
|
|
192
|
+
} else {
|
|
193
|
+
output = plainToInstance(config.targetModel, output);
|
|
194
|
+
}
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
// Remove password if exists
|
|
@@ -198,3 +217,54 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
|
|
|
198
217
|
// Return prepared output
|
|
199
218
|
return output;
|
|
200
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Prepare service options
|
|
223
|
+
*/
|
|
224
|
+
export function prepareServiceOptions(
|
|
225
|
+
serviceOptions: ServiceOptions,
|
|
226
|
+
options?: {
|
|
227
|
+
clone?: boolean;
|
|
228
|
+
inputType?: any;
|
|
229
|
+
outputType?: any;
|
|
230
|
+
subFieldSelection?: string;
|
|
231
|
+
prepareInput?: PrepareInputOptions;
|
|
232
|
+
prepareOutput?: PrepareOutputOptions;
|
|
233
|
+
}
|
|
234
|
+
): ServiceOptions {
|
|
235
|
+
// Set default values
|
|
236
|
+
const config = {
|
|
237
|
+
clone: true,
|
|
238
|
+
...options,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Clone
|
|
242
|
+
if (serviceOptions && config.clone) {
|
|
243
|
+
serviceOptions = _.cloneDeep(serviceOptions);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Init if not exists
|
|
247
|
+
serviceOptions = serviceOptions || {};
|
|
248
|
+
|
|
249
|
+
// Set properties
|
|
250
|
+
serviceOptions.inputType = serviceOptions.inputType || options?.inputType;
|
|
251
|
+
serviceOptions.outputType = serviceOptions.outputType || options?.outputType;
|
|
252
|
+
|
|
253
|
+
// Set properties which can deactivate handling when falsy
|
|
254
|
+
if (!serviceOptions.prepareInput && 'prepareInput' in config) {
|
|
255
|
+
serviceOptions.prepareInput = config.prepareInput;
|
|
256
|
+
}
|
|
257
|
+
if (!serviceOptions.prepareOutput && 'prepareOutput' in config) {
|
|
258
|
+
serviceOptions.prepareOutput = config.prepareOutput;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Set subfield selection
|
|
262
|
+
if (config.subFieldSelection) {
|
|
263
|
+
if ((serviceOptions.fieldSelection as ResolveSelector)?.select) {
|
|
264
|
+
(serviceOptions.fieldSelection as ResolveSelector).select += '.' + config.subFieldSelection;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Return (cloned and) prepared service options
|
|
269
|
+
return serviceOptions;
|
|
270
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Model } from 'mongoose';
|
|
2
2
|
import { FieldSelection } from '../types/field-selection.type';
|
|
3
|
+
import { PrepareInputOptions } from './prepare-input-options.interface';
|
|
4
|
+
import { PrepareOutputOptions } from './prepare-output-options.interface';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* General service options
|
|
@@ -26,6 +28,9 @@ export interface ServiceOptions {
|
|
|
26
28
|
// Overwrites type of input (array items)
|
|
27
29
|
inputType?: new (...params: any[]) => any;
|
|
28
30
|
|
|
31
|
+
// Overwrites type of output (array items)
|
|
32
|
+
outputType?: new (...params: any[]) => any;
|
|
33
|
+
|
|
29
34
|
// Process field selection
|
|
30
35
|
// If {} or not set, then the field selection runs with defaults
|
|
31
36
|
// If falsy, then the field selection will not be automatically executed
|
|
@@ -37,31 +42,16 @@ export interface ServiceOptions {
|
|
|
37
42
|
// Prepare input configuration:
|
|
38
43
|
// If {} or not set, then the prepareInput function will run with defaults
|
|
39
44
|
// If falsy, then the prepareInput function is not executed
|
|
40
|
-
prepareInput?:
|
|
41
|
-
[key: string]: any;
|
|
42
|
-
create?: boolean;
|
|
43
|
-
clone?: boolean;
|
|
44
|
-
getNewArray?: boolean;
|
|
45
|
-
removeUndefined?: boolean;
|
|
46
|
-
};
|
|
45
|
+
prepareInput?: PrepareInputOptions;
|
|
47
46
|
|
|
48
47
|
// Prepare output configuration:
|
|
49
48
|
// If {} or not set, then the prepareInput function will run with defaults
|
|
50
49
|
// If falsy, then the prepareInput function is not executed
|
|
51
|
-
prepareOutput?:
|
|
52
|
-
[key: string]: any;
|
|
53
|
-
clone?: boolean;
|
|
54
|
-
getNewArray?: boolean;
|
|
55
|
-
removeUndefined?: boolean;
|
|
56
|
-
targetModel?: new (...args: any[]) => any;
|
|
57
|
-
};
|
|
50
|
+
prepareOutput?: PrepareOutputOptions;
|
|
58
51
|
|
|
59
52
|
// Whether to publish action via GraphQL subscription
|
|
60
53
|
pubSub?: boolean;
|
|
61
54
|
|
|
62
|
-
// Overwrites type of result (array items)
|
|
63
|
-
resultType?: new (...params: any[]) => any;
|
|
64
|
-
|
|
65
55
|
// Roles (as string) to check
|
|
66
56
|
roles?: string | string[];
|
|
67
57
|
}
|
|
@@ -84,7 +84,11 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
84
84
|
|
|
85
85
|
// Prepare input
|
|
86
86
|
if (config.prepareInput && this.prepareInput) {
|
|
87
|
-
|
|
87
|
+
const opts = config.prepareInput;
|
|
88
|
+
if (!opts.targetModel && config.inputType) {
|
|
89
|
+
opts.targetModel = config.inputType;
|
|
90
|
+
}
|
|
91
|
+
await this.prepareInput(config.input, opts);
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
// Get DB object
|
|
@@ -101,7 +105,7 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
101
105
|
if (config.input && config.checkRights && this.checkRights) {
|
|
102
106
|
const opts: any = { dbObject: config.dbObject, processType: ProcessType.INPUT, roles: config.roles };
|
|
103
107
|
if (config.inputType) {
|
|
104
|
-
opts.metatype = config.
|
|
108
|
+
opts.metatype = config.inputType;
|
|
105
109
|
}
|
|
106
110
|
config.input = await this.checkRights(config.input, config.currentUser as any, opts);
|
|
107
111
|
}
|
|
@@ -116,11 +120,11 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
116
120
|
|
|
117
121
|
// Prepare output
|
|
118
122
|
if (config.prepareOutput && this.prepareOutput) {
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
123
|
+
const opts = config.prepareOutput;
|
|
124
|
+
if (!opts.targetModel && config.outputType) {
|
|
125
|
+
opts.targetModel = config.outputType;
|
|
122
126
|
}
|
|
123
|
-
result = await this.prepareOutput(result,
|
|
127
|
+
result = await this.prepareOutput(result, opts);
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
// Check output rights
|
|
@@ -131,8 +135,8 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
131
135
|
roles: config.roles,
|
|
132
136
|
throwError: false,
|
|
133
137
|
};
|
|
134
|
-
if (config.
|
|
135
|
-
opts.metatype = config.
|
|
138
|
+
if (config.outputType) {
|
|
139
|
+
opts.metatype = config.outputType;
|
|
136
140
|
}
|
|
137
141
|
result = await this.checkRights(result, config.currentUser as any, opts);
|
|
138
142
|
}
|
|
@@ -145,7 +149,11 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
145
149
|
* Prepare input before save
|
|
146
150
|
*/
|
|
147
151
|
async prepareInput(input: Record<string, any>, options: ServiceOptions = {}) {
|
|
148
|
-
|
|
152
|
+
const config = {
|
|
153
|
+
targetModel: this.mainModelConstructor,
|
|
154
|
+
...options?.prepareInput,
|
|
155
|
+
};
|
|
156
|
+
return prepareInput(input, options.currentUser, config);
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Field, InputType } from '@nestjs/graphql';
|
|
2
|
+
import { CoreInput } from '../../../common/inputs/core-input.input';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SignIn input
|
|
6
|
+
*/
|
|
7
|
+
@InputType({ description: 'Sign-in input' })
|
|
8
|
+
export class CoreAuthSignInInput extends CoreInput {
|
|
9
|
+
// ===================================================================================================================
|
|
10
|
+
// Properties
|
|
11
|
+
// ===================================================================================================================
|
|
12
|
+
|
|
13
|
+
@Field({ description: 'Email', nullable: false })
|
|
14
|
+
email: string = undefined;
|
|
15
|
+
|
|
16
|
+
@Field({ description: 'Password', nullable: false })
|
|
17
|
+
password: string = undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Field, InputType } from '@nestjs/graphql';
|
|
2
|
+
import { CoreInput } from '../../../common/inputs/core-input.input';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SignUp input
|
|
6
|
+
*/
|
|
7
|
+
@InputType({ description: 'Sign-up input' })
|
|
8
|
+
export class CoreAuthSignUpInput extends CoreInput {
|
|
9
|
+
// ===================================================================================================================
|
|
10
|
+
// Properties
|
|
11
|
+
// ===================================================================================================================
|
|
12
|
+
|
|
13
|
+
@Field({ description: 'Email', nullable: false })
|
|
14
|
+
email: string = undefined;
|
|
15
|
+
|
|
16
|
+
@Field({ description: 'Password', nullable: false })
|
|
17
|
+
password: string = undefined;
|
|
18
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,8 @@ export * from './core/common/inputs/sort.input';
|
|
|
35
35
|
export * from './core/common/interceptors/check-response.interceptor';
|
|
36
36
|
export * from './core/common/interfaces/core-persistence-model.interface';
|
|
37
37
|
export * from './core/common/interfaces/mailjet-options.interface';
|
|
38
|
+
export * from './core/common/interfaces/prepare-input-options.interface';
|
|
39
|
+
export * from './core/common/interfaces/prepare-output-options.interface';
|
|
38
40
|
export * from './core/common/interfaces/resolve-selector.interface';
|
|
39
41
|
export * from './core/common/interfaces/server-options.interface';
|
|
40
42
|
export * from './core/common/interfaces/service-options.interface';
|
|
@@ -65,6 +67,8 @@ export * from './core/common/types/string-or-object-id.type';
|
|
|
65
67
|
|
|
66
68
|
export * from './core/modules/auth/guards/auth.guard';
|
|
67
69
|
export * from './core/modules/auth/guards/roles.guard';
|
|
70
|
+
export * from './core/modules/auth/inputs/core-auth-sign-in.input';
|
|
71
|
+
export * from './core/modules/auth/inputs/core-auth-sign-up.input';
|
|
68
72
|
export * from './core/modules/auth/interfaces/core-auth-user.interface';
|
|
69
73
|
export * from './core/modules/auth/interfaces/jwt-payload.interface';
|
|
70
74
|
export * from './core/modules/auth/services/core-auth.service';
|
|
@@ -3,22 +3,22 @@ import { CoreAuthModel } from '../../../core/modules/auth/core-auth.model';
|
|
|
3
3
|
import { User } from '../user/user.model';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Authentication data
|
|
7
7
|
*/
|
|
8
|
-
@ObjectType({ description: '
|
|
8
|
+
@ObjectType({ description: 'Authentication data' })
|
|
9
9
|
export class Auth extends CoreAuthModel {
|
|
10
10
|
// ===================================================================================================================
|
|
11
11
|
// Properties
|
|
12
12
|
// ===================================================================================================================
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Signed
|
|
15
|
+
* Signed-in user
|
|
16
16
|
*/
|
|
17
|
-
@Field((
|
|
17
|
+
@Field(() => User, { description: 'User who signed in' })
|
|
18
18
|
user: User = undefined;
|
|
19
19
|
|
|
20
20
|
// ===================================================================================================================
|
|
21
|
-
//
|
|
21
|
+
// Methods
|
|
22
22
|
// ===================================================================================================================
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { DynamicModule, Module } from '@nestjs/common';
|
|
2
2
|
import { JwtModuleOptions } from '@nestjs/jwt';
|
|
3
|
+
import { EmailService } from '../../../core/common/services/email.service';
|
|
3
4
|
import { CoreAuthModule } from '../../../core/modules/auth/core-auth.module';
|
|
4
5
|
import { UserModule } from '../user/user.module';
|
|
5
6
|
import { UserService } from '../user/user.service';
|
|
6
7
|
import { AuthResolver } from './auth.resolver';
|
|
8
|
+
import { AuthService } from './auth.service';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* CoreAuthModule to handle user authentication
|
|
@@ -17,8 +19,17 @@ export class AuthModule {
|
|
|
17
19
|
static forRoot(options: JwtModuleOptions): DynamicModule {
|
|
18
20
|
return {
|
|
19
21
|
module: AuthModule,
|
|
20
|
-
imports: [
|
|
21
|
-
|
|
22
|
+
imports: [
|
|
23
|
+
CoreAuthModule.forRoot(UserModule, UserService, {
|
|
24
|
+
...options,
|
|
25
|
+
...{
|
|
26
|
+
// imports: [], // Integrate additional Services here to resolve dependencies
|
|
27
|
+
// providers: [] // Integrate additional Providers here to resolve dependencies
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
EmailService,
|
|
31
|
+
],
|
|
32
|
+
providers: [AuthResolver, AuthService],
|
|
22
33
|
exports: [AuthResolver, CoreAuthModule],
|
|
23
34
|
};
|
|
24
35
|
}
|
|
@@ -1,22 +1,40 @@
|
|
|
1
|
-
import { Args, Info, Query, Resolver } from '@nestjs/graphql';
|
|
1
|
+
import { Args, Info, Mutation, Query, Resolver } from '@nestjs/graphql';
|
|
2
2
|
import { GraphQLResolveInfo } from 'graphql';
|
|
3
|
-
import { CoreAuthResolver } from '../../../core/modules/auth/core-auth.resolver';
|
|
4
3
|
import { Auth } from './auth.model';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
import { AuthSignInInput } from './inputs/auth-sign-in.input';
|
|
6
|
+
import { AuthSignUpInput } from './inputs/auth-sign-up.input';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Authentication resolver for the sign in
|
|
8
10
|
*/
|
|
9
|
-
@Resolver((
|
|
10
|
-
export class AuthResolver
|
|
11
|
+
@Resolver(() => Auth)
|
|
12
|
+
export class AuthResolver {
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
14
|
+
* Integrate services
|
|
13
15
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
20
|
-
|
|
16
|
+
constructor(private readonly authService: AuthService) {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* SignIn for User
|
|
20
|
+
*/
|
|
21
|
+
@Query(() => Auth, { description: 'Sign in and get JWT token' })
|
|
22
|
+
async signIn(@Info() info: GraphQLResolveInfo, @Args('input') input: AuthSignInInput): Promise<Auth> {
|
|
23
|
+
return this.authService.signIn(input, {
|
|
24
|
+
fieldSelection: { info, select: 'signIn' },
|
|
25
|
+
inputType: AuthSignInInput,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sign up for user
|
|
31
|
+
*/
|
|
32
|
+
@Mutation(() => Auth, {
|
|
33
|
+
description: 'Sign up user and get JWT token',
|
|
34
|
+
})
|
|
35
|
+
async signUp(@Info() info: GraphQLResolveInfo, @Args('input') input: AuthSignUpInput): Promise<Auth> {
|
|
36
|
+
return this.authService.signUp(input, {
|
|
37
|
+
fieldSelection: { info, select: 'signUp' },
|
|
38
|
+
});
|
|
21
39
|
}
|
|
22
40
|
}
|