@nest-yalc-2/app 2.2.1 → 2.2.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/package.json +10 -2
- package/src/__tests__/app-bootstrap-base.helper.spec.ts +86 -38
- package/src/__tests__/app-bootstrap-standalone.helper.spec.ts +35 -35
- package/src/__tests__/app-bootstrap.helper.spec.ts +162 -98
- package/src/__tests__/app-config.service.spec.ts +55 -55
- package/src/__tests__/app-context.service.spec.ts +28 -28
- package/src/__tests__/app.events.spec.ts +12 -12
- package/src/__tests__/app.helper.spec.ts +71 -204
- package/src/__tests__/base-app-module.helper.spec.ts +295 -231
- package/src/__tests__/base-app.controller.spec.ts +90 -90
- package/src/__tests__/base-app.service.spec.ts +39 -39
- package/src/__tests__/cls.module.spec.ts +33 -33
- package/src/__tests__/life-cycle-handler.service.spec.ts +57 -48
- package/src/__tests__/unwrap-result.interceptor.spec.ts +52 -52
- package/src/app-bootstrap-base.helper.ts +206 -206
- package/src/app-bootstrap-standalone.helper.ts +67 -67
- package/src/app-bootstrap.helper.js +9 -9
- package/src/app-bootstrap.helper.ts +279 -279
- package/src/app-config.service.ts +61 -61
- package/src/app-context.module.ts +6 -6
- package/src/app-context.service.ts +16 -16
- package/src/app-imports.factory.js +2 -2
- package/src/app-imports.factory.js.map +1 -1
- package/src/app-imports.factory.ts +321 -321
- package/src/app.events.ts +8 -8
- package/src/app.helper.ts +73 -73
- package/src/base-app-module.helper.d.ts +2 -2
- package/src/base-app-module.helper.js +8 -5
- package/src/base-app-module.helper.js.map +1 -1
- package/src/base-app-module.helper.ts +457 -453
- package/src/base-app.controller.ts +51 -51
- package/src/base-app.interface.ts +55 -55
- package/src/base-app.service.ts +33 -33
- package/src/cls.module.d.ts +2 -1
- package/src/cls.module.js.map +1 -1
- package/src/cls.module.ts +76 -75
- package/src/conf.type.ts +22 -22
- package/src/def.const.ts +33 -33
- package/src/global.enum.ts +6 -6
- package/src/life-cycle-handler.service.ts +66 -66
- package/src/unwrap-result.interceptor.ts +32 -32
- package/src/yalc-controller.interface.ts +6 -6
|
@@ -1,279 +1,279 @@
|
|
|
1
|
-
import { SystemExceptionFilter } from '@nest-yalc-2/errors/filters/index.js';
|
|
2
|
-
import {
|
|
3
|
-
BadRequestException,
|
|
4
|
-
ExceptionFilter,
|
|
5
|
-
NestApplicationOptions,
|
|
6
|
-
ValidationPipe,
|
|
7
|
-
ValidationPipeOptions,
|
|
8
|
-
} from '@nestjs/common';
|
|
9
|
-
import { NestFactory } from '@nestjs/core';
|
|
10
|
-
// import { GqlExceptionFilter } from '@nestjs/graphql';
|
|
11
|
-
import {
|
|
12
|
-
FastifyAdapter,
|
|
13
|
-
NestFastifyApplication,
|
|
14
|
-
} from '@nestjs/platform-fastify';
|
|
15
|
-
import fastifyCookie from '@fastify/cookie';
|
|
16
|
-
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
17
|
-
import { fastify, FastifyInstance } from 'fastify';
|
|
18
|
-
import { envIsTrue } from '@node-yalc/utils/env.helper.js';
|
|
19
|
-
import { useContainer } from 'class-validator';
|
|
20
|
-
import clc from 'cli-color';
|
|
21
|
-
import {
|
|
22
|
-
BaseAppBootstrap,
|
|
23
|
-
IGlobalOptions,
|
|
24
|
-
} from './app-bootstrap-base.helper.js';
|
|
25
|
-
import { getEnvLoggerLevels } from '@node-yalc/logger/logger.helper.js';
|
|
26
|
-
import { UnwrapResultInterceptor } from './unwrap-result.interceptor.js';
|
|
27
|
-
|
|
28
|
-
export interface ICreateOptions {
|
|
29
|
-
enableSwagger?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Path segment (without leading slash) where Swagger UI/JSON is exposed.
|
|
32
|
-
* Defaults to "api".
|
|
33
|
-
*/
|
|
34
|
-
swaggerPath?: string;
|
|
35
|
-
filters?: ExceptionFilter[];
|
|
36
|
-
validationPipeOptions?: ValidationPipeOptions;
|
|
37
|
-
/**
|
|
38
|
-
* On some cases we do want to manually override the apiPrefix of the service conf
|
|
39
|
-
*/
|
|
40
|
-
apiPrefix?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface INestCreateOptions
|
|
44
|
-
extends ICreateOptions, NestApplicationOptions {}
|
|
45
|
-
|
|
46
|
-
export class AppBootstrap<
|
|
47
|
-
TGlobalOptions extends IGlobalOptions = IGlobalOptions,
|
|
48
|
-
> extends BaseAppBootstrap<NestFastifyApplication> {
|
|
49
|
-
private fastifyInstance?: FastifyInstance;
|
|
50
|
-
protected isSwaggerEnabled: boolean = false;
|
|
51
|
-
protected swaggerPath = 'api';
|
|
52
|
-
|
|
53
|
-
constructor(appAlias: string, module: any, options?: TGlobalOptions) {
|
|
54
|
-
super(appAlias, module, { globalsOptions: options });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* istanbul ignore next */
|
|
58
|
-
async startServer(options?: {
|
|
59
|
-
createOptions?: INestCreateOptions;
|
|
60
|
-
fastifyInstance?: FastifyInstance;
|
|
61
|
-
}) {
|
|
62
|
-
await this.initApp(options);
|
|
63
|
-
|
|
64
|
-
if (envIsTrue(process.env.APP_DRY_RUN) === true) {
|
|
65
|
-
await this.closeApp();
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// no need to wait here
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
71
|
-
this.listen();
|
|
72
|
-
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async initApp(options?: {
|
|
77
|
-
createOptions?: INestCreateOptions;
|
|
78
|
-
fastifyInstance?: FastifyInstance;
|
|
79
|
-
}) {
|
|
80
|
-
await this.createApp({
|
|
81
|
-
fastifyInstance: this.fastifyInstance,
|
|
82
|
-
createOptions: options?.createOptions,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
return this.initSetup({
|
|
86
|
-
fastifyInstance: this.fastifyInstance,
|
|
87
|
-
createOptions: options?.createOptions,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async initSetup(options?: {
|
|
92
|
-
createOptions?: ICreateOptions;
|
|
93
|
-
fastifyInstance?: FastifyInstance;
|
|
94
|
-
}) {
|
|
95
|
-
try {
|
|
96
|
-
await this.applyBootstrapGlobals(options?.createOptions);
|
|
97
|
-
|
|
98
|
-
await this.getApp().init();
|
|
99
|
-
} catch (err) {
|
|
100
|
-
/* istanbul ignore next */
|
|
101
|
-
console.error('App init failed:', err);
|
|
102
|
-
/* istanbul ignore next */
|
|
103
|
-
this.closeCleanup();
|
|
104
|
-
/* istanbul ignore next */
|
|
105
|
-
throw new Error('Process aborted');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* istanbul ignore next */
|
|
109
|
-
if (envIsTrue(process.env.APP_DRY_RUN) === true) {
|
|
110
|
-
/* istanbul ignore next */
|
|
111
|
-
this.loggerService?.log('Dry run, exiting...');
|
|
112
|
-
/* istanbul ignore next */
|
|
113
|
-
await this.closeApp();
|
|
114
|
-
/* istanbul ignore next */
|
|
115
|
-
process.exit(0);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return this;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async createApp(options?: {
|
|
122
|
-
createOptions?: INestCreateOptions;
|
|
123
|
-
fastifyInstance?: FastifyInstance;
|
|
124
|
-
}) {
|
|
125
|
-
this.fastifyInstance = options?.fastifyInstance ?? fastify();
|
|
126
|
-
|
|
127
|
-
let app;
|
|
128
|
-
try {
|
|
129
|
-
app = await NestFactory.create<NestFastifyApplication>(
|
|
130
|
-
this.module,
|
|
131
|
-
new FastifyAdapter(this.fastifyInstance as any),
|
|
132
|
-
{
|
|
133
|
-
bufferLogs: false,
|
|
134
|
-
abortOnError: options?.createOptions?.abortOnError ?? false,
|
|
135
|
-
logger: getEnvLoggerLevels(),
|
|
136
|
-
...(options?.createOptions ?? {}),
|
|
137
|
-
},
|
|
138
|
-
);
|
|
139
|
-
} catch (err) {
|
|
140
|
-
this.closeCleanup();
|
|
141
|
-
// eslint-disable-next-line no-console
|
|
142
|
-
console.error(clc.red('Failed to create app'), clc.red(err));
|
|
143
|
-
throw new Error('Process aborted');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return this.setApp(app);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* istanbul ignore next */
|
|
150
|
-
/* istanbul ignore next */
|
|
151
|
-
getFastifyInstance() {
|
|
152
|
-
return this.fastifyInstance;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/* istanbul ignore next */
|
|
156
|
-
setSwaggerEnabled(enabled: boolean) {
|
|
157
|
-
this.isSwaggerEnabled = enabled;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async applyBootstrapGlobals(options?: ICreateOptions) {
|
|
161
|
-
await super.applyBootstrapGlobals(options);
|
|
162
|
-
|
|
163
|
-
this.getApp().useGlobalPipes(
|
|
164
|
-
new ValidationPipe({
|
|
165
|
-
transform: true,
|
|
166
|
-
transformOptions: { enableImplicitConversion: false },
|
|
167
|
-
validateCustomDecorators: true,
|
|
168
|
-
exceptionFactory: /* istanbul ignore next */ (errors) => {
|
|
169
|
-
/* istanbul ignore next */
|
|
170
|
-
const errorMessages: { [key: string]: any } = {};
|
|
171
|
-
/* istanbul ignore next */
|
|
172
|
-
errors.forEach((error) => {
|
|
173
|
-
errorMessages[error.property] = error;
|
|
174
|
-
});
|
|
175
|
-
/* istanbul ignore next */
|
|
176
|
-
return new BadRequestException(errorMessages);
|
|
177
|
-
},
|
|
178
|
-
...(options?.validationPipeOptions ?? {}),
|
|
179
|
-
}),
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
this.getApp().setGlobalPrefix(
|
|
183
|
-
options?.apiPrefix ?? (this.getConf()?.apiPrefix || ''),
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
await this.getApp().register(fastifyCookie as any, {});
|
|
187
|
-
|
|
188
|
-
this.getApp().useGlobalInterceptors(new UnwrapResultInterceptor());
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* @todo refactor using a factory function to share with all services
|
|
192
|
-
*/
|
|
193
|
-
const filters = [
|
|
194
|
-
new SystemExceptionFilter(this.loggerService),
|
|
195
|
-
...(options?.filters ?? []),
|
|
196
|
-
];
|
|
197
|
-
this.getApp().useGlobalFilters(...filters);
|
|
198
|
-
|
|
199
|
-
if (options?.enableSwagger) {
|
|
200
|
-
const swaggerPath = sanitizeSwaggerPath(options.swaggerPath);
|
|
201
|
-
this.swaggerPath = swaggerPath;
|
|
202
|
-
this.setSwaggerEnabled(true);
|
|
203
|
-
const document = SwaggerModule.createDocument(
|
|
204
|
-
this.getApp(),
|
|
205
|
-
this.buildSwaggerConfig().build(),
|
|
206
|
-
);
|
|
207
|
-
SwaggerModule.setup(swaggerPath, this.getApp(), document, {
|
|
208
|
-
jsonDocumentUrl: `/${swaggerPath}/json`,
|
|
209
|
-
useGlobalPrefix: true,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
useContainer(this.getApp().select(this.getModule()), {
|
|
214
|
-
fallbackOnErrors: true,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
return this;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
buildSwaggerConfig() {
|
|
221
|
-
return new DocumentBuilder()
|
|
222
|
-
.setTitle(this.appAlias)
|
|
223
|
-
.setDescription(`${this.appAlias} rest api`);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/* istanbul ignore next */
|
|
227
|
-
async listen(callback?: {
|
|
228
|
-
(port: number, host: string, domain: string): void;
|
|
229
|
-
}) {
|
|
230
|
-
const port = this.getConf()?.port || 0;
|
|
231
|
-
const host = this.getConf()?.host || '0.0.0.0';
|
|
232
|
-
let apiPrefix = this.getConf()?.apiPrefix;
|
|
233
|
-
apiPrefix = apiPrefix ? `/${apiPrefix}` : '';
|
|
234
|
-
const domain = this.getConf()?.domain || 'localhost';
|
|
235
|
-
await this.getApp().listen(port, host, async (_err, address) => {
|
|
236
|
-
// eslint-disable-next-line no-console
|
|
237
|
-
console.debug(`Server ${this.appAlias} listening on
|
|
238
|
-
http://localhost:${port}${apiPrefix}/
|
|
239
|
-
http://127.0.0.1:${port}${apiPrefix}/
|
|
240
|
-
http://${domain}:${port}${apiPrefix}/
|
|
241
|
-
${address}`);
|
|
242
|
-
|
|
243
|
-
// // eslint-disable-next-line no-console
|
|
244
|
-
// console.debug(`GraphQL ${this.appAlias} listening on
|
|
245
|
-
// http://localhost:${port}${apiPrefix}/graphql
|
|
246
|
-
// http://127.0.0.1:${port}${apiPrefix}/graphql
|
|
247
|
-
// http://${domain}:${port}${apiPrefix}/graphql`);
|
|
248
|
-
|
|
249
|
-
if (this.isSwaggerEnabled) {
|
|
250
|
-
// eslint-disable-next-line no-console
|
|
251
|
-
console.debug(`Swagger ${this.appAlias} listening on
|
|
252
|
-
http://localhost:${port}${apiPrefix}/${this.swaggerPath}
|
|
253
|
-
http://127.0.0.1:${port}${apiPrefix}/${this.swaggerPath}
|
|
254
|
-
http://${domain}:${port}${apiPrefix}/${this.swaggerPath}
|
|
255
|
-
${address}/${this.swaggerPath}
|
|
256
|
-
JSON: http://localhost:${port}${apiPrefix}/${this.swaggerPath}/json`);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
callback?.(port, host, domain);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Hot reload @see https://docs.nestjs.com/recipes/hot-reload#hot-module-replacement-1
|
|
264
|
-
*/
|
|
265
|
-
const hmr = typeof module !== 'undefined' ? (module as any).hot : undefined;
|
|
266
|
-
if (hmr) {
|
|
267
|
-
// eslint-disable-next-line no-console
|
|
268
|
-
console.debug('Hot reload enabled. Reloading...');
|
|
269
|
-
hmr.accept();
|
|
270
|
-
hmr.dispose(() => this.closeApp());
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/* istanbul ignore next */
|
|
276
|
-
function sanitizeSwaggerPath(path?: string): string {
|
|
277
|
-
const normalized = (path ?? 'api').replace(/^\/+/, '').replace(/\/+$/, '');
|
|
278
|
-
return normalized || 'api';
|
|
279
|
-
}
|
|
1
|
+
import { SystemExceptionFilter } from '@nest-yalc-2/errors/filters/index.js';
|
|
2
|
+
import {
|
|
3
|
+
BadRequestException,
|
|
4
|
+
ExceptionFilter,
|
|
5
|
+
NestApplicationOptions,
|
|
6
|
+
ValidationPipe,
|
|
7
|
+
ValidationPipeOptions,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
import { NestFactory } from '@nestjs/core';
|
|
10
|
+
// import { GqlExceptionFilter } from '@nestjs/graphql';
|
|
11
|
+
import {
|
|
12
|
+
FastifyAdapter,
|
|
13
|
+
NestFastifyApplication,
|
|
14
|
+
} from '@nestjs/platform-fastify';
|
|
15
|
+
import fastifyCookie from '@fastify/cookie';
|
|
16
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
17
|
+
import { fastify, FastifyInstance } from 'fastify';
|
|
18
|
+
import { envIsTrue } from '@node-yalc/utils/env.helper.js';
|
|
19
|
+
import { useContainer } from 'class-validator';
|
|
20
|
+
import clc from 'cli-color';
|
|
21
|
+
import {
|
|
22
|
+
BaseAppBootstrap,
|
|
23
|
+
IGlobalOptions,
|
|
24
|
+
} from './app-bootstrap-base.helper.js';
|
|
25
|
+
import { getEnvLoggerLevels } from '@node-yalc/logger/logger.helper.js';
|
|
26
|
+
import { UnwrapResultInterceptor } from './unwrap-result.interceptor.js';
|
|
27
|
+
|
|
28
|
+
export interface ICreateOptions {
|
|
29
|
+
enableSwagger?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Path segment (without leading slash) where Swagger UI/JSON is exposed.
|
|
32
|
+
* Defaults to "api".
|
|
33
|
+
*/
|
|
34
|
+
swaggerPath?: string;
|
|
35
|
+
filters?: ExceptionFilter[];
|
|
36
|
+
validationPipeOptions?: ValidationPipeOptions;
|
|
37
|
+
/**
|
|
38
|
+
* On some cases we do want to manually override the apiPrefix of the service conf
|
|
39
|
+
*/
|
|
40
|
+
apiPrefix?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface INestCreateOptions
|
|
44
|
+
extends ICreateOptions, NestApplicationOptions {}
|
|
45
|
+
|
|
46
|
+
export class AppBootstrap<
|
|
47
|
+
TGlobalOptions extends IGlobalOptions = IGlobalOptions,
|
|
48
|
+
> extends BaseAppBootstrap<NestFastifyApplication> {
|
|
49
|
+
private fastifyInstance?: FastifyInstance;
|
|
50
|
+
protected isSwaggerEnabled: boolean = false;
|
|
51
|
+
protected swaggerPath = 'api';
|
|
52
|
+
|
|
53
|
+
constructor(appAlias: string, module: any, options?: TGlobalOptions) {
|
|
54
|
+
super(appAlias, module, { globalsOptions: options });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* istanbul ignore next */
|
|
58
|
+
async startServer(options?: {
|
|
59
|
+
createOptions?: INestCreateOptions;
|
|
60
|
+
fastifyInstance?: FastifyInstance;
|
|
61
|
+
}) {
|
|
62
|
+
await this.initApp(options);
|
|
63
|
+
|
|
64
|
+
if (envIsTrue(process.env.APP_DRY_RUN) === true) {
|
|
65
|
+
await this.closeApp();
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// no need to wait here
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
71
|
+
this.listen();
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async initApp(options?: {
|
|
77
|
+
createOptions?: INestCreateOptions;
|
|
78
|
+
fastifyInstance?: FastifyInstance;
|
|
79
|
+
}) {
|
|
80
|
+
await this.createApp({
|
|
81
|
+
fastifyInstance: this.fastifyInstance,
|
|
82
|
+
createOptions: options?.createOptions,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return this.initSetup({
|
|
86
|
+
fastifyInstance: this.fastifyInstance,
|
|
87
|
+
createOptions: options?.createOptions,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async initSetup(options?: {
|
|
92
|
+
createOptions?: ICreateOptions;
|
|
93
|
+
fastifyInstance?: FastifyInstance;
|
|
94
|
+
}) {
|
|
95
|
+
try {
|
|
96
|
+
await this.applyBootstrapGlobals(options?.createOptions);
|
|
97
|
+
|
|
98
|
+
await this.getApp().init();
|
|
99
|
+
} catch (err) {
|
|
100
|
+
/* istanbul ignore next */
|
|
101
|
+
console.error('App init failed:', err);
|
|
102
|
+
/* istanbul ignore next */
|
|
103
|
+
this.closeCleanup();
|
|
104
|
+
/* istanbul ignore next */
|
|
105
|
+
throw new Error('Process aborted');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* istanbul ignore next */
|
|
109
|
+
if (envIsTrue(process.env.APP_DRY_RUN) === true) {
|
|
110
|
+
/* istanbul ignore next */
|
|
111
|
+
this.loggerService?.log('Dry run, exiting...');
|
|
112
|
+
/* istanbul ignore next */
|
|
113
|
+
await this.closeApp();
|
|
114
|
+
/* istanbul ignore next */
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async createApp(options?: {
|
|
122
|
+
createOptions?: INestCreateOptions;
|
|
123
|
+
fastifyInstance?: FastifyInstance;
|
|
124
|
+
}) {
|
|
125
|
+
this.fastifyInstance = options?.fastifyInstance ?? fastify();
|
|
126
|
+
|
|
127
|
+
let app;
|
|
128
|
+
try {
|
|
129
|
+
app = await NestFactory.create<NestFastifyApplication>(
|
|
130
|
+
this.module,
|
|
131
|
+
new FastifyAdapter(this.fastifyInstance as any),
|
|
132
|
+
{
|
|
133
|
+
bufferLogs: false,
|
|
134
|
+
abortOnError: options?.createOptions?.abortOnError ?? false,
|
|
135
|
+
logger: getEnvLoggerLevels(),
|
|
136
|
+
...(options?.createOptions ?? {}),
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
this.closeCleanup();
|
|
141
|
+
// eslint-disable-next-line no-console
|
|
142
|
+
console.error(clc.red('Failed to create app'), clc.red(err));
|
|
143
|
+
throw new Error('Process aborted');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.setApp(app);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* istanbul ignore next */
|
|
150
|
+
/* istanbul ignore next */
|
|
151
|
+
getFastifyInstance() {
|
|
152
|
+
return this.fastifyInstance;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* istanbul ignore next */
|
|
156
|
+
setSwaggerEnabled(enabled: boolean) {
|
|
157
|
+
this.isSwaggerEnabled = enabled;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async applyBootstrapGlobals(options?: ICreateOptions) {
|
|
161
|
+
await super.applyBootstrapGlobals(options);
|
|
162
|
+
|
|
163
|
+
this.getApp().useGlobalPipes(
|
|
164
|
+
new ValidationPipe({
|
|
165
|
+
transform: true,
|
|
166
|
+
transformOptions: { enableImplicitConversion: false },
|
|
167
|
+
validateCustomDecorators: true,
|
|
168
|
+
exceptionFactory: /* istanbul ignore next */ (errors) => {
|
|
169
|
+
/* istanbul ignore next */
|
|
170
|
+
const errorMessages: { [key: string]: any } = {};
|
|
171
|
+
/* istanbul ignore next */
|
|
172
|
+
errors.forEach((error) => {
|
|
173
|
+
errorMessages[error.property] = error;
|
|
174
|
+
});
|
|
175
|
+
/* istanbul ignore next */
|
|
176
|
+
return new BadRequestException(errorMessages);
|
|
177
|
+
},
|
|
178
|
+
...(options?.validationPipeOptions ?? {}),
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
this.getApp().setGlobalPrefix(
|
|
183
|
+
options?.apiPrefix ?? (this.getConf()?.apiPrefix || ''),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
await this.getApp().register(fastifyCookie as any, {});
|
|
187
|
+
|
|
188
|
+
this.getApp().useGlobalInterceptors(new UnwrapResultInterceptor());
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @todo refactor using a factory function to share with all services
|
|
192
|
+
*/
|
|
193
|
+
const filters = [
|
|
194
|
+
new SystemExceptionFilter(this.loggerService),
|
|
195
|
+
...(options?.filters ?? []),
|
|
196
|
+
];
|
|
197
|
+
this.getApp().useGlobalFilters(...filters);
|
|
198
|
+
|
|
199
|
+
if (options?.enableSwagger) {
|
|
200
|
+
const swaggerPath = sanitizeSwaggerPath(options.swaggerPath);
|
|
201
|
+
this.swaggerPath = swaggerPath;
|
|
202
|
+
this.setSwaggerEnabled(true);
|
|
203
|
+
const document = SwaggerModule.createDocument(
|
|
204
|
+
this.getApp(),
|
|
205
|
+
this.buildSwaggerConfig().build(),
|
|
206
|
+
);
|
|
207
|
+
SwaggerModule.setup(swaggerPath, this.getApp(), document, {
|
|
208
|
+
jsonDocumentUrl: `/${swaggerPath}/json`,
|
|
209
|
+
useGlobalPrefix: true,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
useContainer(this.getApp().select(this.getModule()), {
|
|
214
|
+
fallbackOnErrors: true,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
buildSwaggerConfig() {
|
|
221
|
+
return new DocumentBuilder()
|
|
222
|
+
.setTitle(this.appAlias)
|
|
223
|
+
.setDescription(`${this.appAlias} rest api`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* istanbul ignore next */
|
|
227
|
+
async listen(callback?: {
|
|
228
|
+
(port: number, host: string, domain: string): void;
|
|
229
|
+
}) {
|
|
230
|
+
const port = this.getConf()?.port || 0;
|
|
231
|
+
const host = this.getConf()?.host || '0.0.0.0';
|
|
232
|
+
let apiPrefix = this.getConf()?.apiPrefix;
|
|
233
|
+
apiPrefix = apiPrefix ? `/${apiPrefix}` : '';
|
|
234
|
+
const domain = this.getConf()?.domain || 'localhost';
|
|
235
|
+
await this.getApp().listen(port, host, async (_err, address) => {
|
|
236
|
+
// eslint-disable-next-line no-console
|
|
237
|
+
console.debug(`Server ${this.appAlias} listening on
|
|
238
|
+
http://localhost:${port}${apiPrefix}/
|
|
239
|
+
http://127.0.0.1:${port}${apiPrefix}/
|
|
240
|
+
http://${domain}:${port}${apiPrefix}/
|
|
241
|
+
${address}`);
|
|
242
|
+
|
|
243
|
+
// // eslint-disable-next-line no-console
|
|
244
|
+
// console.debug(`GraphQL ${this.appAlias} listening on
|
|
245
|
+
// http://localhost:${port}${apiPrefix}/graphql
|
|
246
|
+
// http://127.0.0.1:${port}${apiPrefix}/graphql
|
|
247
|
+
// http://${domain}:${port}${apiPrefix}/graphql`);
|
|
248
|
+
|
|
249
|
+
if (this.isSwaggerEnabled) {
|
|
250
|
+
// eslint-disable-next-line no-console
|
|
251
|
+
console.debug(`Swagger ${this.appAlias} listening on
|
|
252
|
+
http://localhost:${port}${apiPrefix}/${this.swaggerPath}
|
|
253
|
+
http://127.0.0.1:${port}${apiPrefix}/${this.swaggerPath}
|
|
254
|
+
http://${domain}:${port}${apiPrefix}/${this.swaggerPath}
|
|
255
|
+
${address}/${this.swaggerPath}
|
|
256
|
+
JSON: http://localhost:${port}${apiPrefix}/${this.swaggerPath}/json`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
callback?.(port, host, domain);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Hot reload @see https://docs.nestjs.com/recipes/hot-reload#hot-module-replacement-1
|
|
264
|
+
*/
|
|
265
|
+
const hmr = typeof module !== 'undefined' ? (module as any).hot : undefined;
|
|
266
|
+
if (hmr) {
|
|
267
|
+
// eslint-disable-next-line no-console
|
|
268
|
+
console.debug('Hot reload enabled. Reloading...');
|
|
269
|
+
hmr.accept();
|
|
270
|
+
hmr.dispose(() => this.closeApp());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* istanbul ignore next */
|
|
276
|
+
function sanitizeSwaggerPath(path?: string): string {
|
|
277
|
+
const normalized = (path ?? 'api').replace(/^\/+/, '').replace(/\/+$/, '');
|
|
278
|
+
return normalized || 'api';
|
|
279
|
+
}
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { Provider } from '@nestjs/common';
|
|
2
|
-
import { ConfigService } from '@nestjs/config';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* This service is used to access the app config registered with a specific app alias.
|
|
6
|
-
* This is useful when you want to access the config of the current app with the dependency injection and
|
|
7
|
-
* without specifying the app alias.
|
|
8
|
-
*/
|
|
9
|
-
export class AppConfigService<T = any> {
|
|
10
|
-
protected _config: T;
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
public readonly service: ConfigService,
|
|
14
|
-
protected readonly appAlias: string,
|
|
15
|
-
) {
|
|
16
|
-
const config = this.service.get<T>(this.appAlias);
|
|
17
|
-
if (!config) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
`AppConfigService: No config found for app alias '${this.appAlias}'`,
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
this._config = config;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Type-safe access to the app config values of the current app.
|
|
28
|
-
*/
|
|
29
|
-
get values(): T {
|
|
30
|
-
return this._config;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @deprecated - use .values instead
|
|
35
|
-
*/
|
|
36
|
-
get(): T {
|
|
37
|
-
return this.values;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function createAppConfigProvider(appAlias: string): Provider {
|
|
42
|
-
return {
|
|
43
|
-
provide: getAppConfigToken(appAlias),
|
|
44
|
-
useFactory: (config: ConfigService): AppConfigService => {
|
|
45
|
-
return new AppConfigService(config, appAlias);
|
|
46
|
-
},
|
|
47
|
-
inject: [ConfigService],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function getAppConfigToken(appAlias: string): string {
|
|
52
|
-
return `${appAlias}Config`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function getAppEventToken(appAlias: string): string {
|
|
56
|
-
return `${appAlias}Event`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function getAppLoggerToken(appAlias: string): string {
|
|
60
|
-
return `${appAlias}Logger`;
|
|
61
|
-
}
|
|
1
|
+
import { Provider } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This service is used to access the app config registered with a specific app alias.
|
|
6
|
+
* This is useful when you want to access the config of the current app with the dependency injection and
|
|
7
|
+
* without specifying the app alias.
|
|
8
|
+
*/
|
|
9
|
+
export class AppConfigService<T = any> {
|
|
10
|
+
protected _config: T;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
public readonly service: ConfigService,
|
|
14
|
+
protected readonly appAlias: string,
|
|
15
|
+
) {
|
|
16
|
+
const config = this.service.get<T>(this.appAlias);
|
|
17
|
+
if (!config) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`AppConfigService: No config found for app alias '${this.appAlias}'`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this._config = config;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Type-safe access to the app config values of the current app.
|
|
28
|
+
*/
|
|
29
|
+
get values(): T {
|
|
30
|
+
return this._config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated - use .values instead
|
|
35
|
+
*/
|
|
36
|
+
get(): T {
|
|
37
|
+
return this.values;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function createAppConfigProvider(appAlias: string): Provider {
|
|
42
|
+
return {
|
|
43
|
+
provide: getAppConfigToken(appAlias),
|
|
44
|
+
useFactory: (config: ConfigService): AppConfigService => {
|
|
45
|
+
return new AppConfigService(config, appAlias);
|
|
46
|
+
},
|
|
47
|
+
inject: [ConfigService],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getAppConfigToken(appAlias: string): string {
|
|
52
|
+
return `${appAlias}Config`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getAppEventToken(appAlias: string): string {
|
|
56
|
+
return `${appAlias}Event`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getAppLoggerToken(appAlias: string): string {
|
|
60
|
+
return `${appAlias}Logger`;
|
|
61
|
+
}
|