@lenne.tech/nest-server 8.6.4 → 8.6.7

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 (81) hide show
  1. package/dist/config.env.js +4 -2
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/decorators/restricted.decorator.js +4 -4
  4. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  5. package/dist/core/common/helpers/db.helper.js +3 -2
  6. package/dist/core/common/helpers/db.helper.js.map +1 -1
  7. package/dist/core/common/helpers/file.helper.js +4 -7
  8. package/dist/core/common/helpers/file.helper.js.map +1 -1
  9. package/dist/core/common/helpers/filter.helper.d.ts +7 -0
  10. package/dist/core/common/helpers/filter.helper.js +38 -1
  11. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  12. package/dist/core/common/helpers/input.helper.d.ts +6 -0
  13. package/dist/core/common/helpers/input.helper.js +28 -6
  14. package/dist/core/common/helpers/input.helper.js.map +1 -1
  15. package/dist/core/common/helpers/service.helper.d.ts +2 -0
  16. package/dist/core/common/helpers/service.helper.js +7 -9
  17. package/dist/core/common/helpers/service.helper.js.map +1 -1
  18. package/dist/core/common/inputs/filter.input.js.map +1 -1
  19. package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +1 -0
  20. package/dist/core/common/interfaces/prepare-output-options.interface.d.ts +1 -0
  21. package/dist/core/common/interfaces/server-options.interface.d.ts +16 -13
  22. package/dist/core/common/interfaces/service-options.interface.d.ts +3 -1
  23. package/dist/core/common/models/core-model.model.js +4 -1
  24. package/dist/core/common/models/core-model.model.js.map +1 -1
  25. package/dist/core/common/services/crud.service.js +9 -4
  26. package/dist/core/common/services/crud.service.js.map +1 -1
  27. package/dist/core/common/services/module.service.js +14 -2
  28. package/dist/core/common/services/module.service.js.map +1 -1
  29. package/dist/core/common/types/field-selection.type.d.ts +1 -1
  30. package/dist/core/modules/auth/guards/roles.guard.js +2 -1
  31. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  32. package/dist/core/modules/user/core-user.service.js +9 -13
  33. package/dist/core/modules/user/core-user.service.js.map +1 -1
  34. package/dist/core.module.d.ts +3 -2
  35. package/dist/core.module.js +26 -4
  36. package/dist/core.module.js.map +1 -1
  37. package/dist/main.js +13 -0
  38. package/dist/main.js.map +1 -1
  39. package/dist/server/modules/auth/auth.module.js +1 -2
  40. package/dist/server/modules/auth/auth.module.js.map +1 -1
  41. package/dist/server/modules/file/file.controller.js +1 -1
  42. package/dist/server/modules/file/file.controller.js.map +1 -1
  43. package/dist/server/modules/file/file.resolver.d.ts +5 -0
  44. package/dist/server/modules/file/file.resolver.js +51 -0
  45. package/dist/server/modules/file/file.resolver.js.map +1 -0
  46. package/dist/server/modules/user/user.model.d.ts +14 -1
  47. package/dist/server/modules/user/user.resolver.js +2 -1
  48. package/dist/server/modules/user/user.resolver.js.map +1 -1
  49. package/dist/server/server.module.js +3 -1
  50. package/dist/server/server.module.js.map +1 -1
  51. package/dist/test/test.helper.d.ts +4 -1
  52. package/dist/test/test.helper.js +51 -25
  53. package/dist/test/test.helper.js.map +1 -1
  54. package/dist/tsconfig.build.tsbuildinfo +1 -1
  55. package/package.json +43 -37
  56. package/src/config.env.ts +4 -2
  57. package/src/core/common/decorators/restricted.decorator.ts +1 -2
  58. package/src/core/common/helpers/db.helper.ts +7 -2
  59. package/src/core/common/helpers/file.helper.ts +9 -11
  60. package/src/core/common/helpers/filter.helper.ts +66 -1
  61. package/src/core/common/helpers/input.helper.ts +61 -2
  62. package/src/core/common/helpers/service.helper.ts +8 -9
  63. package/src/core/common/inputs/filter.input.ts +0 -1
  64. package/src/core/common/interfaces/prepare-input-options.interface.ts +1 -0
  65. package/src/core/common/interfaces/prepare-output-options.interface.ts +1 -0
  66. package/src/core/common/interfaces/server-options.interface.ts +68 -52
  67. package/src/core/common/interfaces/service-options.interface.ts +8 -2
  68. package/src/core/common/models/core-model.model.ts +4 -1
  69. package/src/core/common/services/crud.service.ts +7 -4
  70. package/src/core/common/services/module.service.ts +17 -1
  71. package/src/core/common/types/field-selection.type.ts +1 -1
  72. package/src/core/modules/auth/guards/roles.guard.ts +1 -1
  73. package/src/core/modules/user/core-user.service.ts +13 -22
  74. package/src/core.module.ts +38 -6
  75. package/src/main.ts +16 -0
  76. package/src/server/modules/auth/auth.module.ts +1 -2
  77. package/src/server/modules/file/file.controller.ts +1 -1
  78. package/src/server/modules/file/file.resolver.ts +55 -0
  79. package/src/server/modules/user/user.resolver.ts +1 -1
  80. package/src/server/server.module.ts +5 -1
  81. package/src/test/test.helper.ts +92 -34
