@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.
Files changed (99) hide show
  1. package/dist/core/common/decorators/restricted.decorator.d.ts +12 -5
  2. package/dist/core/common/decorators/restricted.decorator.js +91 -29
  3. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  4. package/dist/core/common/enums/process-type.enum.d.ts +4 -0
  5. package/dist/core/common/enums/process-type.enum.js +9 -0
  6. package/dist/core/common/enums/process-type.enum.js.map +1 -0
  7. package/dist/core/common/enums/role.enum.d.ts +1 -2
  8. package/dist/core/common/enums/role.enum.js +0 -1
  9. package/dist/core/common/enums/role.enum.js.map +1 -1
  10. package/dist/core/common/helpers/db.helper.d.ts +4 -4
  11. package/dist/core/common/helpers/db.helper.js +54 -30
  12. package/dist/core/common/helpers/db.helper.js.map +1 -1
  13. package/dist/core/common/helpers/input.helper.d.ts +6 -5
  14. package/dist/core/common/helpers/input.helper.js +4 -15
  15. package/dist/core/common/helpers/input.helper.js.map +1 -1
  16. package/dist/core/common/helpers/service.helper.d.ts +12 -0
  17. package/dist/core/common/helpers/service.helper.js +42 -3
  18. package/dist/core/common/helpers/service.helper.js.map +1 -1
  19. package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +8 -0
  20. package/dist/core/common/interfaces/prepare-input-options.interface.js +3 -0
  21. package/dist/core/common/interfaces/prepare-input-options.interface.js.map +1 -0
  22. package/dist/core/common/interfaces/prepare-output-options.interface.d.ts +7 -0
  23. package/dist/core/common/interfaces/prepare-output-options.interface.js +3 -0
  24. package/dist/core/common/interfaces/prepare-output-options.interface.js.map +1 -0
  25. package/dist/core/common/interfaces/service-options.interface.d.ts +5 -17
  26. package/dist/core/common/models/core-persistence.model.d.ts +0 -1
  27. package/dist/core/common/models/core-persistence.model.js +0 -10
  28. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  29. package/dist/core/common/services/module.service.d.ts +4 -4
  30. package/dist/core/common/services/module.service.js +27 -26
  31. package/dist/core/common/services/module.service.js.map +1 -1
  32. package/dist/core/common/types/require-only-one.type.d.ts +3 -0
  33. package/dist/core/common/types/require-only-one.type.js +3 -0
  34. package/dist/core/common/types/require-only-one.type.js.map +1 -0
  35. package/dist/core/common/types/required-at-least-one.type.d.ts +3 -0
  36. package/dist/core/common/types/required-at-least-one.type.js +3 -0
  37. package/dist/core/common/types/required-at-least-one.type.js.map +1 -0
  38. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +5 -0
  39. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +34 -0
  40. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -0
  41. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +5 -0
  42. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +34 -0
  43. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -0
  44. package/dist/core.module.js +0 -5
  45. package/dist/core.module.js.map +1 -1
  46. package/dist/index.d.ts +5 -0
  47. package/dist/index.js +5 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/server/modules/auth/auth.model.js +2 -2
  50. package/dist/server/modules/auth/auth.model.js.map +1 -1
  51. package/dist/server/modules/auth/auth.module.js +7 -2
  52. package/dist/server/modules/auth/auth.module.js.map +1 -1
  53. package/dist/server/modules/auth/auth.resolver.d.ts +8 -3
  54. package/dist/server/modules/auth/auth.resolver.js +33 -10
  55. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  56. package/dist/server/modules/auth/auth.service.d.ts +15 -0
  57. package/dist/server/modules/auth/auth.service.js +71 -0
  58. package/dist/server/modules/auth/auth.service.js.map +1 -0
  59. package/dist/server/modules/auth/inputs/auth-sign-in.input.d.ts +3 -0
  60. package/dist/server/modules/auth/inputs/auth-sign-in.input.js +18 -0
  61. package/dist/server/modules/auth/inputs/auth-sign-in.input.js.map +1 -0
  62. package/dist/server/modules/auth/inputs/auth-sign-up.input.d.ts +5 -0
  63. package/dist/server/modules/auth/inputs/auth-sign-up.input.js +34 -0
  64. package/dist/server/modules/auth/inputs/auth-sign-up.input.js.map +1 -0
  65. package/dist/server/modules/user/user.model.d.ts +1 -1
  66. package/dist/server/modules/user/user.model.js +1 -1
  67. package/dist/server/modules/user/user.model.js.map +1 -1
  68. package/dist/server/modules/user/user.resolver.js +12 -11
  69. package/dist/server/modules/user/user.resolver.js.map +1 -1
  70. package/dist/server/modules/user/user.service.js +1 -5
  71. package/dist/server/modules/user/user.service.js.map +1 -1
  72. package/dist/tsconfig.build.tsbuildinfo +1 -1
  73. package/package.json +4 -4
  74. package/src/core/common/decorators/restricted.decorator.ts +150 -53
  75. package/src/core/common/enums/process-type.enum.ts +7 -0
  76. package/src/core/common/enums/role.enum.ts +1 -4
  77. package/src/core/common/helpers/db.helper.ts +70 -56
  78. package/src/core/common/helpers/input.helper.ts +24 -15
  79. package/src/core/common/helpers/service.helper.ts +72 -2
  80. package/src/core/common/interfaces/prepare-input-options.interface.ts +11 -0
  81. package/src/core/common/interfaces/prepare-output-options.interface.ts +10 -0
  82. package/src/core/common/interfaces/service-options.interface.ts +8 -22
  83. package/src/core/common/models/core-persistence.model.ts +0 -11
  84. package/src/core/common/services/module.service.ts +32 -31
  85. package/src/core/common/types/require-only-one.type.ts +6 -0
  86. package/src/core/common/types/required-at-least-one.type.ts +6 -0
  87. package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +18 -0
  88. package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +18 -0
  89. package/src/core.module.ts +1 -19
  90. package/src/index.ts +5 -0
  91. package/src/server/modules/auth/auth.model.ts +5 -5
  92. package/src/server/modules/auth/auth.module.ts +13 -2
  93. package/src/server/modules/auth/auth.resolver.ts +30 -12
  94. package/src/server/modules/auth/auth.service.ts +84 -0
  95. package/src/server/modules/auth/inputs/auth-sign-in.input.ts +10 -0
  96. package/src/server/modules/auth/inputs/auth-sign-up.input.ts +18 -0
  97. package/src/server/modules/user/user.model.ts +2 -2
  98. package/src/server/modules/user/user.resolver.ts +12 -11
  99. package/src/server/modules/user/user.service.ts +3 -11
