@rsdk/http.server 6.0.0-next.1 → 6.0.0-next.11
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/error-handling/index.d.ts +1 -1
- package/dist/error-handling/index.js +1 -1
- package/dist/error-handling/index.js.map +1 -1
- package/dist/error-handling/no-interception.filter.d.ts +19 -0
- package/dist/error-handling/no-interception.filter.js +52 -0
- package/dist/error-handling/no-interception.filter.js.map +1 -0
- package/dist/http.config.js +5 -5
- package/dist/http.config.js.map +1 -1
- package/dist/http.transport.d.ts +15 -14
- package/dist/http.transport.js +44 -24
- package/dist/http.transport.js.map +1 -1
- package/package.json +5 -2
- package/src/error-handling/index.ts +1 -1
- package/src/error-handling/no-interception.filter.ts +48 -0
- package/src/http.config.ts +4 -7
- package/src/http.transport.ts +59 -46
- package/dist/error-handling/http-errors.transformer.d.ts +0 -5
- package/dist/error-handling/http-errors.transformer.js +0 -17
- package/dist/error-handling/http-errors.transformer.js.map +0 -1
- package/src/error-handling/http-errors.transformer.ts +0 -17
|
@@ -16,5 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./http-errors.formatter"), exports);
|
|
18
18
|
__exportStar(require("./http-errors.sender"), exports);
|
|
19
|
-
__exportStar(require("./
|
|
19
|
+
__exportStar(require("./no-interception.filter"), exports);
|
|
20
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/error-handling/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,uDAAqC;AACrC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/error-handling/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,uDAAqC;AACrC,2DAAyC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ArgumentsHost, ExceptionFilter, NotFoundException as NestNotFoundException } from '@nestjs/common';
|
|
2
|
+
import { ILogger } from '@rsdk/logging';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
/**
|
|
5
|
+
* Специализированный фильтр для обработки ошибок NotFound.
|
|
6
|
+
* Имеет более высокий приоритет, чем GlobalExceptionsFilter,
|
|
7
|
+
* т. к. задуман для перехвата только NotFoundException. Её выбрасывает
|
|
8
|
+
* nest, когда не находит маршрут.
|
|
9
|
+
*
|
|
10
|
+
* К сожалению, эти ошибки не попадают в интерцепторы и, соответственно,
|
|
11
|
+
* приходится обрабатывать их вот так отдельно.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RouteNotFoundFilter implements ExceptionFilter {
|
|
14
|
+
private readonly logger;
|
|
15
|
+
private readonly formatter;
|
|
16
|
+
private readonly sender;
|
|
17
|
+
constructor(logger: ILogger);
|
|
18
|
+
catch(exception: NestNotFoundException, host: ArgumentsHost): Observable<any>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RouteNotFoundFilter = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const core_1 = require("@rsdk/core");
|
|
18
|
+
const http_errors_formatter_1 = require("./http-errors.formatter");
|
|
19
|
+
const http_errors_sender_1 = require("./http-errors.sender");
|
|
20
|
+
/**
|
|
21
|
+
* Специализированный фильтр для обработки ошибок NotFound.
|
|
22
|
+
* Имеет более высокий приоритет, чем GlobalExceptionsFilter,
|
|
23
|
+
* т. к. задуман для перехвата только NotFoundException. Её выбрасывает
|
|
24
|
+
* nest, когда не находит маршрут.
|
|
25
|
+
*
|
|
26
|
+
* К сожалению, эти ошибки не попадают в интерцепторы и, соответственно,
|
|
27
|
+
* приходится обрабатывать их вот так отдельно.
|
|
28
|
+
*/
|
|
29
|
+
let RouteNotFoundFilter = class RouteNotFoundFilter {
|
|
30
|
+
logger;
|
|
31
|
+
formatter = new http_errors_formatter_1.HttpErrorsFormatter();
|
|
32
|
+
sender = new http_errors_sender_1.HttpErrorsSender();
|
|
33
|
+
constructor(logger) {
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
}
|
|
36
|
+
catch(exception, host) {
|
|
37
|
+
const ctx = host.switchToHttp();
|
|
38
|
+
const { method, path } = ctx.getRequest();
|
|
39
|
+
const pipelineEx = new core_1.NotFoundException(exception.message);
|
|
40
|
+
// Логируем ошибку со всеми деталями
|
|
41
|
+
this.logger.error(`Route not found [HTTP] ${method} ${path}`);
|
|
42
|
+
const formattedEx = this.formatter.format(pipelineEx);
|
|
43
|
+
return this.sender.send(host, formattedEx);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.RouteNotFoundFilter = RouteNotFoundFilter;
|
|
47
|
+
exports.RouteNotFoundFilter = RouteNotFoundFilter = __decorate([
|
|
48
|
+
(0, common_1.Catch)(common_1.NotFoundException),
|
|
49
|
+
__param(0, (0, core_1.InjectLogger)(RouteNotFoundFilter)),
|
|
50
|
+
__metadata("design:paramtypes", [Object])
|
|
51
|
+
], RouteNotFoundFilter);
|
|
52
|
+
//# sourceMappingURL=no-interception.filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-interception.filter.js","sourceRoot":"","sources":["../../src/error-handling/no-interception.filter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAKwB;AACxB,qCAA6D;AAI7D,mEAA8D;AAC9D,6DAAwD;AAExD;;;;;;;;GAQG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAKwB;IAJrC,SAAS,GAAG,IAAI,2CAAmB,EAAE,CAAC;IACtC,MAAM,GAAG,IAAI,qCAAgB,EAAE,CAAC;IAEjD,YACsD,MAAe;QAAf,WAAM,GAAN,MAAM,CAAS;IAClE,CAAC;IAEJ,KAAK,CACH,SAAgC,EAChC,IAAmB;QAEnB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC;QAE1C,MAAM,UAAU,GAAG,IAAI,wBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAE5D,oCAAoC;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAE9D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEtD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7C,CAAC;CACF,CAAA;AAxBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,cAAK,EAAC,0BAAqB,CAAC;IAMxB,WAAA,IAAA,mBAAY,EAAC,mBAAmB,CAAC,CAAA;;GALzB,mBAAmB,CAwB/B"}
|
package/dist/http.config.js
CHANGED
|
@@ -20,14 +20,14 @@ let HttpConfig = class HttpConfig extends core_1.Config {
|
|
|
20
20
|
};
|
|
21
21
|
exports.HttpConfig = HttpConfig;
|
|
22
22
|
__decorate([
|
|
23
|
-
(0, core_1.Property)('HTTP_HOST', new
|
|
23
|
+
(0, core_1.Property)('HTTP_HOST', new common_1.StringParser(), {
|
|
24
24
|
defaultValue: '0.0.0.0',
|
|
25
25
|
description: 'HTTP host',
|
|
26
26
|
}),
|
|
27
27
|
__metadata("design:type", String)
|
|
28
28
|
], HttpConfig.prototype, "host", void 0);
|
|
29
29
|
__decorate([
|
|
30
|
-
(0, core_1.Property)('HTTP_PORT', new
|
|
30
|
+
(0, core_1.Property)('HTTP_PORT', new common_1.IntParser(), {
|
|
31
31
|
// eslint-disable-next-line unicorn/numeric-separators-style
|
|
32
32
|
defaultValue: 50050,
|
|
33
33
|
description: 'HTTP port',
|
|
@@ -35,16 +35,16 @@ __decorate([
|
|
|
35
35
|
__metadata("design:type", Number)
|
|
36
36
|
], HttpConfig.prototype, "port", void 0);
|
|
37
37
|
__decorate([
|
|
38
|
-
(0, core_1.Property)('HTTP_BODY_LIMIT', new
|
|
38
|
+
(0, core_1.Property)('HTTP_BODY_LIMIT', new common_1.SizeParser(), {
|
|
39
39
|
defaultValue: new common_1.Size(100, 'kb'),
|
|
40
40
|
description: 'Max HTTP body size',
|
|
41
41
|
}),
|
|
42
42
|
__metadata("design:type", common_1.Size)
|
|
43
43
|
], HttpConfig.prototype, "bodyLimit", void 0);
|
|
44
44
|
__decorate([
|
|
45
|
-
(0, core_1.Property)('HTTP_SWAGGER_UI_ENABLED', new
|
|
45
|
+
(0, core_1.Property)('HTTP_SWAGGER_UI_ENABLED', new common_1.BoolParser(), {
|
|
46
46
|
defaultValue: true,
|
|
47
|
-
description: 'Enable swagger UI on /swagger endpoint',
|
|
47
|
+
description: 'Enable swagger UI on /swagger (can be configured) endpoint',
|
|
48
48
|
}),
|
|
49
49
|
__metadata("design:type", Boolean)
|
|
50
50
|
], HttpConfig.prototype, "swaggerUI", void 0);
|
package/dist/http.config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.config.js","sourceRoot":"","sources":["../src/http.config.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"http.config.js","sourceRoot":"","sources":["../src/http.config.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAMsB;AACtB,qCAAwE;AAKjE,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,aAAM;IAK3B,IAAI,CAAU;IAOd,IAAI,CAAU;IAMd,SAAS,CAAQ;IAMjB,SAAS,CAAW;CAC9B,CAAA;AAzBY,gCAAU;AAKZ;IAJR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,qBAAY,EAAE,EAAE;QACzC,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAOd;IALR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,kBAAS,EAAE,EAAE;QACtC,4DAA4D;QAC5D,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAMd;IAJR,IAAA,eAAQ,EAAC,iBAAiB,EAAE,IAAI,mBAAU,EAAE,EAAE;QAC7C,YAAY,EAAE,IAAI,aAAI,CAAC,GAAG,EAAE,IAAI,CAAC;QACjC,WAAW,EAAE,oBAAoB;KAClC,CAAC;8BACmB,aAAI;6CAAC;AAMjB;IAJR,IAAA,eAAQ,EAAC,yBAAyB,EAAE,IAAI,mBAAU,EAAE,EAAE;QACrD,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,4DAA4D;KAC1E,CAAC;;6CAC2B;qBAxBlB,UAAU;IAHtB,IAAA,oBAAa,EAAC;QACb,IAAI,EAAE,CAAC,gBAAS,CAAC,cAAc,EAAE,gBAAS,CAAC,SAAS,EAAE,gBAAS,CAAC,IAAI,CAAC;KACtE,CAAC;GACW,UAAU,CAyBtB"}
|
package/dist/http.transport.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ExecutionContext } from '@nestjs/common';
|
|
2
2
|
import type { Controller, INestApplication } from '@nestjs/common/interfaces';
|
|
3
3
|
import type { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
|
|
4
|
-
import type
|
|
5
|
-
import { DocumentBuilder as OriginalDocumentBuilder } from '@nestjs/swagger';
|
|
4
|
+
import { type AbstractHttpAdapter } from '@nestjs/core';
|
|
6
5
|
import type { Constructor, DeepPartial } from '@rsdk/common';
|
|
7
|
-
import type { ConfigContext, GenericHeaders, HttpOptions, IErrorsFormatter, IErrorsSender, IErrorsTransformer, IHttpTransport, LogFormatter } from '@rsdk/core';
|
|
6
|
+
import type { ConfigContext, GenericHeaders, HttpOptions, IErrorsFormatter, IErrorsSender, IErrorsTransformer, IHttpTransport, LogFormatter, NestModuleDefinitions, PlatformContext } from '@rsdk/core';
|
|
7
|
+
import type { OpenApiOptions } from '@rsdk/http.openapi';
|
|
8
|
+
import type { ILogger } from '@rsdk/logging';
|
|
8
9
|
import { HttpConfig } from './http.config';
|
|
9
10
|
export interface ParsersConfig {
|
|
10
11
|
body: {
|
|
@@ -13,12 +14,12 @@ export interface ParsersConfig {
|
|
|
13
14
|
};
|
|
14
15
|
cookie: boolean;
|
|
15
16
|
}
|
|
16
|
-
export type
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
export type SwaggerOptions = Partial<OpenApiOptions> | {
|
|
18
|
+
fromFile: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function isFromFile(swagger: SwaggerOptions): swagger is {
|
|
21
|
+
fromFile: string;
|
|
22
|
+
};
|
|
22
23
|
export interface HttpTransportOptions {
|
|
23
24
|
/**
|
|
24
25
|
* CORS middleware options
|
|
@@ -30,7 +31,6 @@ export interface HttpTransportOptions {
|
|
|
30
31
|
globalPrefix?: string;
|
|
31
32
|
/**
|
|
32
33
|
* Configuration of body and cookie parsers
|
|
33
|
-
// NOTE: This fields is actually is handled in transport specific way
|
|
34
34
|
*/
|
|
35
35
|
parsers?: DeepPartial<ParsersConfig>;
|
|
36
36
|
/**
|
|
@@ -40,11 +40,11 @@ export interface HttpTransportOptions {
|
|
|
40
40
|
* NOTE: SwaggerUI is enabled or disabled by HTTP_SWAGGER_UI_ENABLED
|
|
41
41
|
* (true by default).
|
|
42
42
|
*/
|
|
43
|
-
swagger?:
|
|
43
|
+
swagger?: Record<string, SwaggerOptions>;
|
|
44
44
|
}
|
|
45
45
|
export declare abstract class AbstractHttpTransport implements IHttpTransport {
|
|
46
|
-
protected readonly options
|
|
47
|
-
constructor(options?: HttpTransportOptions
|
|
46
|
+
protected readonly options: HttpTransportOptions;
|
|
47
|
+
constructor(options?: HttpTransportOptions);
|
|
48
48
|
extractHeaders(ctx: ExecutionContext): GenericHeaders;
|
|
49
49
|
matchByContext(ctx: ExecutionContext): boolean;
|
|
50
50
|
errorFormatter(): IErrorsFormatter;
|
|
@@ -55,7 +55,8 @@ export declare abstract class AbstractHttpTransport implements IHttpTransport {
|
|
|
55
55
|
getMetricsController(): Constructor<Controller>;
|
|
56
56
|
createHttpOptions(configContext: ConfigContext): HttpOptions;
|
|
57
57
|
createAdapter(configContext: ConfigContext): AbstractHttpAdapter;
|
|
58
|
-
|
|
58
|
+
modules(): NestModuleDefinitions;
|
|
59
|
+
configureApp(app: INestApplication, context: PlatformContext, logger: ILogger): Promise<void>;
|
|
59
60
|
abstract createHttpAdapter(config: HttpConfig): AbstractHttpAdapter;
|
|
60
61
|
abstract logFormatter(): LogFormatter;
|
|
61
62
|
}
|
package/dist/http.transport.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AbstractHttpTransport = void 0;
|
|
4
|
+
exports.isFromFile = isFromFile;
|
|
5
|
+
const core_1 = require("@nestjs/core");
|
|
4
6
|
const swagger_1 = require("@nestjs/swagger");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
+
const common_node_1 = require("@rsdk/common.node");
|
|
8
|
+
const core_2 = require("@rsdk/core");
|
|
9
|
+
const http_openapi_1 = require("@rsdk/http.openapi");
|
|
10
|
+
const no_interception_filter_1 = require("./error-handling/no-interception.filter");
|
|
7
11
|
const controllers_1 = require("./controllers");
|
|
8
12
|
const error_handling_1 = require("./error-handling");
|
|
9
13
|
const http_config_1 = require("./http.config");
|
|
10
14
|
const http_headers_1 = require("./http.headers");
|
|
15
|
+
function isFromFile(swagger) {
|
|
16
|
+
return typeof swagger === 'object' && 'fromFile' in swagger;
|
|
17
|
+
}
|
|
11
18
|
class AbstractHttpTransport {
|
|
12
19
|
options;
|
|
13
|
-
constructor(options) {
|
|
20
|
+
constructor(options = {}) {
|
|
14
21
|
this.options = options;
|
|
15
22
|
}
|
|
16
23
|
extractHeaders(ctx) {
|
|
@@ -26,7 +33,7 @@ class AbstractHttpTransport {
|
|
|
26
33
|
return new error_handling_1.HttpErrorsSender();
|
|
27
34
|
}
|
|
28
35
|
errorTransformers() {
|
|
29
|
-
return [
|
|
36
|
+
return [];
|
|
30
37
|
}
|
|
31
38
|
getProtocol() {
|
|
32
39
|
return 'http';
|
|
@@ -39,38 +46,51 @@ class AbstractHttpTransport {
|
|
|
39
46
|
}
|
|
40
47
|
createHttpOptions(configContext) {
|
|
41
48
|
const { host, port } = configContext.resolve(http_config_1.HttpConfig);
|
|
42
|
-
return {
|
|
43
|
-
host,
|
|
44
|
-
port,
|
|
45
|
-
};
|
|
49
|
+
return { host, port };
|
|
46
50
|
}
|
|
47
51
|
createAdapter(configContext) {
|
|
48
52
|
return this.createHttpAdapter(configContext.resolve(http_config_1.HttpConfig));
|
|
49
53
|
}
|
|
50
|
-
|
|
54
|
+
modules() {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
module: no_interception_filter_1.RouteNotFoundFilter,
|
|
58
|
+
imports: [],
|
|
59
|
+
providers: [{ provide: core_1.APP_FILTER, useClass: no_interception_filter_1.RouteNotFoundFilter }],
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
async configureApp(app, context, logger) {
|
|
64
|
+
const configContext = await context.getConfigContext();
|
|
51
65
|
const { swaggerUI } = configContext.resolve(http_config_1.HttpConfig);
|
|
52
|
-
const { globalPrefix, swagger, cors } = this.options
|
|
66
|
+
const { globalPrefix, swagger, cors } = this.options;
|
|
53
67
|
if (globalPrefix) {
|
|
54
68
|
app.setGlobalPrefix(globalPrefix);
|
|
55
69
|
}
|
|
56
70
|
if (cors) {
|
|
57
71
|
app.enableCors(cors);
|
|
58
72
|
}
|
|
59
|
-
if (swaggerUI || swagger
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
swagger_1.SwaggerModule.setup(swagger?.url ?? '/swagger', app, spec);
|
|
73
|
+
if (!swaggerUI || !swagger) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const [url, config] of Object.entries(swagger)) {
|
|
77
|
+
if (isFromFile(config)) {
|
|
78
|
+
try {
|
|
79
|
+
const document = await (0, common_node_1.readObj)(config.fromFile);
|
|
80
|
+
swagger_1.SwaggerModule.setup(url, app, document);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
logger.warn(`Failed to load OpenAPI spec from ${config.fromFile}`);
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
73
86
|
}
|
|
87
|
+
const document = await new http_openapi_1.OpenApiGenerator(context, {
|
|
88
|
+
title: app.get(core_2.APP_NAME),
|
|
89
|
+
version: app.get(core_2.APP_VERSION),
|
|
90
|
+
description: app.get(core_2.APP_DESCRIPTION),
|
|
91
|
+
...config,
|
|
92
|
+
}).generate();
|
|
93
|
+
swagger_1.SwaggerModule.setup(url, app, document);
|
|
74
94
|
}
|
|
75
95
|
}
|
|
76
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.transport.js","sourceRoot":"","sources":["../src/http.transport.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"http.transport.js","sourceRoot":"","sources":["../src/http.transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsdk/http.server",
|
|
3
|
-
"version": "6.0.0-next.
|
|
3
|
+
"version": "6.0.0-next.11",
|
|
4
4
|
"description": "HTTP transport for rsdk apps (needs some of HTTP adapters)",
|
|
5
5
|
"license": "Apache License 2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -15,13 +15,16 @@
|
|
|
15
15
|
"@nestjs/core": "^10.0.0",
|
|
16
16
|
"@nestjs/swagger": "^7.0.0 || ^8.0.0",
|
|
17
17
|
"@rsdk/common": "*",
|
|
18
|
+
"@rsdk/common.node": "*",
|
|
18
19
|
"@rsdk/core": "*",
|
|
20
|
+
"@rsdk/http.openapi": "*",
|
|
19
21
|
"@rsdk/logging": "*",
|
|
20
22
|
"@rsdk/zones": "*",
|
|
21
23
|
"body-parser": "^1.20.1",
|
|
22
24
|
"cookie-parser": "^1.4.6",
|
|
25
|
+
"lodash": "^4.17.21",
|
|
23
26
|
"reflect-metadata": "^0.1.12 || ^0.2.0",
|
|
24
27
|
"rxjs": "^7.8.1"
|
|
25
28
|
},
|
|
26
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "e4a3d962f23e2c4ea39ec08e07628290f54812a6"
|
|
27
30
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArgumentsHost,
|
|
3
|
+
Catch,
|
|
4
|
+
ExceptionFilter,
|
|
5
|
+
NotFoundException as NestNotFoundException,
|
|
6
|
+
} from '@nestjs/common';
|
|
7
|
+
import { InjectLogger, NotFoundException } from '@rsdk/core';
|
|
8
|
+
import { ILogger } from '@rsdk/logging';
|
|
9
|
+
import { Observable } from 'rxjs';
|
|
10
|
+
|
|
11
|
+
import { HttpErrorsFormatter } from './http-errors.formatter';
|
|
12
|
+
import { HttpErrorsSender } from './http-errors.sender';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Специализированный фильтр для обработки ошибок NotFound.
|
|
16
|
+
* Имеет более высокий приоритет, чем GlobalExceptionsFilter,
|
|
17
|
+
* т. к. задуман для перехвата только NotFoundException. Её выбрасывает
|
|
18
|
+
* nest, когда не находит маршрут.
|
|
19
|
+
*
|
|
20
|
+
* К сожалению, эти ошибки не попадают в интерцепторы и, соответственно,
|
|
21
|
+
* приходится обрабатывать их вот так отдельно.
|
|
22
|
+
*/
|
|
23
|
+
@Catch(NestNotFoundException)
|
|
24
|
+
export class RouteNotFoundFilter implements ExceptionFilter {
|
|
25
|
+
private readonly formatter = new HttpErrorsFormatter();
|
|
26
|
+
private readonly sender = new HttpErrorsSender();
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
@InjectLogger(RouteNotFoundFilter) private readonly logger: ILogger,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
catch(
|
|
33
|
+
exception: NestNotFoundException,
|
|
34
|
+
host: ArgumentsHost,
|
|
35
|
+
): Observable<any> {
|
|
36
|
+
const ctx = host.switchToHttp();
|
|
37
|
+
const { method, path } = ctx.getRequest();
|
|
38
|
+
|
|
39
|
+
const pipelineEx = new NotFoundException(exception.message);
|
|
40
|
+
|
|
41
|
+
// Логируем ошибку со всеми деталями
|
|
42
|
+
this.logger.error(`Route not found [HTTP] ${method} ${path}`);
|
|
43
|
+
|
|
44
|
+
const formattedEx = this.formatter.format(pipelineEx);
|
|
45
|
+
|
|
46
|
+
return this.sender.send(host, formattedEx);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/http.config.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { Size } from '@rsdk/common';
|
|
2
1
|
import {
|
|
3
2
|
BoolParser,
|
|
4
|
-
Config,
|
|
5
|
-
ConfigSection,
|
|
6
|
-
ConfigTag,
|
|
7
3
|
IntParser,
|
|
8
|
-
|
|
4
|
+
Size,
|
|
9
5
|
SizeParser,
|
|
10
6
|
StringParser,
|
|
11
|
-
} from '@rsdk/
|
|
7
|
+
} from '@rsdk/common';
|
|
8
|
+
import { Config, ConfigSection, ConfigTag, Property } from '@rsdk/core';
|
|
12
9
|
|
|
13
10
|
@ConfigSection({
|
|
14
11
|
tags: [ConfigTag.infrastructure, ConfigTag.transport, ConfigTag.http],
|
|
@@ -35,7 +32,7 @@ export class HttpConfig extends Config {
|
|
|
35
32
|
|
|
36
33
|
@Property('HTTP_SWAGGER_UI_ENABLED', new BoolParser(), {
|
|
37
34
|
defaultValue: true,
|
|
38
|
-
description: 'Enable swagger UI on /swagger endpoint',
|
|
35
|
+
description: 'Enable swagger UI on /swagger (can be configured) endpoint',
|
|
39
36
|
})
|
|
40
37
|
readonly swaggerUI!: boolean;
|
|
41
38
|
}
|
package/src/http.transport.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type ExecutionContext } from '@nestjs/common';
|
|
2
2
|
import type { Controller, INestApplication } from '@nestjs/common/interfaces';
|
|
3
3
|
import type { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
|
|
4
|
-
import type
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
SwaggerModule,
|
|
8
|
-
} from '@nestjs/swagger';
|
|
4
|
+
import { type AbstractHttpAdapter, APP_FILTER } from '@nestjs/core';
|
|
5
|
+
import type { OpenAPIObject } from '@nestjs/swagger';
|
|
6
|
+
import { SwaggerModule } from '@nestjs/swagger';
|
|
9
7
|
import type { Constructor, DeepPartial } from '@rsdk/common';
|
|
8
|
+
import { readObj } from '@rsdk/common.node';
|
|
10
9
|
import type {
|
|
11
10
|
ConfigContext,
|
|
12
11
|
GenericHeaders,
|
|
@@ -16,16 +15,18 @@ import type {
|
|
|
16
15
|
IErrorsTransformer,
|
|
17
16
|
IHttpTransport,
|
|
18
17
|
LogFormatter,
|
|
18
|
+
NestModuleDefinitions,
|
|
19
|
+
PlatformContext,
|
|
19
20
|
} from '@rsdk/core';
|
|
20
21
|
import { APP_DESCRIPTION, APP_NAME, APP_VERSION } from '@rsdk/core';
|
|
21
|
-
import {
|
|
22
|
+
import type { OpenApiOptions } from '@rsdk/http.openapi';
|
|
23
|
+
import { OpenApiGenerator } from '@rsdk/http.openapi';
|
|
24
|
+
import type { ILogger } from '@rsdk/logging';
|
|
25
|
+
import _ from 'lodash';
|
|
22
26
|
|
|
27
|
+
import { RouteNotFoundFilter } from './error-handling/no-interception.filter';
|
|
23
28
|
import { HealthHttpController, MetricsHttpController } from './controllers';
|
|
24
|
-
import {
|
|
25
|
-
HttpErrorsFormatter,
|
|
26
|
-
HttpErrorsSender,
|
|
27
|
-
HttpErrorsTransformer,
|
|
28
|
-
} from './error-handling';
|
|
29
|
+
import { HttpErrorsFormatter, HttpErrorsSender } from './error-handling';
|
|
29
30
|
import { HttpConfig } from './http.config';
|
|
30
31
|
import { HttpHeaders } from './http.headers';
|
|
31
32
|
|
|
@@ -37,16 +38,12 @@ export interface ParsersConfig {
|
|
|
37
38
|
cookie: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
export type
|
|
41
|
-
OriginalDocumentBuilder,
|
|
42
|
-
'build' | 'setTitle' | 'setDescription' | 'setVersion'
|
|
43
|
-
>;
|
|
41
|
+
export type SwaggerOptions = Partial<OpenApiOptions> | { fromFile: string };
|
|
44
42
|
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
configure(builder: DocumentBuilder): void;
|
|
43
|
+
export function isFromFile(
|
|
44
|
+
swagger: SwaggerOptions,
|
|
45
|
+
): swagger is { fromFile: string } {
|
|
46
|
+
return typeof swagger === 'object' && 'fromFile' in swagger;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
export interface HttpTransportOptions {
|
|
@@ -62,7 +59,6 @@ export interface HttpTransportOptions {
|
|
|
62
59
|
|
|
63
60
|
/**
|
|
64
61
|
* Configuration of body and cookie parsers
|
|
65
|
-
// NOTE: This fields is actually is handled in transport specific way
|
|
66
62
|
*/
|
|
67
63
|
parsers?: DeepPartial<ParsersConfig>;
|
|
68
64
|
|
|
@@ -73,11 +69,11 @@ export interface HttpTransportOptions {
|
|
|
73
69
|
* NOTE: SwaggerUI is enabled or disabled by HTTP_SWAGGER_UI_ENABLED
|
|
74
70
|
* (true by default).
|
|
75
71
|
*/
|
|
76
|
-
swagger?:
|
|
72
|
+
swagger?: Record<string, SwaggerOptions>;
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
export abstract class AbstractHttpTransport implements IHttpTransport {
|
|
80
|
-
constructor(protected readonly options
|
|
76
|
+
constructor(protected readonly options: HttpTransportOptions = {}) {}
|
|
81
77
|
|
|
82
78
|
extractHeaders(ctx: ExecutionContext): GenericHeaders {
|
|
83
79
|
return new HttpHeaders(ctx);
|
|
@@ -96,7 +92,7 @@ export abstract class AbstractHttpTransport implements IHttpTransport {
|
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
errorTransformers(): IErrorsTransformer[] {
|
|
99
|
-
return [
|
|
95
|
+
return [];
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
getProtocol(): string {
|
|
@@ -114,48 +110,65 @@ export abstract class AbstractHttpTransport implements IHttpTransport {
|
|
|
114
110
|
createHttpOptions(configContext: ConfigContext): HttpOptions {
|
|
115
111
|
const { host, port } = configContext.resolve(HttpConfig);
|
|
116
112
|
|
|
117
|
-
return {
|
|
118
|
-
host,
|
|
119
|
-
port,
|
|
120
|
-
};
|
|
113
|
+
return { host, port };
|
|
121
114
|
}
|
|
122
115
|
|
|
123
116
|
createAdapter(configContext: ConfigContext): AbstractHttpAdapter {
|
|
124
117
|
return this.createHttpAdapter(configContext.resolve(HttpConfig));
|
|
125
118
|
}
|
|
126
119
|
|
|
127
|
-
|
|
120
|
+
modules(): NestModuleDefinitions {
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
module: RouteNotFoundFilter,
|
|
124
|
+
imports: [],
|
|
125
|
+
providers: [{ provide: APP_FILTER, useClass: RouteNotFoundFilter }],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async configureApp(
|
|
128
131
|
app: INestApplication,
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
context: PlatformContext,
|
|
133
|
+
logger: ILogger,
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
const configContext = await context.getConfigContext();
|
|
131
136
|
const { swaggerUI } = configContext.resolve(HttpConfig);
|
|
132
|
-
const { globalPrefix, swagger, cors } = this.options
|
|
137
|
+
const { globalPrefix, swagger, cors } = this.options;
|
|
138
|
+
|
|
133
139
|
if (globalPrefix) {
|
|
134
140
|
app.setGlobalPrefix(globalPrefix);
|
|
135
141
|
}
|
|
142
|
+
|
|
136
143
|
if (cors) {
|
|
137
144
|
app.enableCors(cors);
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
if (swaggerUI || swagger
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
.setVersion(app.get(APP_VERSION))
|
|
144
|
-
.setDescription(app.get(APP_DESCRIPTION));
|
|
147
|
+
if (!swaggerUI || !swagger) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
for (const [url, config] of Object.entries(swagger)) {
|
|
152
|
+
if (isFromFile(config)) {
|
|
153
|
+
try {
|
|
154
|
+
const document = await readObj<OpenAPIObject>(config.fromFile);
|
|
148
155
|
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
SwaggerModule.setup(url, app, document);
|
|
157
|
+
} catch {
|
|
158
|
+
logger.warn(`Failed to load OpenAPI spec from ${config.fromFile}`);
|
|
159
|
+
}
|
|
151
160
|
|
|
152
|
-
|
|
153
|
-
writeFileSync(swagger.file, JSON.stringify(spec));
|
|
161
|
+
continue;
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
const document = await new OpenApiGenerator(context, {
|
|
165
|
+
title: app.get(APP_NAME),
|
|
166
|
+
version: app.get(APP_VERSION),
|
|
167
|
+
description: app.get(APP_DESCRIPTION),
|
|
168
|
+
...config,
|
|
169
|
+
}).generate();
|
|
170
|
+
|
|
171
|
+
SwaggerModule.setup(url, app, document);
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HttpErrorsTransformer = void 0;
|
|
4
|
-
const common_1 = require("@nestjs/common");
|
|
5
|
-
const core_1 = require("@rsdk/core");
|
|
6
|
-
class HttpErrorsTransformer {
|
|
7
|
-
match(ex) {
|
|
8
|
-
return ex instanceof common_1.NotFoundException;
|
|
9
|
-
}
|
|
10
|
-
transform(ex) {
|
|
11
|
-
const err = new core_1.NotFoundException(ex.message);
|
|
12
|
-
err.stack = ex.stack;
|
|
13
|
-
return err;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
exports.HttpErrorsTransformer = HttpErrorsTransformer;
|
|
17
|
-
//# sourceMappingURL=http-errors.transformer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http-errors.transformer.js","sourceRoot":"","sources":["../../src/error-handling/http-errors.transformer.ts"],"names":[],"mappings":";;;AAAA,2CAA4E;AAE5E,qCAA+C;AAE/C,MAAa,qBAAqB;IAChC,KAAK,CAAC,EAAO;QACX,OAAO,EAAE,YAAY,0BAAqB,CAAC;IAC7C,CAAC;IAED,SAAS,CAAC,EAAO;QACf,MAAM,GAAG,GAAG,IAAI,wBAAiB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QAE9C,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;QAErB,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AAZD,sDAYC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { NotFoundException as NestNotFoundException } from '@nestjs/common';
|
|
2
|
-
import type { IErrorsTransformer, PipelineException } from '@rsdk/core';
|
|
3
|
-
import { NotFoundException } from '@rsdk/core';
|
|
4
|
-
|
|
5
|
-
export class HttpErrorsTransformer implements IErrorsTransformer {
|
|
6
|
-
match(ex: any): boolean {
|
|
7
|
-
return ex instanceof NestNotFoundException;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
transform(ex: any): PipelineException {
|
|
11
|
-
const err = new NotFoundException(ex.message);
|
|
12
|
-
|
|
13
|
-
err.stack = ex.stack;
|
|
14
|
-
|
|
15
|
-
return err;
|
|
16
|
-
}
|
|
17
|
-
}
|