@@ -1,4 +1,5 @@
1
1
  import { ApolloDriverConfig } from '@nestjs/apollo';
2
+ import { GqlModuleAsyncOptions } from '@nestjs/graphql';
2
3
  import { JwtModuleOptions } from '@nestjs/jwt';
3
4
  import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
4
5
  import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
@@ -9,12 +10,58 @@ import { MailjetOptions } from './mailjet-options.interface';
9
10
  * Options for the server
10
11
  */
11
12
  export interface IServerOptions {
13
+ /**
14
+ * SMTP and template configuration for sending emails
15
+ */
16
+ email?: {
17
+ /**
18
+ * Data for default sender
19
+ */
20
+ defaultSender?: {
21
+ /**
22
+ * Default email for sending emails
23
+ */
24
+ email?: string;
25
+
26
+ /**
27
+ * Default name for sending emails
28
+ */
29
+ name?: string;
30
+ };
31
+
32
+ /**
33
+ * Options for Mailjet
34
+ */
35
+ mailjet?: MailjetOptions;
36
+
37
+ /**
38
+ * Password reset link for email
39
+ */
40
+ passwordResetLink?: string;
41
+
42
+ /**
43
+ * SMTP configuration for nodemailer
44
+ */
45
+ smtp?: SMTPTransport | SMTPTransport.Options | string;
46
+
47
+ /**
48
+ * Verification link for email
49
+ */
50
+ verificationLink?: string;
51
+ };
52
+
12
53
  /**
13
54
  * Environment
14
55
  * e.g. 'development'
15
56
  */
16
57
  env?: string;
17
58
 
59
+ /**
60
+ * Exec a command after server is initialized
61
+ * e.g. 'npm run docs:bootstrap'
62
+ */
63
+ execAfterInit?: string;
64
+
18
65
  /**
19
66
  * Configuration of the GraphQL module
20
67
  * see https://docs.nestjs.com/graphql/quick-start
@@ -30,6 +77,11 @@ export interface IServerOptions {
30
77
  * Subscription authentication
31
78
  */
32
79
  enableSubscriptionAuth?: boolean;
80
+
81
+ /**
82
+ * Module options (forRootAsync)
83
+ */
84
+ options?: GqlModuleAsyncOptions;
33
85
  };
34
86
 
35
87
  /**
@@ -67,6 +119,11 @@ export interface IServerOptions {
67
119
  secretOrPrivateKey?: string;
68
120
  } & JwtModuleOptions;
69
121
 
122
+ /**
123
+ * Configuration for Mongoose
124
+ */
125
+ mongoose?: { uri: string; options?: MongooseModuleOptions };
126
+
70
127
  /**
71
128
  * Port number of the server
72
129
  * e.g. 8080
@@ -74,77 +131,36 @@ export interface IServerOptions {
74
131
  port?: number;
75
132
 
76
133
  /**
77
- * SMTP and template configuration for sending emails
134
+ * Configuration for useStaticAssets
78
135
  */
