@lenne.tech/nest-server 8.6.2 → 8.6.5
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/inputs/filter.input.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +16 -13
- 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.resolver.js +1 -1
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.js +1 -1
- package/dist/server/modules/user/user.service.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 +42 -2
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +24 -19
- package/src/config.env.ts +4 -2
- package/src/core/common/inputs/filter.input.ts +0 -1
- package/src/core/common/interfaces/server-options.interface.ts +68 -52
- 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/modules/user/user.service.ts +1 -1
- package/src/server/server.module.ts +5 -1
- package/src/test/test.helper.ts +67 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.5",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "rimraf dist && tsc -p tsconfig.build.json",
|
|
18
18
|
"build:pack": "npm pack && echo 'use file:/ROOT_PATH_TO_TGZ_FILE to integrate the package'",
|
|
19
|
+
"docs": "npm run docs:ci && open ./public/index.html",
|
|
20
|
+
"docs:bootstrap": "node extras/update-spectaql-version.mjs && npx -y spectaql ./spectaql.yml",
|
|
21
|
+
"docs:ci": "ts-node ./scripts/init-server.ts && npm run docs:boostrap",
|
|
19
22
|
"format": "prettier --write 'src/**/*.ts'",
|
|
20
23
|
"format:staged": "pretty-quick --staged",
|
|
21
24
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
|
@@ -32,9 +35,10 @@
|
|
|
32
35
|
"test": "NODE_ENV=local jest",
|
|
33
36
|
"test:cov": "NODE_ENV=local jest --coverage",
|
|
34
37
|
"test:debug": "NODE_ENV=local node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
35
|
-
"test:e2e": "NODE_ENV=local jest --config jest-e2e.json --forceExit
|
|
36
|
-
"test:e2e-cov": "NODE_ENV=local jest --config jest-e2e.json --coverage --forceExit
|
|
37
|
-
"test:
|
|
38
|
+
"test:e2e": "NODE_ENV=local jest --config jest-e2e.json --forceExit",
|
|
39
|
+
"test:e2e-cov": "NODE_ENV=local jest --config jest-e2e.json --coverage --forceExit",
|
|
40
|
+
"test:e2e-doh": "NODE_ENV=local jest --config jest-e2e.json --forceExit --detectOpenHandles",
|
|
41
|
+
"test:ci": "NODE_ENV=local jest --config jest-e2e.json --ci --forceExit",
|
|
38
42
|
"test:watch": "NODE_ENV=local jest --watch",
|
|
39
43
|
"prepack": "npm run prestart:prod",
|
|
40
44
|
"prepare": "husky install",
|
|
@@ -54,26 +58,27 @@
|
|
|
54
58
|
"dependencies": {
|
|
55
59
|
"@apollo/gateway": "0.50.2",
|
|
56
60
|
"@nestjs/apollo": "10.0.11",
|
|
57
|
-
"@nestjs/common": "8.4.
|
|
58
|
-
"@nestjs/core": "8.4.
|
|
61
|
+
"@nestjs/common": "8.4.5",
|
|
62
|
+
"@nestjs/core": "8.4.5",
|
|
59
63
|
"@nestjs/graphql": "10.0.11",
|
|
60
64
|
"@nestjs/jwt": "8.0.0",
|
|
61
65
|
"@nestjs/mongoose": "9.0.3",
|
|
62
66
|
"@nestjs/passport": "8.2.1",
|
|
63
|
-
"@nestjs/platform-express": "8.4.
|
|
67
|
+
"@nestjs/platform-express": "8.4.5",
|
|
64
68
|
"apollo-server-core": "3.7.0",
|
|
65
69
|
"apollo-server-express": "3.7.0",
|
|
66
70
|
"bcrypt": "5.0.1",
|
|
67
71
|
"class-transformer": "0.5.1",
|
|
68
72
|
"class-validator": "0.13.2",
|
|
69
|
-
"ejs": "3.1.
|
|
70
|
-
"graphql": "16.
|
|
73
|
+
"ejs": "3.1.8",
|
|
74
|
+
"graphql": "16.5.0",
|
|
71
75
|
"graphql-subscriptions": "2.0.0",
|
|
76
|
+
"graphql-upload": "13.0.0",
|
|
72
77
|
"json-to-graphql-query": "2.2.4",
|
|
73
78
|
"light-my-request": "4.10.1",
|
|
74
79
|
"lodash": "4.17.21",
|
|
75
|
-
"mongodb": "4.
|
|
76
|
-
"mongoose": "6.3.
|
|
80
|
+
"mongodb": "4.6.0",
|
|
81
|
+
"mongoose": "6.3.3",
|
|
77
82
|
"multer": "1.4.4",
|
|
78
83
|
"node-mailjet": "3.4.1",
|
|
79
84
|
"nodemailer": "6.7.5",
|
|
@@ -85,28 +90,28 @@
|
|
|
85
90
|
"rxjs": "7.5.5"
|
|
86
91
|
},
|
|
87
92
|
"devDependencies": {
|
|
88
|
-
"@nestjs/testing": "8.4.
|
|
89
|
-
"@types/ejs": "3.1.
|
|
90
|
-
"@types/jest": "27.5.
|
|
93
|
+
"@nestjs/testing": "8.4.5",
|
|
94
|
+
"@types/ejs": "3.1.1",
|
|
95
|
+
"@types/jest": "27.5.1",
|
|
91
96
|
"@types/lodash": "4.14.182",
|
|
92
97
|
"@types/multer": "1.4.7",
|
|
93
|
-
"@types/node": "16.11.
|
|
98
|
+
"@types/node": "16.11.35",
|
|
94
99
|
"@types/node-mailjet": "3.3.8",
|
|
95
100
|
"@types/nodemailer": "6.4.4",
|
|
96
101
|
"@types/passport": "1.0.7",
|
|
97
102
|
"@types/supertest": "2.0.12",
|
|
98
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
99
|
-
"@typescript-eslint/parser": "5.
|
|
103
|
+
"@typescript-eslint/eslint-plugin": "5.23.0",
|
|
104
|
+
"@typescript-eslint/parser": "5.23.0",
|
|
100
105
|
"coffeescript": "2.7.0",
|
|
101
106
|
"eslint": "8.15.0",
|
|
102
107
|
"eslint-config-prettier": "8.5.0",
|
|
103
108
|
"find-file-up": "2.0.1",
|
|
104
|
-
"grunt": "1.5.
|
|
109
|
+
"grunt": "1.5.3",
|
|
105
110
|
"grunt-bg-shell": "2.3.3",
|
|
106
111
|
"grunt-contrib-clean": "2.0.1",
|
|
107
112
|
"grunt-contrib-watch": "1.1.0",
|
|
108
113
|
"grunt-sync": "0.8.2",
|
|
109
|
-
"husky": "
|
|
114
|
+
"husky": "8.0.1",
|
|
110
115
|
"jest": "28.1.0",
|
|
111
116
|
"pm2": "5.2.0",
|
|
112
117
|
"prettier": "2.6.2",
|
package/src/config.env.ts
CHANGED
|
@@ -31,6 +31,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
31
31
|
passwordResetLink: 'http://localhost:4200/user/password-reset',
|
|
32
32
|
},
|
|
33
33
|
env: 'development',
|
|
34
|
+
execAfterInit: 'npm run docs:bootstrap',
|
|
34
35
|
graphQl: {
|
|
35
36
|
driver: {
|
|
36
37
|
debug: true,
|
|
@@ -46,7 +47,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
46
47
|
port: 3000,
|
|
47
48
|
staticAssets: {
|
|
48
49
|
path: join(__dirname, '..', 'public'),
|
|
49
|
-
options: { prefix: '
|
|
50
|
+
options: { prefix: '' },
|
|
50
51
|
},
|
|
51
52
|
templates: {
|
|
52
53
|
path: join(__dirname, 'templates'),
|
|
@@ -80,6 +81,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
80
81
|
passwordResetLink: 'http://localhost:4200/user/password-reset',
|
|
81
82
|
},
|
|
82
83
|
env: 'productive',
|
|
84
|
+
execAfterInit: 'npm run docs:bootstrap',
|
|
83
85
|
graphQl: {
|
|
84
86
|
driver: {
|
|
85
87
|
debug: false,
|
|
@@ -95,7 +97,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
95
97
|
port: 3000,
|
|
96
98
|
staticAssets: {
|
|
97
99
|
path: join(__dirname, '..', 'public'),
|
|
98
|
-
options: { prefix: '
|
|
100
|
+
options: { prefix: '' },
|
|
99
101
|
},
|
|
100
102
|
templates: {
|
|
101
103
|
path: join(__dirname, 'templates'),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Field, InputType } from '@nestjs/graphql';
|
|
2
|
-
import { ModelHelper } from '../helpers/model.helper';
|
|
3
2
|
import { CombinedFilterInput } from './combined-filter.input';
|
|
4
3
|
import { CoreInput } from './core-input.input';
|
|
5
4
|
import { SingleFilterInput } from './single-filter.input';
|
|
@@ -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
|
}
|
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.user.
|
|
148
|
+
return context.user.hasRole(RoleEnum.ADMIN);
|
|
149
149
|
},
|
|
150
150
|
resolve: (user) => user,
|
|
151
151
|
})
|
|
@@ -50,7 +50,7 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
|
|
|
50
50
|
// and could not exist as currentUser before
|
|
51
51
|
if (!user.createdBy) {
|
|
52
52
|
await this.mainDbModel.findByIdAndUpdate(user.id, { createdBy: user.id });
|
|
53
|
-
user = await this.get(user.id, { ...serviceOptions, currentUser: serviceOptions
|
|
53
|
+
user = await this.get(user.id, { ...serviceOptions, currentUser: serviceOptions?.currentUser || user });
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Publish action
|
|
@@ -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
|
})
|
package/src/test/test.helper.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { INestApplication } from '@nestjs/common';
|
|
2
|
+
import { createClient } from 'graphql-ws';
|
|
2
3
|
// import { FastifyInstance } from 'fastify';
|
|
3
4
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
|
4
5
|
import * as LightMyRequest from 'light-my-request';
|
|
5
6
|
import * as supertest from 'supertest';
|
|
6
7
|
import * as util from 'util';
|
|
8
|
+
import * as ws from 'ws';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* GraphQL request type
|
|
@@ -64,6 +66,11 @@ export interface TestGraphQLOptions {
|
|
|
64
66
|
*/
|
|
65
67
|
convertEnums?: boolean | string[];
|
|
66
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Count of subscription messages, specifies how many messages are to be received on subscription
|
|
71
|
+
*/
|
|
72
|
+
countOfSubscriptionMessages?: number;
|
|
73
|
+
|
|
67
74
|
/**
|
|
68
75
|
* Print console logs
|
|
69
76
|
*/
|
|
@@ -104,11 +111,16 @@ export class TestHelper {
|
|
|
104
111
|
// app: FastifyInstance | INestApplication;
|
|
105
112
|
app: INestApplication;
|
|
106
113
|
|
|
114
|
+
// URL with port and directory to subscription endpoint
|
|
115
|
+
// e.g.: ws://localhost:3030/graphql
|
|
116
|
+
subscriptionUrl: string;
|
|
117
|
+
|
|
107
118
|
/**
|
|
108
119
|
* Constructor
|
|
109
120
|
*/
|
|
110
|
-
constructor(app: any) {
|
|
121
|
+
constructor(app: any, subscriptionUrl?: string) {
|
|
111
122
|
this.app = app;
|
|
123
|
+
this.subscriptionUrl = subscriptionUrl;
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
/**
|
|
@@ -121,6 +133,7 @@ export class TestHelper {
|
|
|
121
133
|
options = Object.assign(
|
|
122
134
|
{
|
|
123
135
|
convertEnums: true,
|
|
136
|
+
countOfSubscriptionMessages: 1,
|
|
124
137
|
token: null,
|
|
125
138
|
statusCode: 200,
|
|
126
139
|
log: false,
|
|
@@ -174,6 +187,10 @@ export class TestHelper {
|
|
|
174
187
|
query = jsonToGraphQLQuery(queryObj, { pretty: true });
|
|
175
188
|
}
|
|
176
189
|
|
|
190
|
+
if ((graphql as TestGraphQLConfig).type === TestGraphQLType.SUBSCRIPTION) {
|
|
191
|
+
return this.getSubscription(graphql as TestGraphQLConfig, query, options);
|
|
192
|
+
}
|
|
193
|
+
|
|
177
194
|
// Convert uppercase strings in arguments of query to enums
|
|
178
195
|
if (options.convertEnums) {
|
|
179
196
|
if (Array.isArray(options.convertEnums)) {
|
|
@@ -354,7 +371,7 @@ export class TestHelper {
|
|
|
354
371
|
|
|
355
372
|
// Log response
|
|
356
373
|
if (log) {
|
|
357
|
-
console.log(response);
|
|
374
|
+
console.log(JSON.stringify(response, null, 2));
|
|
358
375
|
}
|
|
359
376
|
|
|
360
377
|
// Log error
|
|
@@ -373,4 +390,52 @@ export class TestHelper {
|
|
|
373
390
|
// Return response
|
|
374
391
|
return response;
|
|
375
392
|
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get subscription
|
|
396
|
+
*/
|
|
397
|
+
async getSubscription(graphql: TestGraphQLConfig, query: string, options?: TestGraphQLOptions) {
|
|
398
|
+
// Check url
|
|
399
|
+
if (!this.subscriptionUrl) {
|
|
400
|
+
throw new Error("Missing subscriptionUrl in TestHelper: new TestHelper(app, 'ws://localhost:3030/graphql')");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Prepare subscription
|
|
404
|
+
let connectionParams;
|
|
405
|
+
if (options?.token) {
|
|
406
|
+
connectionParams = { Authorization: `Bearer ${options?.token}` };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Init client
|
|
410
|
+
if (options.log) {
|
|
411
|
+
console.log('Subscription query', JSON.stringify(query, null, 2));
|
|
412
|
+
}
|
|
413
|
+
const client = createClient({ url: this.subscriptionUrl, connectionParams, webSocketImpl: ws });
|
|
414
|
+
const messages: any[] = [];
|
|
415
|
+
let unsubscribe: () => void;
|
|
416
|
+
const onNext = (message) => {
|
|
417
|
+
if (options.log) {
|
|
418
|
+
console.log('Subscription message', JSON.stringify(message, null, 2));
|
|
419
|
+
}
|
|
420
|
+
messages.push(message?.data?.[graphql.name]);
|
|
421
|
+
if (messages.length <= options.countOfSubscriptionMessages) {
|
|
422
|
+
unsubscribe();
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// Subscribe
|
|
427
|
+
await new Promise((resolve, reject) => {
|
|
428
|
+
unsubscribe = client.subscribe(
|
|
429
|
+
{ query },
|
|
430
|
+
{
|
|
431
|
+
next: onNext,
|
|
432
|
+
error: reject,
|
|
433
|
+
complete: resolve as any,
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Return subscribed messages
|
|
439
|
+
return messages;
|
|
440
|
+
}
|
|
376
441
|
}
|