@lenne.tech/nest-server 10.0.3 → 10.0.4
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/README.md +9 -0
- package/dist/config.env.js +18 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +5 -4
- package/dist/core/common/services/model-doc.service.d.ts +16 -0
- package/dist/core/common/services/model-doc.service.js +107 -0
- package/dist/core/common/services/model-doc.service.js.map +1 -0
- package/dist/core/modules/health-check/core-health-check.service.js +6 -6
- package/dist/core/modules/health-check/core-health-check.service.js.map +1 -1
- package/dist/core/modules/user/core-user.model.js +1 -1
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core.module.js +4 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -6
- package/src/config.env.ts +18 -0
- package/src/core/common/interfaces/server-options.interface.ts +82 -4
- package/src/core/common/services/model-doc.service.ts +140 -0
- package/src/core/modules/health-check/core-health-check.service.ts +8 -6
- package/src/core/modules/user/core-user.model.ts +1 -1
- package/src/core.module.ts +7 -2
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.4",
|
|
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",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"build": "rimraf dist && nest build",
|
|
18
18
|
"build:pack": "npm pack && echo 'use file:/ROOT_PATH_TO_TGZ_FILE to integrate the package'",
|
|
19
19
|
"build:dev": "npm run build && yalc push --private",
|
|
20
|
-
"docs": "npm run docs:ci && open ./public/index.html",
|
|
20
|
+
"docs": "npm run docs:ci && open http://127.0.0.1:8080/ && open ./public/index.html && compodoc -p tsconfig.json -s ",
|
|
21
21
|
"docs:bootstrap": "node extras/update-spectaql-version.mjs && npx -y spectaql ./spectaql.yml",
|
|
22
|
-
"docs:ci": "ts-node ./scripts/init-server.ts && npm run docs:bootstrap",
|
|
22
|
+
"docs:ci": "ts-node ./scripts/init-server.ts && npm run docs:bootstrap && compodoc -p tsconfig.json",
|
|
23
23
|
"format": "prettier --write 'src/**/*.ts'",
|
|
24
24
|
"format:staged": "pretty-quick --staged",
|
|
25
25
|
"lint": "eslint \"{src,tests}/**/*.ts\" --fix",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@nestjs/passport": "10.0.0",
|
|
73
73
|
"@nestjs/platform-express": "10.1.3",
|
|
74
74
|
"@nestjs/schedule": "3.0.1",
|
|
75
|
-
"@nestjs/terminus": "
|
|
75
|
+
"@nestjs/terminus": "10.0.1",
|
|
76
76
|
"apollo-server-core": "3.11.1",
|
|
77
77
|
"apollo-server-express": "3.11.1",
|
|
78
78
|
"bcrypt": "5.1.0",
|
|
@@ -102,9 +102,12 @@
|
|
|
102
102
|
"reflect-metadata": "0.1.13",
|
|
103
103
|
"rfdc": "1.3.0",
|
|
104
104
|
"rimraf": "5.0.1",
|
|
105
|
-
"rxjs": "7.8.1"
|
|
105
|
+
"rxjs": "7.8.1",
|
|
106
|
+
"yuml-diagram": "1.2.0"
|
|
106
107
|
},
|
|
107
108
|
"devDependencies": {
|
|
109
|
+
"@babel/plugin-proposal-private-methods": "7.18.6",
|
|
110
|
+
"@compodoc/compodoc": "1.1.21",
|
|
108
111
|
"@lenne.tech/eslint-config-ts": "0.0.8",
|
|
109
112
|
"@nestjs/cli": "10.1.11",
|
|
110
113
|
"@nestjs/schematics": "10.0.1",
|
|
@@ -129,7 +132,7 @@
|
|
|
129
132
|
"coffeescript": "2.7.0",
|
|
130
133
|
"eslint": "8.46.0",
|
|
131
134
|
"eslint-config-prettier": "8.9.0",
|
|
132
|
-
"eslint-plugin-unused-imports": "
|
|
135
|
+
"eslint-plugin-unused-imports": "3.0.0",
|
|
133
136
|
"find-file-up": "2.0.1",
|
|
134
137
|
"grunt": "1.6.1",
|
|
135
138
|
"grunt-bg-shell": "2.3.3",
|
package/src/config.env.ts
CHANGED
|
@@ -57,6 +57,11 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
57
57
|
},
|
|
58
58
|
healthCheck: {
|
|
59
59
|
enabled: true,
|
|
60
|
+
configs: {
|
|
61
|
+
database: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
60
65
|
},
|
|
61
66
|
ignoreSelectionsForPopulate: true,
|
|
62
67
|
jwt: {
|
|
@@ -78,6 +83,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
78
83
|
collation: {
|
|
79
84
|
locale: 'de',
|
|
80
85
|
},
|
|
86
|
+
modelDocumentation: true,
|
|
81
87
|
uri: 'mongodb://127.0.0.1/nest-server-local',
|
|
82
88
|
},
|
|
83
89
|
port: 3000,
|
|
@@ -133,6 +139,11 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
133
139
|
},
|
|
134
140
|
healthCheck: {
|
|
135
141
|
enabled: true,
|
|
142
|
+
configs: {
|
|
143
|
+
database: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
136
147
|
},
|
|
137
148
|
ignoreSelectionsForPopulate: true,
|
|
138
149
|
jwt: {
|
|
@@ -154,6 +165,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
154
165
|
collation: {
|
|
155
166
|
locale: 'de',
|
|
156
167
|
},
|
|
168
|
+
modelDocumentation: false,
|
|
157
169
|
uri: 'mongodb://127.0.0.1/nest-server-dev',
|
|
158
170
|
},
|
|
159
171
|
port: 3000,
|
|
@@ -209,6 +221,11 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
209
221
|
},
|
|
210
222
|
healthCheck: {
|
|
211
223
|
enabled: true,
|
|
224
|
+
configs: {
|
|
225
|
+
database: {
|
|
226
|
+
enabled: true,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
212
229
|
},
|
|
213
230
|
ignoreSelectionsForPopulate: true,
|
|
214
231
|
jwt: {
|
|
@@ -230,6 +247,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
230
247
|
collation: {
|
|
231
248
|
locale: 'de',
|
|
232
249
|
},
|
|
250
|
+
modelDocumentation: false,
|
|
233
251
|
uri: 'mongodb://127.0.0.1/nest-server-prod',
|
|
234
252
|
},
|
|
235
253
|
port: 3000,
|
|
@@ -176,26 +176,98 @@ export interface IServerOptions {
|
|
|
176
176
|
* Whether to activate health check endpoints
|
|
177
177
|
*/
|
|
178
178
|
healthCheck?: {
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Whether health check is enabled
|
|
182
|
+
*/
|
|
179
183
|
enabled?: boolean;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Configuration of single health checks
|
|
187
|
+
*/
|
|
180
188
|
configs?: {
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Configuration for database health check
|
|
192
|
+
*/
|
|
181
193
|
database?: {
|
|
182
|
-
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Whether to enable the database health check
|
|
197
|
+
*/
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Key in result JSON
|
|
202
|
+
*/
|
|
183
203
|
key?: string;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Database health check options
|
|
207
|
+
*/
|
|
184
208
|
options?: MongoosePingCheckSettings;
|
|
185
209
|
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Configuration for memory heap health check
|
|
213
|
+
*/
|
|
186
214
|
memoryHeap?: {
|
|
187
|
-
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Whether to enable the memory heap health check
|
|
218
|
+
*/
|
|
219
|
+
enabled?: boolean;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Key in result JSON
|
|
223
|
+
*/
|
|
188
224
|
key?: string;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Memory limit in bytes
|
|
228
|
+
*/
|
|
189
229
|
heapUsedThreshold?: number;
|
|
190
230
|
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Configuration for memory resident set size health check
|
|
234
|
+
*/
|
|
191
235
|
memoryRss?: {
|
|
192
|
-
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Whether to enable the memory resident set size health check
|
|
239
|
+
*/
|
|
240
|
+
enabled?: boolean;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Key in result JSON
|
|
244
|
+
*/
|
|
193
245
|
key?: string;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Memory limit in bytes
|
|
249
|
+
*/
|
|
194
250
|
rssThreshold?: number;
|
|
195
251
|
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Configuration for disk space health check
|
|
255
|
+
*/
|
|
196
256
|
storage?: {
|
|
197
|
-
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Whether to enable the disk space health check
|
|
260
|
+
*/
|
|
261
|
+
enabled?: boolean;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Key in result JSON
|
|
265
|
+
*/
|
|
198
266
|
key?: string;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Disk health indicator options
|
|
270
|
+
*/
|
|
199
271
|
options?: DiskHealthIndicatorOptions;
|
|
200
272
|
};
|
|
201
273
|
};
|
|
@@ -249,6 +321,12 @@ export interface IServerOptions {
|
|
|
249
321
|
mongoose?: {
|
|
250
322
|
collation?: CollationOptions;
|
|
251
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Whether to create SVG-Diagrams of mongoose models
|
|
326
|
+
* @beta
|
|
327
|
+
*/
|
|
328
|
+
modelDocumentation?: boolean;
|
|
329
|
+
|
|
252
330
|
/**
|
|
253
331
|
* Mongoose connection string
|
|
254
332
|
*/
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs = require('fs');
|
|
2
|
+
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
|
|
3
|
+
import { InjectConnection } from '@nestjs/mongoose';
|
|
4
|
+
import { Connection } from 'mongoose';
|
|
5
|
+
import YumlDiagram = require('yuml-diagram');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schema config for yUml creation
|
|
9
|
+
*/
|
|
10
|
+
export interface ModelDocSchemaConfig {
|
|
11
|
+
isArray: boolean;
|
|
12
|
+
name: string;
|
|
13
|
+
ref: string;
|
|
14
|
+
type: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Model documentation as yUML-SVG
|
|
19
|
+
*/
|
|
20
|
+
@Injectable()
|
|
21
|
+
export class ModelDocService implements OnApplicationBootstrap {
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
@InjectConnection() private readonly connection: Connection,
|
|
25
|
+
) {
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Lifecycle hook that is called right after the application has started.
|
|
30
|
+
*/
|
|
31
|
+
async onApplicationBootstrap() {
|
|
32
|
+
const schemaJson = this.getSchemaJson();
|
|
33
|
+
const yUml = this.jsonToYuml(schemaJson);
|
|
34
|
+
this.yUmlToSvg(yUml);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Analyse the mongoose database models and create JSON
|
|
39
|
+
* @protected
|
|
40
|
+
*/
|
|
41
|
+
protected getSchemaJson(): Record<string, Record<string, ModelDocSchemaConfig>> {
|
|
42
|
+
|
|
43
|
+
// Prepare results
|
|
44
|
+
const results: Record<string, Record<string, ModelDocSchemaConfig>> = {};
|
|
45
|
+
|
|
46
|
+
// Process models
|
|
47
|
+
const models = this.connection.modelNames();
|
|
48
|
+
for (const modelName of models) {
|
|
49
|
+
results[modelName] = {};
|
|
50
|
+
|
|
51
|
+
// Process schema
|
|
52
|
+
const schema = this.connection.model(modelName).schema;
|
|
53
|
+
Object.keys(schema.paths).forEach((key) => {
|
|
54
|
+
const obj: any = schema.obj[key] || {};
|
|
55
|
+
const path = schema.paths[key];
|
|
56
|
+
|
|
57
|
+
results[modelName][key] = {
|
|
58
|
+
isArray: path.instance === 'Array',
|
|
59
|
+
name: path.path,
|
|
60
|
+
ref: Array.isArray(obj) ? undefined : obj.ref,
|
|
61
|
+
type: path.instance === 'Array'
|
|
62
|
+
? Array.isArray(obj)
|
|
63
|
+
? obj[0]() === '' ? 'String' : obj[0]()
|
|
64
|
+
: typeof obj.type === 'function' ? obj.type.name : obj.type
|
|
65
|
+
: path.instance,
|
|
66
|
+
};
|
|
67
|
+
if (results[modelName][key].type === 'Mixed') {
|
|
68
|
+
results[modelName][key].type = 'JSON';
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Return results
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Convert JSON to yUML
|
|
79
|
+
* @param json
|
|
80
|
+
* @protected
|
|
81
|
+
*/
|
|
82
|
+
protected jsonToYuml(json: Record<string, Record<string, ModelDocSchemaConfig>>) {
|
|
83
|
+
// Convert JSON to yUML
|
|
84
|
+
let yumlText = '// {type:class}';
|
|
85
|
+
for (const [modelName, properties] of Object.entries(json)) {
|
|
86
|
+
yumlText += `\n[${modelName} | `;
|
|
87
|
+
const refs = [];
|
|
88
|
+
let subYumlText = '';
|
|
89
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
90
|
+
let type = value.type;
|
|
91
|
+
if (value.isArray) {
|
|
92
|
+
type = `Array<${type}>`;
|
|
93
|
+
}
|
|
94
|
+
if (value.ref) {
|
|
95
|
+
refs.push(`[${modelName}]-${key}>[${value.ref}]`);
|
|
96
|
+
}
|
|
97
|
+
if (key.startsWith('__')) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (key === '_id') {
|
|
101
|
+
subYumlText = `id: ObjectId; ${subYumlText}`;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
subYumlText += `${key}: ${type}; `;
|
|
105
|
+
}
|
|
106
|
+
yumlText += `${subYumlText}]\n`;
|
|
107
|
+
for (const ref of refs) {
|
|
108
|
+
yumlText += `${ref}\n`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return yumlText;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Convert yUML to SVG
|
|
116
|
+
* @param yUmlText
|
|
117
|
+
* @protected
|
|
118
|
+
*/
|
|
119
|
+
protected yUmlToSvg(yUmlText: String) {
|
|
120
|
+
|
|
121
|
+
// Create diagrams
|
|
122
|
+
// see https://github.com/jaime-olivares/yuml-diagram
|
|
123
|
+
// and https://yuml.me/diagram/scruffy/class/samples
|
|
124
|
+
const yuml = new YumlDiagram();
|
|
125
|
+
const svgLightBg = yuml.processYumlDocument(yUmlText, false);
|
|
126
|
+
const svgDarkBg = yuml.processYumlDocument(yUmlText, true);
|
|
127
|
+
|
|
128
|
+
// Save diagrams
|
|
129
|
+
fs.writeFile('model-doc-light.svg', svgLightBg, (err) => {
|
|
130
|
+
if (err) {
|
|
131
|
+
console.error(err);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
fs.writeFile('model-doc-dark.svg', svgDarkBg, (err) => {
|
|
135
|
+
if (err) {
|
|
136
|
+
console.error(err);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -27,26 +27,28 @@ export class CoreHealthCheckService {
|
|
|
27
27
|
|
|
28
28
|
healthCheck(): Promise<HealthCheckResult> {
|
|
29
29
|
const healthIndicatorFunctions = [];
|
|
30
|
-
if (
|
|
30
|
+
if (this.config.get<boolean>('healthCheck.configs.database.enabled')) {
|
|
31
31
|
healthIndicatorFunctions.push(() =>
|
|
32
32
|
this.db.pingCheck(
|
|
33
33
|
this.config.get<string>('healthCheck.configs.database.key') ?? 'database',
|
|
34
34
|
this.config.get<MongoosePingCheckSettings>('healthCheck.configs.database.options') ?? { timeout: 300 },
|
|
35
35
|
));
|
|
36
36
|
}
|
|
37
|
-
if (
|
|
37
|
+
if (this.config.get<boolean>('healthCheck.configs.memoryHeap.enabled')) {
|
|
38
38
|
healthIndicatorFunctions.push(() => this.memory.checkHeap(
|
|
39
39
|
this.config.get<string>('healthCheck.configs.memoryHeap.key') ?? 'memoryHeap',
|
|
40
|
-
|
|
40
|
+
// memory in bytes (4GB default)
|
|
41
|
+
this.config.get<number>('healthCheck.configs.memoryHeap.heapUsedThreshold') ?? 4 * 1024 * 1024 * 1024,
|
|
41
42
|
));
|
|
42
43
|
}
|
|
43
|
-
if (
|
|
44
|
+
if (this.config.get<boolean>('healthCheck.configs.memoryRss.enabled')) {
|
|
44
45
|
healthIndicatorFunctions.push(() => this.memory.checkRSS(
|
|
45
46
|
this.config.get<string>('healthCheck.configs.memoryRss.key') ?? 'memoryRss',
|
|
46
|
-
|
|
47
|
+
// memory in bytes (4GB default)
|
|
48
|
+
this.config.get<number>('healthCheck.configs.memoryRss.rssThreshold') ?? 4 * 1024 * 1024 * 1024,
|
|
47
49
|
));
|
|
48
50
|
}
|
|
49
|
-
if (
|
|
51
|
+
if (this.config.get<boolean>('healthCheck.configs.storage.enabled')) {
|
|
50
52
|
healthIndicatorFunctions.push(() => this.disk.checkStorage(
|
|
51
53
|
this.config.get<string>('healthCheck.configs.storage.key') ?? 'storage',
|
|
52
54
|
this.config.get<DiskHealthIndicatorOptions>('healthCheck.configs.storage.options') ?? {
|
package/src/core.module.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { EmailService } from './core/common/services/email.service';
|
|
|
14
14
|
import { MailjetService } from './core/common/services/mailjet.service';
|
|
15
15
|
import { TemplateService } from './core/common/services/template.service';
|
|
16
16
|
import { CoreHealthCheckModule } from './core/modules/health-check/core-health-check.module';
|
|
17
|
+
import { ModelDocService } from './core/common/services/model-doc.service';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Core module (dynamic)
|
|
@@ -127,14 +128,14 @@ export class CoreModule implements NestModule {
|
|
|
127
128
|
);
|
|
128
129
|
|
|
129
130
|
// Set providers
|
|
130
|
-
const providers = [
|
|
131
|
+
const providers: any[] = [
|
|
131
132
|
// The ConfigService provides access to the current configuration of the module
|
|
132
133
|
{
|
|
133
134
|
provide: ConfigService,
|
|
134
135
|
useValue: new ConfigService(config),
|
|
135
136
|
},
|
|
136
137
|
|
|
137
|
-
// [Global] Map plain objects to
|
|
138
|
+
// [Global] Map plain objects to meta-type and validate
|
|
138
139
|
{
|
|
139
140
|
provide: APP_PIPE,
|
|
140
141
|
useClass: MapAndValidatePipe,
|
|
@@ -149,6 +150,10 @@ export class CoreModule implements NestModule {
|
|
|
149
150
|
ComplexityPlugin,
|
|
150
151
|
];
|
|
151
152
|
|
|
153
|
+
if (config.mongoose?.modelDocumentation) {
|
|
154
|
+
providers.push(ModelDocService);
|
|
155
|
+
}
|
|
156
|
+
|
|
152
157
|
const imports: any[] = [
|
|
153
158
|
MongooseModule.forRoot(config.mongoose.uri, config.mongoose.options),
|
|
154
159
|
GraphQLModule.forRootAsync<ApolloDriverConfig>(
|
package/src/index.ts
CHANGED
|
@@ -64,6 +64,7 @@ export * from './core/common/services/core-cron-jobs.service';
|
|
|
64
64
|
export * from './core/common/services/crud.service';
|
|
65
65
|
export * from './core/common/services/email.service';
|
|
66
66
|
export * from './core/common/services/mailjet.service';
|
|
67
|
+
export * from './core/common/services/model-doc.service';
|
|
67
68
|
export * from './core/common/services/module.service';
|
|
68
69
|
export * from './core/common/services/template.service';
|
|
69
70
|
export * from './core/common/types/core-model-constructor.type';
|