79
- email?: {
80
- /**
81
- * SMTP configuration for nodemailer
82
- */
83
- smtp?: SMTPTransport | SMTPTransport.Options | string;
84
-
85
- mailjet?: MailjetOptions;
86
- /**
87
- * Verification link for email
88
- */
89
- verificationLink?: string;
90
-
91
- /**
92
- * Password reset link for email
93
- */
94
- passwordResetLink?: string;
95
-
136
+ staticAssets?: {
96
137
  /**
97
- * Data for default sender
138
+ * Additional options for useStaticAssets
139
+ * e.g. {prefix: '/public/'}
98
140
  */
99
- defaultSender?: {
100
- /**
101
- * Default email for sending emails
102
- */
103
- email?: string;
104
-
105
- /**
106
- * Default name for sending emails
107
- */
108
- name?: string;
109
- };
110
- };
111
-
112
- /**
113
- * Configuration for Mongoose
114
- */
115
- mongoose?: { uri: string; options?: MongooseModuleOptions };
141
+ options?: ServeStaticOptions;
116
142
 
117
- /**
118
- * Configuration for useStaticAssets
119
- */
120
- staticAssets?: {
121
143
  /**
122
144
  * Root directory for static assets
123
145
  * e.g. join(__dirname, '..', 'public')
124
146
  */
125
147
  path?: string;
126
-
127
- /**
128
- * Additional options for useStaticAssets
129
- * e.g. {prefix: '/public/'}
130
- */
131
- options?: ServeStaticOptions;
132
148
  };
133
149
 
134
150
  /**
135
151
  * Templates
136
152
  */
137
153
  templates?: {
138
- /**
139
- * Directory for templates
140
- * e.g. join(__dirname, '..', 'templates')
141
- */
142
- path?: string;
143
-
144
154
  /**
145
155
  * View engine
146
156
  * e.g. 'ejs'
147
157
  */
148
158
  engine?: string;
159
+
160
+ /**
161
+ * Directory for templates
162
+ * e.g. join(__dirname, '..', 'templates')
163
+ */
164
+ path?: string;
149
165
  };
150
166
  }
@@ -1,4 +1,4 @@
1
- import { Model } from 'mongoose';
1
+ import { Model, PopulateOptions } from 'mongoose';
2
2
  import { FieldSelection } from '../types/field-selection.type';
3
3
  import { PrepareInputOptions } from './prepare-input-options.interface';
4
4
  import { PrepareOutputOptions } from './prepare-output-options.interface';
@@ -22,15 +22,21 @@ export interface ServiceOptions {
22
22
  roles?: string[];
23
23
  };
24
24
 
25
- // Field selection for results
25
+ // Field selection for results (will be overwritten by populate if populate was set)
26
26
  fieldSelection?: FieldSelection;
27
27
 
28
+ // Determines whether all restrictions are ignored
29
+ force?: boolean;
30
+
28
31
  // Overwrites type of input (array items)
29
32
  inputType?: new (...params: any[]) => any;
30
33
 
31
34
  // Overwrites type of output (array items)
32
35
  outputType?: new (...params: any[]) => any;
33
36
 
37
+ // Alias for fieldSelection (if both are set fieldSelection is overwritten by populate)
38
+ populate?: PopulateOptions | (PopulateOptions | string)[];
39
+
34
40
  // Process field selection
35
41
  // If {} or not set, then the field selection runs with defaults
36
42
  // If falsy, then the field selection will not be automatically executed
@@ -92,7 +92,10 @@ export abstract class CoreModel {
92
92
  mapId: false,
93
93
  ...options,
94
94
  };
95
- return config.init ? map(data, this, config).init(config.init) : map(data, this, config);
95
+ if (config.init) {
96
+ this.init(config.init);
97
+ }
98
+ return map(data, this, config);
96
99
  }
97
100
 
