@lenne.tech/nest-server 8.3.1 → 8.6.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/core/common/decorators/restricted.decorator.d.ts +12 -5
- package/dist/core/common/decorators/restricted.decorator.js +91 -29
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/enums/process-type.enum.d.ts +4 -0
- package/dist/core/common/enums/process-type.enum.js +9 -0
- package/dist/core/common/enums/process-type.enum.js.map +1 -0
- package/dist/core/common/enums/role.enum.d.ts +1 -2
- package/dist/core/common/enums/role.enum.js +0 -1
- package/dist/core/common/enums/role.enum.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +4 -4
- package/dist/core/common/helpers/db.helper.js +54 -30
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +6 -5
- package/dist/core/common/helpers/input.helper.js +4 -15
- package/dist/core/common/helpers/input.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 -17
- package/dist/core/common/models/core-persistence.model.d.ts +0 -1
- package/dist/core/common/models/core-persistence.model.js +0 -10
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/services/module.service.d.ts +4 -4
- package/dist/core/common/services/module.service.js +27 -26
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/common/types/require-only-one.type.d.ts +3 -0
- package/dist/core/common/types/require-only-one.type.js +3 -0
- package/dist/core/common/types/require-only-one.type.js.map +1 -0
- package/dist/core/common/types/required-at-least-one.type.d.ts +3 -0
- package/dist/core/common/types/required-at-least-one.type.js +3 -0
- package/dist/core/common/types/required-at-least-one.type.js.map +1 -0
- 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/core.module.js +0 -5
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -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.model.d.ts +1 -1
- package/dist/server/modules/user/user.model.js +1 -1
- package/dist/server/modules/user/user.model.js.map +1 -1
- 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 +1 -5
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/core/common/decorators/restricted.decorator.ts +150 -53
- package/src/core/common/enums/process-type.enum.ts +7 -0
- package/src/core/common/enums/role.enum.ts +1 -4
- package/src/core/common/helpers/db.helper.ts +70 -56
- package/src/core/common/helpers/input.helper.ts +24 -15
- 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 +8 -22
- package/src/core/common/models/core-persistence.model.ts +0 -11
- package/src/core/common/services/module.service.ts +32 -31
- package/src/core/common/types/require-only-one.type.ts +6 -0
- package/src/core/common/types/required-at-least-one.type.ts +6 -0
- 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/core.module.ts +1 -19
- package/src/index.ts +5 -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 +84 -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.model.ts +2 -2
- package/src/server/modules/user/user.resolver.ts +12 -11
- package/src/server/modules/user/user.service.ts +3 -11
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Model
|
|
1
|
+
import { Model } from 'mongoose';
|
|
2
2
|
import { FieldSelection } from '../types/field-selection.type';
|
|
3
|
-
import {
|
|
3
|
+
import { PrepareInputOptions } from './prepare-input-options.interface';
|
|
4
|
+
import { PrepareOutputOptions } from './prepare-output-options.interface';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* General service options
|
|
@@ -14,7 +15,7 @@ export interface ServiceOptions {
|
|
|
14
15
|
// If truly (default): input data will be checked
|
|
15
16
|
checkRights?: boolean;
|
|
16
17
|
|
|
17
|
-
// Current user to set ownership, check
|
|
18
|
+
// Current user to set ownership, check rights and other things
|
|
18
19
|
currentUser?: {
|
|
19
20
|
[key: string]: any;
|
|
20
21
|
id: string;
|
|
@@ -27,8 +28,8 @@ export interface ServiceOptions {
|
|
|
27
28
|
// Overwrites type of input (array items)
|
|
28
29
|
inputType?: new (...params: any[]) => any;
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
-
|
|
31
|
+
// Overwrites type of output (array items)
|
|
32
|
+
outputType?: new (...params: any[]) => any;
|
|
32
33
|
|
|
33
34
|
// Process field selection
|
|
34
35
|
// If {} or not set, then the field selection runs with defaults
|
|
@@ -41,31 +42,16 @@ export interface ServiceOptions {
|
|
|
41
42
|
// Prepare input configuration:
|
|
42
43
|
// If {} or not set, then the prepareInput function will run with defaults
|
|
43
44
|
// If falsy, then the prepareInput function is not executed
|
|
44
|
-
prepareInput?:
|
|
45
|
-
[key: string]: any;
|
|
46
|
-
create?: boolean;
|
|
47
|
-
clone?: boolean;
|
|
48
|
-
getNewArray?: boolean;
|
|
49
|
-
removeUndefined?: boolean;
|
|
50
|
-
};
|
|
45
|
+
prepareInput?: PrepareInputOptions;
|
|
51
46
|
|
|
52
47
|
// Prepare output configuration:
|
|
53
48
|
// If {} or not set, then the prepareInput function will run with defaults
|
|
54
49
|
// If falsy, then the prepareInput function is not executed
|
|
55
|
-
prepareOutput?:
|
|
56
|
-
[key: string]: any;
|
|
57
|
-
clone?: boolean;
|
|
58
|
-
getNewArray?: boolean;
|
|
59
|
-
removeUndefined?: boolean;
|
|
60
|
-
targetModel?: new (...args: any[]) => any;
|
|
61
|
-
};
|
|
50
|
+
prepareOutput?: PrepareOutputOptions;
|
|
62
51
|
|
|
63
52
|
// Whether to publish action via GraphQL subscription
|
|
64
53
|
pubSub?: boolean;
|
|
65
54
|
|
|
66
|
-
// Overwrites type of result (array items)
|
|
67
|
-
resultType?: new (...params: any[]) => any;
|
|
68
|
-
|
|
69
55
|
// Roles (as string) to check
|
|
70
56
|
roles?: string | string[];
|
|
71
57
|
}
|
|
@@ -59,16 +59,6 @@ export abstract class CorePersistenceModel extends CoreModel {
|
|
|
59
59
|
@Prop([String])
|
|
60
60
|
labels: string[] = undefined;
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
* IDs of the Owners
|
|
64
|
-
*/
|
|
65
|
-
@Field((type) => [String], {
|
|
66
|
-
description: 'Users who own the object',
|
|
67
|
-
nullable: true,
|
|
68
|
-
})
|
|
69
|
-
@Prop([String])
|
|
70
|
-
ownerIds: string[] = undefined;
|
|
71
|
-
|
|
72
62
|
/**
|
|
73
63
|
* Tags for the object
|
|
74
64
|
*/
|
|
@@ -97,7 +87,6 @@ export abstract class CorePersistenceModel extends CoreModel {
|
|
|
97
87
|
super.init();
|
|
98
88
|
this.createdAt = this.createdAt === undefined ? new Date() : this.createdAt;
|
|
99
89
|
this.labels = this.labels === undefined ? [] : this.labels;
|
|
100
|
-
this.ownerIds = this.ownerIds === undefined ? [] : this.ownerIds;
|
|
101
90
|
this.tags = this.tags === undefined ? [] : this.tags;
|
|
102
91
|
this.updatedAt = this.tags === undefined ? this.createdAt : this.updatedAt;
|
|
103
92
|
return this;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Document, Model, Types } from 'mongoose';
|
|
2
|
+
import { ProcessType } from '../enums/process-type.enum';
|
|
2
3
|
import { getStringIds, popAndMap } from '../helpers/db.helper';
|
|
3
4
|
import { check } from '../helpers/input.helper';
|
|
4
5
|
import { prepareInput, prepareOutput } from '../helpers/service.helper';
|
|
5
6
|
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
6
7
|
import { CoreModel } from '../models/core-model.model';
|
|
7
8
|
import { FieldSelection } from '../types/field-selection.type';
|
|
8
|
-
import { IdsType } from '../types/ids.type';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Module service class to be extended by concrete module services
|
|
@@ -39,9 +39,9 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
39
39
|
input: any,
|
|
40
40
|
currentUser: { id: any; hasRole: (roles: string[]) => boolean },
|
|
41
41
|
options?: {
|
|
42
|
-
|
|
42
|
+
dbObject?: any;
|
|
43
43
|
metatype?: any;
|
|
44
|
-
|
|
44
|
+
processType?: ProcessType;
|
|
45
45
|
roles?: string | string[];
|
|
46
46
|
throwError?: boolean;
|
|
47
47
|
}
|
|
@@ -84,36 +84,28 @@ 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
|
|
91
|
-
|
|
92
|
-
if (config.dbObject) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
config.dbObject = dbObject;
|
|
97
|
-
}
|
|
95
|
+
if (config.dbObject && config.checkRights && this.checkRights) {
|
|
96
|
+
if (typeof config.dbObject === 'string' || config.dbObject instanceof Types.ObjectId) {
|
|
97
|
+
const dbObject = await this.get(getStringIds(config.dbObject));
|
|
98
|
+
if (dbObject) {
|
|
99
|
+
config.dbObject = dbObject;
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
|
-
return config.dbObject;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
// Get owner IDs
|
|
104
|
-
let ownerIds = undefined;
|
|
105
|
-
if (config.checkRights && this.checkRights) {
|
|
106
|
-
ownerIds = getStringIds(config.ownerIds);
|
|
107
|
-
if (!ownerIds?.length) {
|
|
108
|
-
ownerIds = (await getDbObject())?.ownerIds;
|
|
109
|
-
}
|
|
110
102
|
}
|
|
111
103
|
|
|
112
104
|
// Check rights for input
|
|
113
105
|
if (config.input && config.checkRights && this.checkRights) {
|
|
114
|
-
const opts: any = {
|
|
106
|
+
const opts: any = { dbObject: config.dbObject, processType: ProcessType.INPUT, roles: config.roles };
|
|
115
107
|
if (config.inputType) {
|
|
116
|
-
opts.metatype = config.
|
|
108
|
+
opts.metatype = config.inputType;
|
|
117
109
|
}
|
|
118
110
|
config.input = await this.checkRights(config.input, config.currentUser as any, opts);
|
|
119
111
|
}
|
|
@@ -128,18 +120,23 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
128
120
|
|
|
129
121
|
// Prepare output
|
|
130
122
|
if (config.prepareOutput && this.prepareOutput) {
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
123
|
+
const opts = config.prepareOutput;
|
|
124
|
+
if (!opts.targetModel && config.outputType) {
|
|
125
|
+
opts.targetModel = config.outputType;
|
|
134
126
|
}
|
|
135
|
-
result = await this.prepareOutput(result,
|
|
127
|
+
result = await this.prepareOutput(result, opts);
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
// Check output rights
|
|
139
131
|
if (config.checkRights && this.checkRights) {
|
|
140
|
-
const opts: any = {
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
const opts: any = {
|
|
133
|
+
dbObject: config.dbObject,
|
|
134
|
+
processType: ProcessType.OUTPUT,
|
|
135
|
+
roles: config.roles,
|
|
136
|
+
throwError: false,
|
|
137
|
+
};
|
|
138
|
+
if (config.outputType) {
|
|
139
|
+
opts.metatype = config.outputType;
|
|
143
140
|
}
|
|
144
141
|
result = await this.checkRights(result, config.currentUser as any, opts);
|
|
145
142
|
}
|
|
@@ -152,7 +149,11 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
152
149
|
* Prepare input before save
|
|
153
150
|
*/
|
|
154
151
|
async prepareInput(input: Record<string, any>, options: ServiceOptions = {}) {
|
|
155
|
-
|
|
152
|
+
const config = {
|
|
153
|
+
targetModel: this.mainModelConstructor,
|
|
154
|
+
...options?.prepareInput,
|
|
155
|
+
};
|
|
156
|
+
return prepareInput(input, options.currentUser, config);
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
/**
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Require only one of the optional properties
|
|
3
|
+
* See https://stackoverflow.com/a/49725198
|
|
4
|
+
*/
|
|
5
|
+
export type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
|
|
6
|
+
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>> }[Keys];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Require at least on of optional properties
|
|
3
|
+
* See https://stackoverflow.com/a/49725198
|
|
4
|
+
*/
|
|
5
|
+
export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
|
|
6
|
+
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];
|
|
@@ -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/core.module.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { DynamicModule, Global, Module, UnauthorizedException } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { APP_PIPE } from '@nestjs/core';
|
|
3
3
|
import { GraphQLModule } from '@nestjs/graphql';
|
|
4
4
|
import { merge } from './core/common/helpers/config.helper';
|
|
5
|
-
import { CheckResponseInterceptor } from './core/common/interceptors/check-response.interceptor';
|
|
6
5
|
import { IServerOptions } from './core/common/interfaces/server-options.interface';
|
|
7
6
|
import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
|
|
8
7
|
import { ConfigService } from './core/common/services/config.service';
|
|
@@ -19,8 +18,6 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
|
19
18
|
* - MongooseModule
|
|
20
19
|
* - GraphQL
|
|
21
20
|
* - ConfigService
|
|
22
|
-
* - CheckInput
|
|
23
|
-
* - CheckResponse
|
|
24
21
|
*
|
|
25
22
|
* and sets the following services as globals:
|
|
26
23
|
* - ConfigService
|
|
@@ -99,21 +96,6 @@ export class CoreModule {
|
|
|
99
96
|
useValue: new ConfigService(config),
|
|
100
97
|
},
|
|
101
98
|
|
|
102
|
-
// [Global] The CheckResponseInterceptor restricts the response to the properties
|
|
103
|
-
// that are permitted for the current user
|
|
104
|
-
{
|
|
105
|
-
provide: APP_INTERCEPTOR,
|
|
106
|
-
useClass: CheckResponseInterceptor,
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
// [Global] The CheckInputPipe checks the permissibility of individual properties of inputs for the resolvers
|
|
110
|
-
// in relation to the current user, replaces MapAndValidatePipe
|
|
111
|
-
// Does not work yet, because context is missing: https://github.com/nestjs/graphql/issues/325
|
|
112
|
-
// {
|
|
113
|
-
// provide: APP_PIPE,
|
|
114
|
-
// useClass: CheckInputPipe,
|
|
115
|
-
// },
|
|
116
|
-
|
|
117
99
|
// [Global] Map plain objects to metatype and validate
|
|
118
100
|
{
|
|
119
101
|
provide: APP_PIPE,
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './core/common/decorators/restricted.decorator';
|
|
|
15
15
|
export * from './core/common/decorators/roles.decorator';
|
|
16
16
|
export * from './core/common/enums/comparison-operator.enum';
|
|
17
17
|
export * from './core/common/enums/logical-operator.enum';
|
|
18
|
+
export * from './core/common/enums/process-type.enum';
|
|
18
19
|
export * from './core/common/enums/role.enum';
|
|
19
20
|
export * from './core/common/enums/sort-order.emum';
|
|
20
21
|
export * from './core/common/helpers/config.helper';
|
|
@@ -34,6 +35,8 @@ export * from './core/common/inputs/sort.input';
|
|
|
34
35
|
export * from './core/common/interceptors/check-response.interceptor';
|
|
35
36
|
export * from './core/common/interfaces/core-persistence-model.interface';
|
|
36
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';
|
|
37
40
|
export * from './core/common/interfaces/resolve-selector.interface';
|
|
38
41
|
export * from './core/common/interfaces/server-options.interface';
|
|
39
42
|
export * from './core/common/interfaces/service-options.interface';
|
|
@@ -54,6 +57,8 @@ export * from './core/common/types/core-model-constructor.type';
|
|
|
54
57
|
export * from './core/common/types/field-selection.type';
|
|
55
58
|
export * from './core/common/types/ids.type';
|
|
56
59
|
export * from './core/common/types/plain-input.type';
|
|
60
|
+
export * from './core/common/types/require-only-one.type';
|
|
61
|
+
export * from './core/common/types/required-at-least-one.type';
|
|
57
62
|
export * from './core/common/types/string-or-object-id.type';
|
|
58
63
|
|
|
59
64
|
// =====================================================================================================================
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { JwtService } from '@nestjs/jwt';
|
|
3
|
+
import * as bcrypt from 'bcrypt';
|
|
4
|
+
import envConfig from '../../../config.env';
|
|
5
|
+
import { prepareServiceOptions } from '../../../core/common/helpers/service.helper';
|
|
6
|
+
import { ResolveSelector } from '../../../core/common/interfaces/resolve-selector.interface';
|
|
7
|
+
import { ServiceOptions } from '../../../core/common/interfaces/service-options.interface';
|
|
8
|
+
import { EmailService } from '../../../core/common/services/email.service';
|
|
9
|
+
import { JwtPayload } from '../../../core/modules/auth/interfaces/jwt-payload.interface';
|
|
10
|
+
import { UserService } from '../user/user.service';
|
|
11
|
+
import { Auth } from './auth.model';
|
|
12
|
+
import { AuthSignInInput } from './inputs/auth-sign-in.input';
|
|
13
|
+
import { AuthSignUpInput } from './inputs/auth-sign-up.input';
|
|
14
|
+
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class AuthService {
|
|
17
|
+
constructor(
|
|
18
|
+
protected readonly jwtService: JwtService,
|
|
19
|
+
protected readonly emailService: EmailService,
|
|
20
|
+
protected readonly userService: UserService
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sign in for user
|
|
25
|
+
*/
|
|
26
|
+
async signIn(input: AuthSignInInput, serviceOptions?: ServiceOptions): Promise<Auth> {
|
|
27
|
+
// Prepare service options
|
|
28
|
+
const serviceOptionsForUserService = prepareServiceOptions(serviceOptions, {
|
|
29
|
+
// We need password, so we can't use prepare output handling and have to deactivate it
|
|
30
|
+
prepareOutput: null,
|
|
31
|
+
|
|
32
|
+
// Select user field for automatic populate handling via user service
|
|
33
|
+
subFieldSelection: 'user',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Get and check user
|
|
37
|
+
const user = await this.userService.getViaEmail(input.email, serviceOptionsForUserService);
|
|
38
|
+
if (!user) {
|
|
39
|
+
throw new UnauthorizedException();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check password
|
|
43
|
+
if (!(await bcrypt.compare(input.password, user.password))) {
|
|
44
|
+
throw new UnauthorizedException();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create JWT and return sign-in data
|
|
48
|
+
const payload: JwtPayload = { email: user.email };
|
|
49
|
+
return Auth.map({
|
|
50
|
+
token: this.jwtService.sign(payload),
|
|
51
|
+
user,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Register a new user Account
|
|
57
|
+
*/
|
|
58
|
+
async signUp(input: AuthSignUpInput, serviceOptions?: ServiceOptions): Promise<Auth> {
|
|
59
|
+
// Prepare service options
|
|
60
|
+
const serviceOptionsForUserService = prepareServiceOptions(serviceOptions, {
|
|
61
|
+
// Select user field for automatic populate handling via user service
|
|
62
|
+
subFieldSelection: 'user',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Get and check user
|
|
66
|
+
const user = await this.userService.create(input, serviceOptionsForUserService);
|
|
67
|
+
if (!user) {
|
|
68
|
+
throw Error('Email Address already in use');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Send email
|
|
72
|
+
await this.emailService.sendMail(user.email, 'Welcome', {
|
|
73
|
+
htmlTemplate: 'welcome',
|
|
74
|
+
templateData: { name: user.username, link: envConfig.email.verificationLink + '/' + user.verificationToken },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Create JWT and return sign-in data
|
|
78
|
+
const payload: JwtPayload = { email: user.email };
|
|
79
|
+
return Auth.map({
|
|
80
|
+
token: this.jwtService.sign(payload),
|
|
81
|
+
user: user,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InputType } from '@nestjs/graphql';
|
|
2
|
+
import { CoreAuthSignInInput } from '../../../../core/modules/auth/inputs/core-auth-sign-in.input';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SignIn input
|
|
6
|
+
*/
|
|
7
|
+
@InputType({ description: 'Description for AuthSignInInput' })
|
|
8
|
+
export class AuthSignInInput extends CoreAuthSignInInput {
|
|
9
|
+
// Extend UserInput here
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Field, InputType } from '@nestjs/graphql';
|
|
2
|
+
import { CoreAuthSignUpInput } from '../../../../core/modules/auth/inputs/core-auth-sign-up.input';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SignUp input
|
|
6
|
+
*/
|
|
7
|
+
@InputType({ description: 'Description for AuthSignUpInput' })
|
|
8
|
+
export class AuthSignUpInput extends CoreAuthSignUpInput {
|
|
9
|
+
// ===================================================================================================================
|
|
10
|
+
// Properties
|
|
11
|
+
// ===================================================================================================================
|
|
12
|
+
|
|
13
|
+
@Field({ description: 'firstName', nullable: true })
|
|
14
|
+
firstName: string = undefined;
|
|
15
|
+
|
|
16
|
+
@Field({ description: 'lastName', nullable: true })
|
|
17
|
+
lastName: string = undefined;
|
|
18
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Field, ObjectType } from '@nestjs/graphql';
|
|
2
|
+
import { Prop, Schema as MongooseSchema, SchemaFactory } from '@nestjs/mongoose';
|
|
3
|
+
import { Document, Schema } from 'mongoose';
|
|
2
4
|
import { CoreUserModel } from '../../../core/modules/user/core-user.model';
|
|
3
5
|
import { PersistenceModel } from '../../common/models/persistence.model';
|
|
4
|
-
import { Prop, Schema as MongooseSchema, SchemaFactory } from '@nestjs/mongoose';
|
|
5
|
-
import { Schema, Document } from 'mongoose';
|
|
6
6
|
|
|
7
7
|
export type UserDocument = User & Document;
|
|
8
8
|
|