@@ -1,6 +1,7 @@
1
- import { Model, Types } from 'mongoose';
1
+ import { Model } from 'mongoose';
2
2
  import { FieldSelection } from '../types/field-selection.type';
3
- import { IdsType } from '../types/ids.type';
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 roles and other things
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
- // Owner IDs
31
- ownerIds?: IdsType;
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
- creator?: IdsType;
42
+ dbObject?: any;
43
43
  metatype?: any;
44
- ownerIds?: IdsType;
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
- await this.prepareInput(config.input, config.prepareInput);
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
- const getDbObject = async () => {
92
- if (config.dbObject) {
93
- if (typeof config.dbObject === 'string' || config.dbObject instanceof Types.ObjectId) {
94
- const dbObject = await this.get(getStringIds(config.dbObject));
95
- if (dbObject) {
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 = { creator: (await getDbObject())?.createdBy, ownerIds, roles: config.roles };
106
+ const opts: any = { dbObject: config.dbObject, processType: ProcessType.INPUT, roles: config.roles };
115
107
  if (config.inputType) {
116
- opts.metatype = config.resultType;
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
- // Check if mapping is already done by processFieldSelection
132
- if (config.processFieldSelection && config.fieldSelection && this.processFieldSelection) {
133
- config.prepareOutput.targetModel = null;
123
+ const opts = config.prepareOutput;
124
+ if (!opts.targetModel && config.outputType) {
125
+ opts.targetModel = config.outputType;
134
126
  }
135
- result = await this.prepareOutput(result, config.prepareOutput);
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 = { creator: (await getDbObject())?.createdBy, ownerIds, roles: config.roles, throwError: false };
141
- if (config.resultType) {
142
- opts.metatype = config.resultType;
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
- return prepareInput(input, options.currentUser, options.prepareInput);
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
+ }
@@ -1,8 +1,7 @@
1
1
  import { DynamicModule, Global, Module, UnauthorizedException } from '@nestjs/common';
2
- import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
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
- * CoreAuthModel model for the response after the sign in
6
+ * Authentication data
7
7
  */
8
- @ObjectType({ description: 'Auth' })
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 in user
15
+ * Signed-in user
16
16
  */
17
- @Field((type) => User, { description: 'User who signed in' })
17
+ @Field(() => User, { description: 'User who signed in' })
18
18
  user: User = undefined;
19
19
 
20
20
  // ===================================================================================================================
21
- // Properties
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: [CoreAuthModule.forRoot(UserModule, UserService, options)],
21
- providers: [AuthResolver],
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((of) => Auth)
10
- export class AuthResolver extends CoreAuthResolver {
11
+ @Resolver(() => Auth)
12
+ export class AuthResolver {
11
13
  /**
12
- * Get user via ID
14
+ * Integrate services
13
15
  */
14
- @Query((returns) => Auth, { description: 'Get JWT token' })
15
- async signIn(
16
- @Args('email') email: string,
17
- @Args('password') password: string,
18
- @Info() info: GraphQLResolveInfo
19
- ): Promise<Auth> {
20
- return (await this.authService.signIn(email, password, { fieldSelection: { info, select: 'signIn' } })) as Auth;
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