98
101
  /**
@@ -2,6 +2,7 @@ import { NotFoundException } from '@nestjs/common';
2
2
  import { FilterArgs } from '../args/filter.args';
3
3
  import { merge } from '../helpers/config.helper';
4
4
  import { convertFilterArgsToQuery } from '../helpers/filter.helper';
5
+ import { assignPlain } from '../helpers/input.helper';
5
6
  import { ServiceOptions } from '../interfaces/service-options.interface';
6
7
  import { CoreModel } from '../models/core-model.model';
7
8
  import { ModuleService } from './module.service';
@@ -14,7 +15,8 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
14
15
  merge({ prepareInput: { create: true } }, serviceOptions);
15
16
  return this.process(
16
17
  async (data) => {
17
- return new this.mainDbModel({ ...data.input }).save();
18
+ const currentUserId = serviceOptions?.currentUser?.id;
19
+ return new this.mainDbModel({ ...data.input, createdBy: currentUserId, updatedBy: currentUserId }).save();
18
20
  },
19
21
  { input, serviceOptions }
20
22
  );
@@ -75,7 +77,8 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
75
77
  }
76
78
  return this.process(
77
79
  async (data) => {
78
- return await Object.assign(dbObject, data.input).save();
80
+ const currentUserId = serviceOptions?.currentUser?.id;
81
+ return await assignPlain(dbObject, data.input, { updatedBy: currentUserId }).save();
79
82
  },
80
83
  { dbObject, input, serviceOptions }
81
84
  );
@@ -90,11 +93,11 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
90
93
  throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
91
94
  }
92
95
  return this.process(
93
- async (data) => {
96
+ async () => {
94
97
  await this.mainDbModel.findByIdAndDelete(id).exec();
95
98
  return dbObject;
96
99
  },
97
- { dbObject, input: id, serviceOptions }
100
+ { dbObject, serviceOptions }
98
101
  );
99
102
  }
100
103
  }
@@ -74,6 +74,7 @@ export abstract class ModuleService<T extends CoreModel = any> {
74
74
  const config = {
75
75
  checkRights: true,
76
76
  dbObject: options?.dbObject,
77
+ force: false,
77
78
  input: options?.input,
78
79
  processFieldSelection: {},
79
80
  prepareInput: {},
@@ -82,6 +83,22 @@ export abstract class ModuleService<T extends CoreModel = any> {
82
83
  ...options?.serviceOptions,
83
84
  };
84
85
 
86
+ // Note force configuration
87
+ if (config.force) {
88
+ config.checkRights = false;
89
+ if (typeof config.prepareInput === 'object') {
90
+ config.prepareInput.checkRoles = false;
91
+ }
92
+ if (typeof config.prepareOutput === 'object') {
93
+ config.prepareOutput.removeSecrets = false;
94
+ }
95
+ }
96
+
97
+ // Note populate
98
+ if (config.populate) {
99
+ config.fieldSelection = config.populate;
100
+ }
101
+
85
102
  // Prepare input
86
103
  if (config.prepareInput && this.prepareInput) {
87
104
  const opts = config.prepareInput;
@@ -150,7 +167,6 @@ export abstract class ModuleService<T extends CoreModel = any> {
150
167
  */
