@hazeljs/core 0.2.0-alpha.1
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/LICENSE +192 -0
- package/README.md +560 -0
- package/dist/__tests__/container.test.d.ts +2 -0
- package/dist/__tests__/container.test.d.ts.map +1 -0
- package/dist/__tests__/container.test.js +454 -0
- package/dist/__tests__/decorators.test.d.ts +2 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +1237 -0
- package/dist/__tests__/errors/http.error.test.d.ts +2 -0
- package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
- package/dist/__tests__/errors/http.error.test.js +117 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/exception-filter.test.js +135 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
- package/dist/__tests__/hazel-app.test.d.ts +2 -0
- package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-app.test.js +810 -0
- package/dist/__tests__/hazel-module.test.d.ts +2 -0
- package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-module.test.js +408 -0
- package/dist/__tests__/hazel-response.test.d.ts +2 -0
- package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-response.test.js +138 -0
- package/dist/__tests__/health.test.d.ts +2 -0
- package/dist/__tests__/health.test.d.ts.map +1 -0
- package/dist/__tests__/health.test.js +147 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +239 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
- package/dist/__tests__/interceptors/interceptor.test.js +166 -0
- package/dist/__tests__/logger.test.d.ts +2 -0
- package/dist/__tests__/logger.test.d.ts.map +1 -0
- package/dist/__tests__/logger.test.js +141 -0
- package/dist/__tests__/middleware/cors.test.d.ts +2 -0
- package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/cors.test.js +129 -0
- package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
- package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/csrf.test.js +247 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/global-middleware.test.js +259 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/rate-limit.test.js +264 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/security-headers.test.js +229 -0
- package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
- package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/timeout.test.js +132 -0
- package/dist/__tests__/middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware.test.js +180 -0
- package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/pipe.test.js +245 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
- package/dist/__tests__/request-parser.test.d.ts +2 -0
- package/dist/__tests__/request-parser.test.d.ts.map +1 -0
- package/dist/__tests__/request-parser.test.js +182 -0
- package/dist/__tests__/router.test.d.ts +2 -0
- package/dist/__tests__/router.test.d.ts.map +1 -0
- package/dist/__tests__/router.test.js +1183 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
- package/dist/__tests__/routing/route-matcher.test.js +219 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
- package/dist/__tests__/routing/version.decorator.test.js +298 -0
- package/dist/__tests__/service.test.d.ts +2 -0
- package/dist/__tests__/service.test.d.ts.map +1 -0
- package/dist/__tests__/service.test.js +121 -0
- package/dist/__tests__/shutdown.test.d.ts +2 -0
- package/dist/__tests__/shutdown.test.d.ts.map +1 -0
- package/dist/__tests__/shutdown.test.js +250 -0
- package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
- package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
- package/dist/__tests__/testing/testing.module.test.js +370 -0
- package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
- package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
- package/dist/__tests__/upload/file-upload.test.js +498 -0
- package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
- package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/utils/sanitize.test.js +291 -0
- package/dist/__tests__/validator.test.d.ts +2 -0
- package/dist/__tests__/validator.test.d.ts.map +1 -0
- package/dist/__tests__/validator.test.js +300 -0
- package/dist/container.d.ts +80 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +271 -0
- package/dist/decorators.d.ts +166 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +538 -0
- package/dist/errors/http.error.d.ts +34 -0
- package/dist/errors/http.error.d.ts.map +1 -0
- package/dist/errors/http.error.js +69 -0
- package/dist/filters/exception-filter.d.ts +39 -0
- package/dist/filters/exception-filter.d.ts.map +1 -0
- package/dist/filters/exception-filter.js +38 -0
- package/dist/filters/http-exception.filter.d.ts +9 -0
- package/dist/filters/http-exception.filter.d.ts.map +1 -0
- package/dist/filters/http-exception.filter.js +42 -0
- package/dist/hazel-app.d.ts +94 -0
- package/dist/hazel-app.d.ts.map +1 -0
- package/dist/hazel-app.js +516 -0
- package/dist/hazel-module.d.ts +29 -0
- package/dist/hazel-module.d.ts.map +1 -0
- package/dist/hazel-module.js +137 -0
- package/dist/hazel-response.d.ts +25 -0
- package/dist/hazel-response.d.ts.map +1 -0
- package/dist/hazel-response.js +89 -0
- package/dist/health.d.ts +73 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +174 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/interceptors/interceptor.d.ts +30 -0
- package/dist/interceptors/interceptor.d.ts.map +1 -0
- package/dist/interceptors/interceptor.js +71 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +261 -0
- package/dist/middleware/cors.middleware.d.ts +44 -0
- package/dist/middleware/cors.middleware.d.ts.map +1 -0
- package/dist/middleware/cors.middleware.js +118 -0
- package/dist/middleware/csrf.middleware.d.ts +82 -0
- package/dist/middleware/csrf.middleware.d.ts.map +1 -0
- package/dist/middleware/csrf.middleware.js +183 -0
- package/dist/middleware/global-middleware.d.ts +111 -0
- package/dist/middleware/global-middleware.d.ts.map +1 -0
- package/dist/middleware/global-middleware.js +179 -0
- package/dist/middleware/rate-limit.middleware.d.ts +73 -0
- package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
- package/dist/middleware/rate-limit.middleware.js +124 -0
- package/dist/middleware/security-headers.middleware.d.ts +76 -0
- package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
- package/dist/middleware/security-headers.middleware.js +123 -0
- package/dist/middleware/timeout.middleware.d.ts +25 -0
- package/dist/middleware/timeout.middleware.d.ts.map +1 -0
- package/dist/middleware/timeout.middleware.js +74 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +47 -0
- package/dist/pipes/pipe.d.ts +50 -0
- package/dist/pipes/pipe.d.ts.map +1 -0
- package/dist/pipes/pipe.js +96 -0
- package/dist/pipes/validation.pipe.d.ts +6 -0
- package/dist/pipes/validation.pipe.d.ts.map +1 -0
- package/dist/pipes/validation.pipe.js +61 -0
- package/dist/request-context.d.ts +22 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +2 -0
- package/dist/request-parser.d.ts +7 -0
- package/dist/request-parser.d.ts.map +1 -0
- package/dist/request-parser.js +60 -0
- package/dist/router.d.ts +33 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +506 -0
- package/dist/routing/route-matcher.d.ts +39 -0
- package/dist/routing/route-matcher.d.ts.map +1 -0
- package/dist/routing/route-matcher.js +93 -0
- package/dist/routing/version.decorator.d.ts +36 -0
- package/dist/routing/version.decorator.d.ts.map +1 -0
- package/dist/routing/version.decorator.js +89 -0
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +39 -0
- package/dist/shutdown.d.ts +32 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +109 -0
- package/dist/testing/testing.module.d.ts +83 -0
- package/dist/testing/testing.module.d.ts.map +1 -0
- package/dist/testing/testing.module.js +164 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/upload/file-upload.d.ts +75 -0
- package/dist/upload/file-upload.d.ts.map +1 -0
- package/dist/upload/file-upload.js +261 -0
- package/dist/utils/sanitize.d.ts +45 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +165 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +119 -0
- package/package.json +67 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CORS Middleware
|
|
4
|
+
* Handles Cross-Origin Resource Sharing
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CorsMiddleware = void 0;
|
|
8
|
+
exports.enableCors = enableCors;
|
|
9
|
+
class CorsMiddleware {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.options = {
|
|
12
|
+
origin: options.origin || '*',
|
|
13
|
+
methods: options.methods || ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
|
|
14
|
+
allowedHeaders: options.allowedHeaders || ['Content-Type', 'Authorization'],
|
|
15
|
+
exposedHeaders: options.exposedHeaders || [],
|
|
16
|
+
credentials: options.credentials || false,
|
|
17
|
+
maxAge: options.maxAge || 86400, // 24 hours
|
|
18
|
+
preflightContinue: options.preflightContinue || false,
|
|
19
|
+
optionsSuccessStatus: options.optionsSuccessStatus || 204,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if origin is allowed
|
|
24
|
+
*/
|
|
25
|
+
isOriginAllowed(origin) {
|
|
26
|
+
const { origin: allowedOrigin } = this.options;
|
|
27
|
+
if (allowedOrigin === '*') {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (typeof allowedOrigin === 'string') {
|
|
31
|
+
return origin === allowedOrigin;
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(allowedOrigin)) {
|
|
34
|
+
return allowedOrigin.includes(origin);
|
|
35
|
+
}
|
|
36
|
+
if (typeof allowedOrigin === 'function') {
|
|
37
|
+
return allowedOrigin(origin);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Set CORS headers
|
|
43
|
+
*/
|
|
44
|
+
setCorsHeaders(req, res) {
|
|
45
|
+
const origin = req.headers?.origin || req.headers?.referer || '';
|
|
46
|
+
// Set Access-Control-Allow-Origin
|
|
47
|
+
if (this.options.origin === '*') {
|
|
48
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
49
|
+
}
|
|
50
|
+
else if (this.isOriginAllowed(origin)) {
|
|
51
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
52
|
+
res.setHeader('Vary', 'Origin');
|
|
53
|
+
}
|
|
54
|
+
// Set Access-Control-Allow-Credentials
|
|
55
|
+
if (this.options.credentials) {
|
|
56
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
57
|
+
}
|
|
58
|
+
// Set Access-Control-Expose-Headers
|
|
59
|
+
if (this.options.exposedHeaders.length > 0) {
|
|
60
|
+
res.setHeader('Access-Control-Expose-Headers', this.options.exposedHeaders.join(', '));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Handle preflight request
|
|
65
|
+
*/
|
|
66
|
+
handlePreflight(req, res) {
|
|
67
|
+
if (req.method !== 'OPTIONS') {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Set CORS headers
|
|
71
|
+
this.setCorsHeaders(req, res);
|
|
72
|
+
// Set Access-Control-Allow-Methods
|
|
73
|
+
res.setHeader('Access-Control-Allow-Methods', this.options.methods.join(', '));
|
|
74
|
+
// Set Access-Control-Allow-Headers
|
|
75
|
+
const requestHeaders = req.headers?.['access-control-request-headers'];
|
|
76
|
+
if (requestHeaders) {
|
|
77
|
+
res.setHeader('Access-Control-Allow-Headers', requestHeaders);
|
|
78
|
+
}
|
|
79
|
+
else if (this.options.allowedHeaders.length > 0) {
|
|
80
|
+
res.setHeader('Access-Control-Allow-Headers', this.options.allowedHeaders.join(', '));
|
|
81
|
+
}
|
|
82
|
+
// Set Access-Control-Max-Age
|
|
83
|
+
res.setHeader('Access-Control-Max-Age', this.options.maxAge.toString());
|
|
84
|
+
// Send response
|
|
85
|
+
if (!this.options.preflightContinue) {
|
|
86
|
+
res.status(this.options.optionsSuccessStatus).end();
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Handle CORS request
|
|
93
|
+
*/
|
|
94
|
+
handle(req, res, next) {
|
|
95
|
+
// Handle preflight request
|
|
96
|
+
if (this.handlePreflight(req, res)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Set CORS headers for actual request
|
|
100
|
+
this.setCorsHeaders(req, res);
|
|
101
|
+
// Continue to next middleware
|
|
102
|
+
next();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create middleware function
|
|
106
|
+
*/
|
|
107
|
+
static create(options) {
|
|
108
|
+
const middleware = new CorsMiddleware(options);
|
|
109
|
+
return (req, res, next) => middleware.handle(req, res, next);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.CorsMiddleware = CorsMiddleware;
|
|
113
|
+
/**
|
|
114
|
+
* Simple CORS helper for common use cases
|
|
115
|
+
*/
|
|
116
|
+
function enableCors(options) {
|
|
117
|
+
return CorsMiddleware.create(options);
|
|
118
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Request, Response } from '../types';
|
|
2
|
+
import { MiddlewareClass, NextFunction } from './global-middleware';
|
|
3
|
+
/**
|
|
4
|
+
* CSRF protection options
|
|
5
|
+
*/
|
|
6
|
+
export interface CsrfOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Cookie name for CSRF token
|
|
9
|
+
*/
|
|
10
|
+
cookieName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Header name for CSRF token
|
|
13
|
+
*/
|
|
14
|
+
headerName?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Secret key for token generation
|
|
17
|
+
*/
|
|
18
|
+
secret?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Cookie options
|
|
21
|
+
*/
|
|
22
|
+
cookieOptions?: {
|
|
23
|
+
httpOnly?: boolean;
|
|
24
|
+
secure?: boolean;
|
|
25
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
26
|
+
path?: string;
|
|
27
|
+
domain?: string;
|
|
28
|
+
maxAge?: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Methods to protect (default: POST, PUT, PATCH, DELETE)
|
|
32
|
+
*/
|
|
33
|
+
methods?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Paths to exclude from CSRF protection
|
|
36
|
+
*/
|
|
37
|
+
excludePaths?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* Token length in bytes
|
|
40
|
+
*/
|
|
41
|
+
tokenLength?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* CSRF Protection Middleware
|
|
45
|
+
* Protects against Cross-Site Request Forgery attacks
|
|
46
|
+
*/
|
|
47
|
+
export declare class CsrfMiddleware implements MiddlewareClass {
|
|
48
|
+
private options;
|
|
49
|
+
private secret;
|
|
50
|
+
private tokenStore;
|
|
51
|
+
constructor(options?: CsrfOptions);
|
|
52
|
+
use(req: Request, res: Response, next: NextFunction): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get or create CSRF token
|
|
55
|
+
*/
|
|
56
|
+
private getOrCreateToken;
|
|
57
|
+
/**
|
|
58
|
+
* Generate CSRF token
|
|
59
|
+
*/
|
|
60
|
+
private generateToken;
|
|
61
|
+
/**
|
|
62
|
+
* Verify CSRF token
|
|
63
|
+
*/
|
|
64
|
+
private verifyToken;
|
|
65
|
+
/**
|
|
66
|
+
* Get token from request
|
|
67
|
+
*/
|
|
68
|
+
private getTokenFromRequest;
|
|
69
|
+
/**
|
|
70
|
+
* Get session ID from request
|
|
71
|
+
*/
|
|
72
|
+
private getSessionId;
|
|
73
|
+
/**
|
|
74
|
+
* Build cookie string
|
|
75
|
+
*/
|
|
76
|
+
private buildCookie;
|
|
77
|
+
/**
|
|
78
|
+
* Cleanup expired tokens
|
|
79
|
+
*/
|
|
80
|
+
private cleanupTokens;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=csrf.middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/csrf.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKpE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,aAAa,CAAC,EAAE;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;QACrC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IAIxC,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAkC;gBAEhC,OAAO,GAAE,WAAgB;IAyB7C,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;IA4B1D;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAQrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,aAAa;CAYtB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CsrfMiddleware = void 0;
|
|
7
|
+
const http_error_1 = require("../errors/http.error");
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
10
|
+
/**
|
|
11
|
+
* CSRF Protection Middleware
|
|
12
|
+
* Protects against Cross-Site Request Forgery attacks
|
|
13
|
+
*/
|
|
14
|
+
class CsrfMiddleware {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.tokenStore = new Map();
|
|
18
|
+
this.options = {
|
|
19
|
+
cookieName: '_csrf',
|
|
20
|
+
headerName: 'x-csrf-token',
|
|
21
|
+
secret: process.env.CSRF_SECRET || (0, crypto_1.randomBytes)(32).toString('hex'),
|
|
22
|
+
cookieOptions: {
|
|
23
|
+
httpOnly: true,
|
|
24
|
+
secure: process.env.NODE_ENV === 'production',
|
|
25
|
+
sameSite: 'strict',
|
|
26
|
+
path: '/',
|
|
27
|
+
maxAge: 3600, // 1 hour
|
|
28
|
+
},
|
|
29
|
+
methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
|
|
30
|
+
tokenLength: 32,
|
|
31
|
+
...options,
|
|
32
|
+
};
|
|
33
|
+
this.secret = this.options.secret;
|
|
34
|
+
// Cleanup expired tokens every 5 minutes
|
|
35
|
+
setInterval(() => {
|
|
36
|
+
this.cleanupTokens();
|
|
37
|
+
}, 300000);
|
|
38
|
+
}
|
|
39
|
+
use(req, res, next) {
|
|
40
|
+
const path = req.url?.split('?')[0] || '/';
|
|
41
|
+
// Skip excluded paths
|
|
42
|
+
if (this.options.excludePaths?.some(excluded => path.startsWith(excluded))) {
|
|
43
|
+
next();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Generate or retrieve token
|
|
47
|
+
const token = this.getOrCreateToken(req, res);
|
|
48
|
+
// Check if method requires CSRF protection
|
|
49
|
+
if (this.options.methods?.includes(req.method || '')) {
|
|
50
|
+
const providedToken = this.getTokenFromRequest(req);
|
|
51
|
+
if (!providedToken || !this.verifyToken(token, providedToken)) {
|
|
52
|
+
logger_1.default.warn(`CSRF token validation failed for ${req.method} ${path}`);
|
|
53
|
+
throw new http_error_1.HttpError(403, 'Invalid CSRF token');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Add token to response for forms
|
|
57
|
+
res.setHeader('X-CSRF-Token', token);
|
|
58
|
+
next();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get or create CSRF token
|
|
62
|
+
*/
|
|
63
|
+
getOrCreateToken(req, res) {
|
|
64
|
+
const sessionId = this.getSessionId(req);
|
|
65
|
+
let token = this.tokenStore.get(sessionId);
|
|
66
|
+
if (!token) {
|
|
67
|
+
token = this.generateToken();
|
|
68
|
+
this.tokenStore.set(sessionId, token);
|
|
69
|
+
}
|
|
70
|
+
// Set cookie
|
|
71
|
+
const cookie = this.buildCookie(token);
|
|
72
|
+
res.setHeader('Set-Cookie', cookie);
|
|
73
|
+
return token;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate CSRF token
|
|
77
|
+
*/
|
|
78
|
+
generateToken() {
|
|
79
|
+
const random = (0, crypto_1.randomBytes)(this.options.tokenLength || 32).toString('hex');
|
|
80
|
+
const hmac = (0, crypto_1.createHmac)('sha256', this.secret);
|
|
81
|
+
hmac.update(random);
|
|
82
|
+
const signature = hmac.digest('hex');
|
|
83
|
+
return `${random}.${signature}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Verify CSRF token
|
|
87
|
+
*/
|
|
88
|
+
verifyToken(storedToken, providedToken) {
|
|
89
|
+
if (!storedToken || !providedToken)
|
|
90
|
+
return false;
|
|
91
|
+
if (storedToken === providedToken)
|
|
92
|
+
return true;
|
|
93
|
+
// Verify signature
|
|
94
|
+
const [random, signature] = providedToken.split('.');
|
|
95
|
+
if (!random || !signature)
|
|
96
|
+
return false;
|
|
97
|
+
const hmac = (0, crypto_1.createHmac)('sha256', this.secret);
|
|
98
|
+
hmac.update(random);
|
|
99
|
+
const expectedSignature = hmac.digest('hex');
|
|
100
|
+
return signature === expectedSignature;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get token from request
|
|
104
|
+
*/
|
|
105
|
+
getTokenFromRequest(req) {
|
|
106
|
+
// Try header first
|
|
107
|
+
if (req.headers) {
|
|
108
|
+
const headerToken = req.headers[this.options.headerName.toLowerCase()];
|
|
109
|
+
if (headerToken) {
|
|
110
|
+
return Array.isArray(headerToken) ? headerToken[0] : headerToken;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Try body
|
|
114
|
+
if (req.body && typeof req.body === 'object' && this.options.cookieName in req.body) {
|
|
115
|
+
const bodyValue = req.body[this.options.cookieName];
|
|
116
|
+
return typeof bodyValue === 'string' ? bodyValue : null;
|
|
117
|
+
}
|
|
118
|
+
// Try query parameter
|
|
119
|
+
if (req.query && typeof req.query === 'object' && this.options.cookieName in req.query) {
|
|
120
|
+
const queryValue = req.query[this.options.cookieName];
|
|
121
|
+
return typeof queryValue === 'string' ? queryValue : null;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get session ID from request
|
|
127
|
+
*/
|
|
128
|
+
getSessionId(req) {
|
|
129
|
+
// Try to get from cookie
|
|
130
|
+
if (req.headers?.cookie) {
|
|
131
|
+
const sessionCookie = req.headers.cookie
|
|
132
|
+
.split(';')
|
|
133
|
+
.find(c => c.trim().startsWith('sessionId='));
|
|
134
|
+
if (sessionCookie) {
|
|
135
|
+
return sessionCookie.split('=')[1].trim();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Fallback to IP + User-Agent hash
|
|
139
|
+
const ip = req.socket?.remoteAddress || 'unknown';
|
|
140
|
+
const ua = req.headers?.['user-agent'] || 'unknown';
|
|
141
|
+
const hash = (0, crypto_1.createHmac)('sha256', this.secret)
|
|
142
|
+
.update(`${ip}:${ua}`)
|
|
143
|
+
.digest('hex')
|
|
144
|
+
.substring(0, 16);
|
|
145
|
+
return hash;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build cookie string
|
|
149
|
+
*/
|
|
150
|
+
buildCookie(token) {
|
|
151
|
+
const opts = this.options.cookieOptions;
|
|
152
|
+
const parts = [`${this.options.cookieName}=${token}`];
|
|
153
|
+
if (opts.path)
|
|
154
|
+
parts.push(`Path=${opts.path}`);
|
|
155
|
+
if (opts.domain)
|
|
156
|
+
parts.push(`Domain=${opts.domain}`);
|
|
157
|
+
if (opts.maxAge)
|
|
158
|
+
parts.push(`Max-Age=${opts.maxAge}`);
|
|
159
|
+
if (opts.httpOnly)
|
|
160
|
+
parts.push('HttpOnly');
|
|
161
|
+
if (opts.secure)
|
|
162
|
+
parts.push('Secure');
|
|
163
|
+
if (opts.sameSite)
|
|
164
|
+
parts.push(`SameSite=${opts.sameSite}`);
|
|
165
|
+
return parts.join('; ');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Cleanup expired tokens
|
|
169
|
+
*/
|
|
170
|
+
cleanupTokens() {
|
|
171
|
+
// Simple cleanup - remove old entries
|
|
172
|
+
// In production, use a proper session store with TTL
|
|
173
|
+
if (this.tokenStore.size > 10000) {
|
|
174
|
+
const entries = Array.from(this.tokenStore.entries());
|
|
175
|
+
this.tokenStore.clear();
|
|
176
|
+
// Keep last 5000 entries
|
|
177
|
+
entries.slice(-5000).forEach(([key, value]) => {
|
|
178
|
+
this.tokenStore.set(key, value);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.CsrfMiddleware = CsrfMiddleware;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Request, Response } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Next function type for middleware chain
|
|
4
|
+
*/
|
|
5
|
+
export type NextFunction = () => Promise<void> | void;
|
|
6
|
+
/**
|
|
7
|
+
* Middleware function type
|
|
8
|
+
*/
|
|
9
|
+
export type MiddlewareFunction = (req: Request, res: Response, next: NextFunction) => Promise<void> | void;
|
|
10
|
+
/**
|
|
11
|
+
* Middleware interface
|
|
12
|
+
*/
|
|
13
|
+
export interface MiddlewareConsumer {
|
|
14
|
+
apply(...middleware: Array<MiddlewareFunction | MiddlewareClass>): MiddlewareConfigProxy;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Middleware class interface
|
|
18
|
+
*/
|
|
19
|
+
export interface MiddlewareClass {
|
|
20
|
+
use(req: Request, res: Response, next: NextFunction): Promise<void> | void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Middleware configuration proxy
|
|
24
|
+
*/
|
|
25
|
+
export interface MiddlewareConfigProxy {
|
|
26
|
+
forRoutes(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
|
|
27
|
+
exclude(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Route information
|
|
31
|
+
*/
|
|
32
|
+
export interface RouteInfo {
|
|
33
|
+
path: string;
|
|
34
|
+
method?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Global middleware manager
|
|
38
|
+
*/
|
|
39
|
+
export declare class GlobalMiddlewareManager {
|
|
40
|
+
private middleware;
|
|
41
|
+
/**
|
|
42
|
+
* Add global middleware
|
|
43
|
+
*/
|
|
44
|
+
use(middleware: MiddlewareFunction | MiddlewareClass): void;
|
|
45
|
+
/**
|
|
46
|
+
* Add middleware for specific routes
|
|
47
|
+
*/
|
|
48
|
+
useFor(middleware: MiddlewareFunction | MiddlewareClass, routes: Array<string | RouteInfo>): void;
|
|
49
|
+
/**
|
|
50
|
+
* Add middleware with exclusions
|
|
51
|
+
*/
|
|
52
|
+
useExcept(middleware: MiddlewareFunction | MiddlewareClass, excludedRoutes: Array<string | RouteInfo>): void;
|
|
53
|
+
/**
|
|
54
|
+
* Execute middleware chain for a request
|
|
55
|
+
*/
|
|
56
|
+
execute(req: Request, res: Response): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Get middleware applicable to a request
|
|
59
|
+
*/
|
|
60
|
+
private getApplicableMiddleware;
|
|
61
|
+
/**
|
|
62
|
+
* Check if request matches any route
|
|
63
|
+
*/
|
|
64
|
+
private matchesAnyRoute;
|
|
65
|
+
/**
|
|
66
|
+
* Match path with pattern
|
|
67
|
+
*/
|
|
68
|
+
private matchPath;
|
|
69
|
+
/**
|
|
70
|
+
* Normalize routes to RouteInfo array
|
|
71
|
+
*/
|
|
72
|
+
private normalizeRoutes;
|
|
73
|
+
/**
|
|
74
|
+
* Clear all middleware
|
|
75
|
+
*/
|
|
76
|
+
clear(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Get all registered middleware
|
|
79
|
+
*/
|
|
80
|
+
getMiddleware(): MiddlewareEntry[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Middleware entry
|
|
84
|
+
*/
|
|
85
|
+
interface MiddlewareEntry {
|
|
86
|
+
handler: MiddlewareFunction | MiddlewareClass;
|
|
87
|
+
routes: RouteInfo[];
|
|
88
|
+
excludedRoutes: RouteInfo[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Built-in CORS middleware
|
|
92
|
+
*/
|
|
93
|
+
export declare class CorsMiddleware implements MiddlewareClass {
|
|
94
|
+
private options;
|
|
95
|
+
constructor(options?: CorsOptions);
|
|
96
|
+
use(req: Request, res: Response, next: NextFunction): void;
|
|
97
|
+
}
|
|
98
|
+
export interface CorsOptions {
|
|
99
|
+
origin?: string;
|
|
100
|
+
methods?: string[];
|
|
101
|
+
allowedHeaders?: string[];
|
|
102
|
+
credentials?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Built-in logging middleware
|
|
106
|
+
*/
|
|
107
|
+
export declare class LoggerMiddleware implements MiddlewareClass {
|
|
108
|
+
use(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
export {};
|
|
111
|
+
//# sourceMappingURL=global-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/global-middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LoggerMiddleware = exports.CorsMiddleware = exports.GlobalMiddlewareManager = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
8
|
+
/**
|
|
9
|
+
* Global middleware manager
|
|
10
|
+
*/
|
|
11
|
+
class GlobalMiddlewareManager {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.middleware = [];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Add global middleware
|
|
17
|
+
*/
|
|
18
|
+
use(middleware) {
|
|
19
|
+
logger_1.default.info('Registering global middleware');
|
|
20
|
+
this.middleware.push({
|
|
21
|
+
handler: middleware,
|
|
22
|
+
routes: [],
|
|
23
|
+
excludedRoutes: [],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Add middleware for specific routes
|
|
28
|
+
*/
|
|
29
|
+
useFor(middleware, routes) {
|
|
30
|
+
logger_1.default.info('Registering route-specific middleware');
|
|
31
|
+
this.middleware.push({
|
|
32
|
+
handler: middleware,
|
|
33
|
+
routes: this.normalizeRoutes(routes),
|
|
34
|
+
excludedRoutes: [],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Add middleware with exclusions
|
|
39
|
+
*/
|
|
40
|
+
useExcept(middleware, excludedRoutes) {
|
|
41
|
+
logger_1.default.info('Registering middleware with exclusions');
|
|
42
|
+
this.middleware.push({
|
|
43
|
+
handler: middleware,
|
|
44
|
+
routes: [],
|
|
45
|
+
excludedRoutes: this.normalizeRoutes(excludedRoutes),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Execute middleware chain for a request
|
|
50
|
+
*/
|
|
51
|
+
async execute(req, res) {
|
|
52
|
+
const applicableMiddleware = this.getApplicableMiddleware(req);
|
|
53
|
+
let index = 0;
|
|
54
|
+
const next = async () => {
|
|
55
|
+
if (index >= applicableMiddleware.length) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const entry = applicableMiddleware[index++];
|
|
59
|
+
const handler = entry.handler;
|
|
60
|
+
try {
|
|
61
|
+
if (typeof handler === 'function') {
|
|
62
|
+
await handler(req, res, next);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await handler.use(req, res, next);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
logger_1.default.error('Middleware error:', error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
await next();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get middleware applicable to a request
|
|
77
|
+
*/
|
|
78
|
+
getApplicableMiddleware(req) {
|
|
79
|
+
return this.middleware.filter((entry) => {
|
|
80
|
+
// Check if route is excluded
|
|
81
|
+
if (this.matchesAnyRoute(req, entry.excludedRoutes)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// If no specific routes, apply to all
|
|
85
|
+
if (entry.routes.length === 0) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
// Check if route matches
|
|
89
|
+
return this.matchesAnyRoute(req, entry.routes);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if request matches any route
|
|
94
|
+
*/
|
|
95
|
+
matchesAnyRoute(req, routes) {
|
|
96
|
+
const requestPath = req.url?.split('?')[0] || '/';
|
|
97
|
+
const requestMethod = req.method || 'GET';
|
|
98
|
+
return routes.some((route) => {
|
|
99
|
+
const methodMatches = !route.method || route.method === requestMethod;
|
|
100
|
+
const pathMatches = this.matchPath(requestPath, route.path);
|
|
101
|
+
return methodMatches && pathMatches;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Match path with pattern
|
|
106
|
+
*/
|
|
107
|
+
matchPath(path, pattern) {
|
|
108
|
+
// Simple wildcard matching
|
|
109
|
+
if (pattern === '*')
|
|
110
|
+
return true;
|
|
111
|
+
if (pattern.endsWith('*')) {
|
|
112
|
+
const prefix = pattern.slice(0, -1);
|
|
113
|
+
return path.startsWith(prefix);
|
|
114
|
+
}
|
|
115
|
+
return path === pattern;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Normalize routes to RouteInfo array
|
|
119
|
+
*/
|
|
120
|
+
normalizeRoutes(routes) {
|
|
121
|
+
return routes.map((route) => {
|
|
122
|
+
if (typeof route === 'string') {
|
|
123
|
+
return { path: route };
|
|
124
|
+
}
|
|
125
|
+
return route;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear all middleware
|
|
130
|
+
*/
|
|
131
|
+
clear() {
|
|
132
|
+
this.middleware = [];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get all registered middleware
|
|
136
|
+
*/
|
|
137
|
+
getMiddleware() {
|
|
138
|
+
return [...this.middleware];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.GlobalMiddlewareManager = GlobalMiddlewareManager;
|
|
142
|
+
/**
|
|
143
|
+
* Built-in CORS middleware
|
|
144
|
+
*/
|
|
145
|
+
class CorsMiddleware {
|
|
146
|
+
constructor(options = {}) {
|
|
147
|
+
this.options = options;
|
|
148
|
+
}
|
|
149
|
+
use(req, res, next) {
|
|
150
|
+
const origin = this.options.origin || '*';
|
|
151
|
+
const methods = this.options.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'];
|
|
152
|
+
const headers = this.options.allowedHeaders || ['Content-Type', 'Authorization'];
|
|
153
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
154
|
+
res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
|
|
155
|
+
res.setHeader('Access-Control-Allow-Headers', headers.join(', '));
|
|
156
|
+
if (this.options.credentials) {
|
|
157
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
158
|
+
}
|
|
159
|
+
if (req.method === 'OPTIONS') {
|
|
160
|
+
res.status(204).end();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
next();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.CorsMiddleware = CorsMiddleware;
|
|
167
|
+
/**
|
|
168
|
+
* Built-in logging middleware
|
|
169
|
+
*/
|
|
170
|
+
class LoggerMiddleware {
|
|
171
|
+
async use(req, res, next) {
|
|
172
|
+
const start = Date.now();
|
|
173
|
+
logger_1.default.info(`→ ${req.method} ${req.url}`);
|
|
174
|
+
await next();
|
|
175
|
+
const duration = Date.now() - start;
|
|
176
|
+
logger_1.default.info(`← ${req.method} ${req.url} ${duration}ms`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.LoggerMiddleware = LoggerMiddleware;
|