@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.
- package/dist/config.env.js +4 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/decorators/restricted.decorator.js +4 -4
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/db.helper.js +3 -2
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/file.helper.js +4 -7
- package/dist/core/common/helpers/file.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +7 -0
- package/dist/core/common/helpers/filter.helper.js +38 -1
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +6 -0
- package/dist/core/common/helpers/input.helper.js +28 -6
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +2 -0
- package/dist/core/common/helpers/service.helper.js +7 -9
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +1 -0
- package/dist/core/common/interfaces/prepare-output-options.interface.d.ts +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +16 -13
- package/dist/core/common/interfaces/service-options.interface.d.ts +3 -1
- package/dist/core/common/models/core-model.model.js +4 -1
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/services/crud.service.js +9 -4
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/common/services/module.service.js +14 -2
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/common/types/field-selection.type.d.ts +1 -1
- package/dist/core/modules/auth/guards/roles.guard.js +2 -1
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/user/core-user.service.js +9 -13
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core.module.d.ts +3 -2
- package/dist/core.module.js +26 -4
- package/dist/core.module.js.map +1 -1
- package/dist/main.js +13 -0
- package/dist/main.js.map +1 -1
- package/dist/server/modules/auth/auth.module.js +1 -2
- package/dist/server/modules/auth/auth.module.js.map +1 -1
- package/dist/server/modules/file/file.controller.js +1 -1
- package/dist/server/modules/file/file.controller.js.map +1 -1
- package/dist/server/modules/file/file.resolver.d.ts +5 -0
- package/dist/server/modules/file/file.resolver.js +51 -0
- package/dist/server/modules/file/file.resolver.js.map +1 -0
- package/dist/server/modules/user/user.model.d.ts +14 -1
- package/dist/server/modules/user/user.resolver.js +2 -1
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/server.module.js +3 -1
- package/dist/server/server.module.js.map +1 -1
- package/dist/test/test.helper.d.ts +4 -1
- package/dist/test/test.helper.js +51 -25
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +43 -37
- package/src/config.env.ts +4 -2
- package/src/core/common/decorators/restricted.decorator.ts +1 -2
- package/src/core/common/helpers/db.helper.ts +7 -2
- package/src/core/common/helpers/file.helper.ts +9 -11
- package/src/core/common/helpers/filter.helper.ts +66 -1
- package/src/core/common/helpers/input.helper.ts +61 -2
- package/src/core/common/helpers/service.helper.ts +8 -9
- package/src/core/common/inputs/filter.input.ts +0 -1
- package/src/core/common/interfaces/prepare-input-options.interface.ts +1 -0
- package/src/core/common/interfaces/prepare-output-options.interface.ts +1 -0
- package/src/core/common/interfaces/server-options.interface.ts +68 -52
- package/src/core/common/interfaces/service-options.interface.ts +8 -2
- package/src/core/common/models/core-model.model.ts +4 -1
- package/src/core/common/services/crud.service.ts +7 -4
- package/src/core/common/services/module.service.ts +17 -1
- package/src/core/common/types/field-selection.type.ts +1 -1
- package/src/core/modules/auth/guards/roles.guard.ts +1 -1
- package/src/core/modules/user/core-user.service.ts +13 -22
- package/src/core.module.ts +38 -6
- package/src/main.ts +16 -0
- package/src/server/modules/auth/auth.module.ts +1 -2
- package/src/server/modules/file/file.controller.ts +1 -1
- package/src/server/modules/file/file.resolver.ts +55 -0
- package/src/server/modules/user/user.resolver.ts +1 -1
- package/src/server/server.module.ts +5 -1
- 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
|
-
*
|
|
134
|
+
* Configuration for useStaticAssets
|
|
78
135
|
*/
|
|
79
|
-
|
|
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
|
-
*
|
|
138
|
+
* Additional options for useStaticAssets
|
|
139
|
+
* e.g. {prefix: '/public/'}
|
|
98
140
|
*/
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
96
|
+
async () => {
|
|
94
97
|
await this.mainDbModel.findByIdAndDelete(id).exec();
|
|
95
98
|
return dbObject;
|
|
96
99
|
},
|
|
97
|
-
{ dbObject,
|
|
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;
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
159
|
-
dbObject.
|
|
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 (
|
|
176
|
+
async () => {
|
|
186
177
|
return await this.mainDbModel.findByIdAndUpdate(userId, { roles }).exec();
|
|
187
178
|
},
|
|
188
179
|
{ serviceOptions }
|
package/src/core.module.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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>(
|
|
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
|
}
|
|
@@ -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
|
|
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
|
})
|