151
168
  async prepareInput(input: Record<string, any>, options: ServiceOptions = {}) {
152
169
  const config = {
153
- targetModel: this.mainModelConstructor,
154
170
  ...options?.prepareInput,
155
171
  };
156
172
  return prepareInput(input, options.currentUser, config);
@@ -5,4 +5,4 @@ import { ResolveSelector } from '../interfaces/resolve-selector.interface';
5
5
  /**
6
6
  * Field selection to set fields of (populated) result
7
7
  */
8
- export type FieldSelection = PopulateOptions[] | SelectionNode[] | ResolveSelector;
8
+ export type FieldSelection = PopulateOptions | (PopulateOptions | string)[] | SelectionNode[] | ResolveSelector;
@@ -38,7 +38,7 @@ export class RolesGuard extends AuthGuard('jwt') {
38
38
  }
39
39
 
40
40
  // Check user and user roles
41
- if (!user || !user.hasRole(roles)) {
41
+ if (!user?.hasRole?.(roles)) {
42
42
  // Get args
43
43
  const args: any = GqlExecutionContext.create(context).getArgs();
44
44
 
@@ -3,6 +3,7 @@ import * as bcrypt from 'bcrypt';
3
3
  import * as crypto from 'crypto';
4
4
  import { Document, Model } from 'mongoose';
5
5
  import { merge } from '../../common/helpers/config.helper';
6
+ import { assignPlain } from '../../common/helpers/input.helper';
6
7
  import { ServiceOptions } from '../../common/interfaces/service-options.interface';
7
8
  import { CrudService } from '../../common/services/crud.service';
8
9
  import { EmailService } from '../../common/services/email.service';
@@ -39,9 +40,12 @@ export abstract class CoreUserService<
39
40
  return this.process(
40
41
  async (data) => {
41
42
  // Create user with verification token
43
+ const currentUserId = serviceOptions?.currentUser?._id;
42
44
  const createdUser = new this.mainDbModel({
43
45
  ...data.input,
44
46
  verificationToken: crypto.randomBytes(32).toString('hex'),
47
+ createdBy: currentUserId,
48
+ updatedBy: currentUserId,
45
49
  });
46
50
 
47
51
  // Distinguish between different error messages when saving
@@ -105,21 +109,15 @@ export abstract class CoreUserService<
105
109
  }
106
110
  return this.process(
107
111
  async () => {
108
- // Update user
109
- await Object.assign(dbObject, {
110
- verified: true,
111
- verificationToken: null,
112
- }).save();
113
-
114
- // Return prepared user
115
- return dbObject;
112
+ // Update and return user
113
+ return await assignPlain(dbObject, { verified: true, verificationToken: null }).save();
116
114
  },
117
115
  { dbObject, serviceOptions }
118
116
  );
119
117
  }
120
118
 
121
119
  /**
122
- * Set newpassword for user with token
120
+ * Set new password for user with token
123
121
  */
124
122
  async resetPassword(token: string, newPassword: string, serviceOptions?: ServiceOptions): Promise<TUser> {
125
123
  // Get user
@@ -130,14 +128,11 @@ export abstract class CoreUserService<
130
128
 
131
129
  return this.process(
132
130
  async () => {
133
- // Update user
134
- await Object.assign(dbObject, {
131
+ // Update and return user
132
+ return await assignPlain(dbObject, {
135
133
  password: await bcrypt.hash(newPassword, 10),
136
134
  passwordResetToken: null,
137
135
  }).save();
138
-
139
- // Return user
140
- return dbObject;
141
136
  },
142
137
  { dbObject, serviceOptions }
143
138
  );
@@ -154,13 +149,9 @@ export abstract class CoreUserService<
154
149
  }
155
150
  return this.process(
156
151
  async () => {
157
- // Set reset token
158
- const resetToken = crypto.randomBytes(32).toString('hex');
159
- dbObject.passwordResetToken = resetToken;
160
- await dbObject.save();
161
-
162
- // Return user
163
- return dbObject;
152
+ // Set reset token and return
153
+ dbObject.passwordResetToken = crypto.randomBytes(32).toString('hex');
154
+ return await dbObject.save();
164
155
  },
165
156
  { dbObject, serviceOptions }
166
157
  );
@@ -182,7 +173,7 @@ export abstract class CoreUserService<
182
173
 
183
174
  // Update and return user
184
175
  return this.process(
185
- async (data) => {
176
+ async () => {
186
177
  return await this.mainDbModel.findByIdAndUpdate(userId, { roles }).exec();
187
178
  },
188
179
  { serviceOptions }
@@ -1,15 +1,17 @@
1
- import { DynamicModule, Global, Module, UnauthorizedException } from '@nestjs/common';
1
+ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
2
+ import { DynamicModule, Global, MiddlewareConsumer, Module, NestModule, UnauthorizedException } from '@nestjs/common';
2
3
  import { APP_PIPE } from '@nestjs/core';
3
4
  import { GraphQLModule } from '@nestjs/graphql';
5
+ import { MongooseModule } from '@nestjs/mongoose';
6
+ import { Context } from 'apollo-server-core';
7
+ import { graphqlUploadExpress } from 'graphql-upload';
4
8
  import { merge } from './core/common/helpers/config.helper';
5
9
  import { IServerOptions } from './core/common/interfaces/server-options.interface';
6
10
  import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
7
11
  import { ConfigService } from './core/common/services/config.service';
8
12
  import { EmailService } from './core/common/services/email.service';
9
- import { TemplateService } from './core/common/services/template.service';
10
- import { MongooseModule } from '@nestjs/mongoose';
11
13
  import { MailjetService } from './core/common/services/mailjet.service';
12
- import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
14
+ import { TemplateService } from './core/common/services/template.service';
13
15
 
14
16
  /**
15
17
  * Core module (dynamic)
@@ -26,7 +28,13 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
26
28
  */
27
29
  @Global()
28
30
  @Module({})
29
- export class CoreModule {
31
+ export class CoreModule implements NestModule {
32
+ /**
33
+ * Integrate middleware, e.g. GraphQL upload handing for express
34
+ */
35
+ configure(consumer: MiddlewareConsumer) {
36
+ consumer.apply(graphqlUploadExpress()).forRoutes('graphql');
37
+ }
30
38
  /**
31
39
  * Dynamic module
32
40
  * see https://docs.nestjs.com/modules#dynamic-modules
@@ -66,6 +74,28 @@ export class CoreModule {
66
74
  }
67
75
  },
68
76
  },
77
+ 'graphql-ws': {
78
+ onConnect: async (context: Context<any>) => {
79
+ const { connectionParams, extra } = context;
80
+ if (config.graphQl.enableSubscriptionAuth) {
81
+ // get authToken from authorization header
82
+ const authToken: string =
83
+ 'Authorization' in connectionParams && connectionParams?.Authorization?.split(' ')[1];
84
+ if (authToken) {
85
+ // verify authToken/getJwtPayLoad
86
+ const payload = authService.decodeJwt(authToken);
87
+ const user = await authService.validateUser(payload);
88
+ // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
89
+ extra.user = user;
90
+ extra.header = connectionParams;
91
+ return extra;
92
+ }
93
+
94
+ throw new UnauthorizedException();
95
+ }
96
+ },
97
+ context: ({ extra }) => extra,
98
+ },
69
99
  },
70
100
  },
71
101
  options?.graphQl?.driver
@@ -113,7 +143,9 @@ export class CoreModule {
113
143
  module: CoreModule,
114
144
  imports: [
115
145
  MongooseModule.forRoot(config.mongoose.uri, config.mongoose.options),
116
- GraphQLModule.forRootAsync<ApolloDriverConfig>(Object.assign({ driver: ApolloDriver }, config.graphQl.driver)),
146
+ GraphQLModule.forRootAsync<ApolloDriverConfig>(
147
+ Object.assign({ driver: ApolloDriver }, config.graphQl.driver, config.graphQl.options)
148
+ ),
117
149
  ],
118
150
  providers,
119
151
  exports: [ConfigService, EmailService, TemplateService, MailjetService],
package/src/main.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NestFactory } from '@nestjs/core';
2
2
  import { NestExpressApplication } from '@nestjs/platform-express';
3
+ import { exec } from 'child_process';
3
4
  import envConfig from './config.env';
4
5
  import { ServerModule } from './server/server.module';
5
6
 
@@ -25,6 +26,21 @@ async function bootstrap() {
25
26
 
26
27
  // Start server on configured port
27
28
  await server.listen(envConfig.port);
29
+
30
+ // Run command after server init
31
+ if (envConfig.execAfterInit) {
32
+ exec(envConfig.execAfterInit, (error, stdout, stderr) => {
33
+ if (error) {
34
+ console.error(`error: ${error.message}`);
35
+ return;
36
+ }
37
+
38
+ if (stderr) {
39
+ console.error(`stderr: ${stderr}`);
40
+ return;
41
+ }
42
+ });
43
+ }
28
44
  }
29
45
 
30
46
  // Start server
@@ -27,9 +27,8 @@ export class AuthModule {
27
27
  // providers: [] // Integrate additional Providers here to resolve dependencies
28
28
  },
29
29
  }),
30
- EmailService,
31
30
  ],
32
- providers: [AuthResolver, AuthService],
31
+ providers: [AuthResolver, AuthService, EmailService],
33
32
  exports: [AuthResolver, CoreAuthModule],
34
33
  };
35
34
  }
@@ -32,6 +32,6 @@ export class FileController {
32
32
  })
33
33
  )
34
34
  uploadFile(@UploadedFiles() files, @Body() fields: any) {
35
- console.log(files, fields);
35
+ console.log(JSON.stringify({ files, fields }, null, 2));
36
36
  }
37
37
  }
@@ -0,0 +1,55 @@
1
+ import { Args, Mutation, Resolver } from '@nestjs/graphql';
2
+ import { createWriteStream } from 'fs';
3
+ import { FileUpload, GraphQLUpload } from 'graphql-upload';
4
+
5
+ /**
6
+ * File resolver
7
+ */
8
+ @Resolver()
9
+ export class FileResolver {
10
+ /**
11
+ * Upload file
12
+ */
13
+ @Mutation(() => Boolean)
14
+ async uploadFile(
15
+ @Args({ name: 'file', type: () => GraphQLUpload })
16
+ file: FileUpload
17
+ ) {
18
+ console.log(JSON.stringify(file, null, 2));
19
+ /*
20
+ const {filename, mimetype, encoding, createReadStream} = file;
21
+ await new Promise((resolve, reject) =>
22
+ createReadStream()
23
+ .pipe(createWriteStream(`./uploads/${filename}`))
24
+ .on('finish', () => resolve(true))
25
+ .on('error', (error) => reject(error))
26
+ );
27
+ */
28
+ return true;
29
+ }
30
+
31
+ /**
32
+ * Upload files
33
+ */
34
+ @Mutation(() => Boolean)
35
+ async uploadFiles(
36
+ @Args({ name: 'files', type: () => [GraphQLUpload] })
37
+ files: FileUpload[]
38
+ ) {
39
+ const promises: Promise<any>[] = [];
40
+ for (const file of files) {
41
+ console.log(JSON.stringify(await file, null, 2));
42
+ /*
43
+ const {filename, mimetype, encoding, createReadStream} = await file
44
+ promises.push(new Promise((resolve, reject) =>
45
+ createReadStream()
46
+ .pipe(createWriteStream(`./uploads/${filename}`))
47
+ .on('finish', () => resolve(true))
48
+ .on('error', (error) => reject(error))
49
+ ));
50
+ */
51
+ }
52
+ await Promise.all(promises);
53
+ return true;
54
+ }
55
+ }
@@ -145,7 +145,7 @@ export class UserResolver {
145
145
  */
146
146
  @Subscription(() => User, {
147
147
  filter(this: UserResolver, payload, variables, context) {
148
- return context.user.roles.include(RoleEnum.ADMIN);
148
+ return context?.user?.hasRole?.(RoleEnum.ADMIN);
149
149
  },
150
150
  resolve: (user) => user,
151
151
  })
@@ -1,10 +1,11 @@
1
1
  import { Module } from '@nestjs/common';
2
2
  import envConfig from '../config.env';
3
3
  import { CoreModule } from '../core.module';
4
+ import { CoreAuthService } from '../core/modules/auth/services/core-auth.service';
4
5
  import { AuthModule } from './modules/auth/auth.module';
5
6
  import { FileController } from './modules/file/file.controller';
7
+ import { FileResolver } from './modules/file/file.resolver';
6
8
  import { ServerController } from './server.controller';
7
- import { CoreAuthService } from '../core/modules/auth/services/core-auth.service';
8
9
 
9
10
  /**
10
11
  * Server module (dynamic)
@@ -26,6 +27,9 @@ import { CoreAuthService } from '../core/modules/auth/services/core-auth.service
26
27
  // Include REST controllers
27
28
  controllers: [FileController, ServerController],
28
29
 
30
+ // Include resolvers, services and other providers
31
+ providers: [FileResolver],
32
+
29
33
  // Export modules for reuse in other modules
30
34
  exports: [CoreModule, AuthModule],
31
35
  })