quantum-flow 1.3.10 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -8
- package/dist/app/aws/helpers.d.ts +25 -0
- package/dist/app/aws/helpers.js +46 -0
- package/dist/app/aws/lambda.js +23 -7
- package/dist/app/aws/utils/response.d.ts +7 -0
- package/dist/app/aws/utils/response.js +13 -0
- package/dist/app/http/Application.js +10 -0
- package/dist/app/http/decorators.js +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/core/Controller.d.ts +1 -9
- package/dist/core/Controller.js +23 -57
- package/dist/core/index.d.ts +1 -1
- package/dist/core/utils/cors.d.ts +2 -0
- package/dist/core/utils/cors.js +21 -0
- package/dist/core/utils/index.d.ts +1 -0
- package/dist/core/utils/index.js +1 -0
- package/dist/examples/app.js +9 -3
- package/dist/examples/controllers/user.js +2 -1
- package/dist/examples/controllers/userMetadata.d.ts +1 -1
- package/dist/examples/controllers/userMetadata.js +13 -5
- package/dist/types/common.d.ts +0 -13
- package/dist/types/controller.d.ts +3 -0
- package/dist/types/cors.d.ts +10 -0
- package/dist/types/cors.js +2 -0
- package/dist/types/http.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/lambda.d.ts +5 -0
- package/dist/utils/controller.d.ts +9 -1
- package/dist/utils/controller.js +62 -5
- package/dist/utils/cors.d.ts +8 -0
- package/dist/utils/cors.js +126 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/multipart.d.ts +1 -0
- package/dist/utils/multipart.js +98 -1
- package/dist/utils/server.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,8 @@ import {
|
|
|
41
41
|
Request,
|
|
42
42
|
Response,
|
|
43
43
|
Status,
|
|
44
|
-
USE
|
|
44
|
+
USE,
|
|
45
|
+
CORS
|
|
45
46
|
} from 'quantum-flow/core';
|
|
46
47
|
import {IsString} from 'class-validator'
|
|
47
48
|
|
|
@@ -58,10 +59,12 @@ class UserDto {
|
|
|
58
59
|
return { data, intercepted: true };
|
|
59
60
|
},
|
|
60
61
|
})
|
|
62
|
+
@CORS({ origin: '*' })
|
|
61
63
|
@Catch((err) => ({ status: 500, err }))
|
|
62
64
|
export class User {
|
|
63
65
|
@Status(201)
|
|
64
66
|
@PUT(':id',[...middlewares])
|
|
67
|
+
@CORS({ origin: '*' })
|
|
65
68
|
async createUser(
|
|
66
69
|
@Body(UserDto) body: UserDto,
|
|
67
70
|
@Query() query: any,
|
|
@@ -97,7 +100,7 @@ Use the `@Server` decorator with configuration options like port, host, controll
|
|
|
97
100
|
```typescript
|
|
98
101
|
import { Server, Port, Host, Use, Catch, HttpServer } from 'quantum-flow/http';
|
|
99
102
|
|
|
100
|
-
@Server({ controllers: [RootController] })
|
|
103
|
+
@Server({ controllers: [RootController], cors: { origin: '*' } })
|
|
101
104
|
@Port(3000)
|
|
102
105
|
@Host('localhost')
|
|
103
106
|
@Use((data) => data)
|
|
@@ -115,6 +118,7 @@ server.listen().catch(console.error);
|
|
|
115
118
|
- Use `@Use` to apply middlewares.
|
|
116
119
|
- Use `@Catch` to handle errors.
|
|
117
120
|
- Use `@Port` and `@Host` to configure server port and host.
|
|
121
|
+
- Use `@CORS` to configure cors.
|
|
118
122
|
|
|
119
123
|
## Request decorators
|
|
120
124
|
|
|
@@ -160,8 +164,6 @@ Enable WebSocket in the server configuration and register WebSocket controllers.
|
|
|
160
164
|
export class Socket {
|
|
161
165
|
@OnConnection()
|
|
162
166
|
onConnection(event: WebSocketEvent) {
|
|
163
|
-
console.log(`✅ Connected: ${event.client.id}`);
|
|
164
|
-
|
|
165
167
|
// Send greeting ONLY to this client
|
|
166
168
|
event.client.socket.send(
|
|
167
169
|
JSON.stringify({
|
|
@@ -181,8 +183,6 @@ export class Socket {
|
|
|
181
183
|
// The message is ALREADY automatically broadcast to all!
|
|
182
184
|
|
|
183
185
|
const msg = event.message?.data;
|
|
184
|
-
console.log(`💬 Message in chat: ${msg?.text}`);
|
|
185
|
-
|
|
186
186
|
// You can add logic, but no need to broadcast
|
|
187
187
|
if (msg?.text.includes('bad')) {
|
|
188
188
|
// If return empty, the message will not be sent
|
|
@@ -197,7 +197,6 @@ export class Socket {
|
|
|
197
197
|
*/
|
|
198
198
|
@Subscribe('news')
|
|
199
199
|
onNewsMessage(event: WebSocketEvent) {
|
|
200
|
-
console.log(`📰 News: ${event.message?.data.title}`);
|
|
201
200
|
// Automatic broadcast to all subscribed to 'news'
|
|
202
201
|
}
|
|
203
202
|
|
|
@@ -221,7 +220,6 @@ export class Socket {
|
|
|
221
220
|
@OnMessage('subscribe')
|
|
222
221
|
onSubscribe(event: WebSocketEvent) {
|
|
223
222
|
const topic = event.message?.data.topic;
|
|
224
|
-
console.log(`📌 Client ${event.client.id} subscribed to ${topic}`);
|
|
225
223
|
|
|
226
224
|
// Server will save the subscription automatically, no need to do anything!
|
|
227
225
|
// Just confirm
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AppRequest, HTTP_METHODS } from '../../types/index.js';
|
|
2
|
+
import { IncomingHttpHeaders } from 'http';
|
|
3
|
+
export declare class LResponse {
|
|
4
|
+
headers: IncomingHttpHeaders;
|
|
5
|
+
constructor();
|
|
6
|
+
setHeader(header: string, value: string): void;
|
|
7
|
+
}
|
|
8
|
+
export declare class LRequest {
|
|
9
|
+
headers: IncomingHttpHeaders;
|
|
10
|
+
url: URL;
|
|
11
|
+
method: HTTP_METHODS;
|
|
12
|
+
path?: string;
|
|
13
|
+
query?: any;
|
|
14
|
+
params?: any;
|
|
15
|
+
body: any;
|
|
16
|
+
cookies: Record<string, string>;
|
|
17
|
+
_startTime: number;
|
|
18
|
+
event?: any;
|
|
19
|
+
context?: any;
|
|
20
|
+
requestId?: string;
|
|
21
|
+
stage?: string;
|
|
22
|
+
sourceIp?: string;
|
|
23
|
+
userAgent?: string;
|
|
24
|
+
constructor(request: AppRequest);
|
|
25
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LRequest = exports.LResponse = void 0;
|
|
4
|
+
class LResponse {
|
|
5
|
+
headers = {};
|
|
6
|
+
constructor() { }
|
|
7
|
+
setHeader(header, value) {
|
|
8
|
+
this.headers[header] = value;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.LResponse = LResponse;
|
|
12
|
+
class LRequest {
|
|
13
|
+
headers;
|
|
14
|
+
url;
|
|
15
|
+
method;
|
|
16
|
+
path;
|
|
17
|
+
query;
|
|
18
|
+
params;
|
|
19
|
+
body;
|
|
20
|
+
cookies;
|
|
21
|
+
_startTime;
|
|
22
|
+
event;
|
|
23
|
+
context;
|
|
24
|
+
requestId;
|
|
25
|
+
stage;
|
|
26
|
+
sourceIp;
|
|
27
|
+
userAgent;
|
|
28
|
+
constructor(request) {
|
|
29
|
+
this.headers = { ...request.headers };
|
|
30
|
+
this.url = request.url;
|
|
31
|
+
this.method = request.method;
|
|
32
|
+
this.path = request.path;
|
|
33
|
+
this.query = request.query;
|
|
34
|
+
this.params = request.params;
|
|
35
|
+
this.body = request.body;
|
|
36
|
+
this.cookies = request.cookies;
|
|
37
|
+
this._startTime = request._startTime;
|
|
38
|
+
this.event = request.event;
|
|
39
|
+
this.context = request.context;
|
|
40
|
+
this.requestId = request.requestId;
|
|
41
|
+
this.stage = request.stage;
|
|
42
|
+
this.sourceIp = request.sourceIp;
|
|
43
|
+
this.userAgent = request.userAgent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.LRequest = LRequest;
|
package/dist/app/aws/lambda.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LambdaAdapter = void 0;
|
|
4
|
+
const _constants_1 = require("../../constants.js");
|
|
4
5
|
const _utils_1 = require("../../utils/index.js");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
5
7
|
class LambdaAdapter {
|
|
6
8
|
static createHandler(Controller) {
|
|
7
9
|
return async (event, context) => {
|
|
@@ -10,14 +12,27 @@ class LambdaAdapter {
|
|
|
10
12
|
await instance.beforeStart?.();
|
|
11
13
|
}
|
|
12
14
|
try {
|
|
15
|
+
const cors = Reflect.getMetadata(_constants_1.CORS_METADATA, instance);
|
|
13
16
|
const eventType = this.getEventType(event);
|
|
14
17
|
const normalizedEvent = (0, _utils_1.normalizeEvent)(event, eventType);
|
|
15
18
|
const request = this.toRequest(normalizedEvent, context);
|
|
19
|
+
const lambdaRequest = new helpers_1.LRequest(request);
|
|
20
|
+
const lambdaResponse = new helpers_1.LResponse();
|
|
21
|
+
let handledCors = { permitted: true, continue: true };
|
|
22
|
+
if (cors) {
|
|
23
|
+
handledCors = (0, _utils_1.handleCORS)(request, lambdaResponse, cors);
|
|
24
|
+
}
|
|
25
|
+
if (!handledCors.permitted) {
|
|
26
|
+
return this.toLambdaResponse({ status: 403, message: 'CORS: Origin not allowed' }, lambdaRequest, lambdaResponse, eventType);
|
|
27
|
+
}
|
|
28
|
+
if (!handledCors.continue && handledCors.permitted) {
|
|
29
|
+
return this.toLambdaResponse({ status: 204 }, lambdaRequest, lambdaResponse, eventType);
|
|
30
|
+
}
|
|
16
31
|
if (typeof instance.handleRequest !== 'function') {
|
|
17
32
|
throw new Error('Controller must have handleRequest method');
|
|
18
33
|
}
|
|
19
|
-
const response = await instance.handleRequest(request);
|
|
20
|
-
return this.toLambdaResponse(response,
|
|
34
|
+
const response = await instance.handleRequest(request, lambdaRequest, lambdaResponse);
|
|
35
|
+
return this.toLambdaResponse(response, lambdaRequest, lambdaResponse, eventType);
|
|
21
36
|
}
|
|
22
37
|
catch (error) {
|
|
23
38
|
return this.handleError(error, event, context);
|
|
@@ -109,12 +124,13 @@ class LambdaAdapter {
|
|
|
109
124
|
: event.headers['user-agent']?.[0] || 'unknown',
|
|
110
125
|
};
|
|
111
126
|
}
|
|
112
|
-
static toLambdaResponse(
|
|
113
|
-
const statusCode =
|
|
127
|
+
static toLambdaResponse(appResponse, request, res, eventType) {
|
|
128
|
+
const statusCode = appResponse.status || 200;
|
|
114
129
|
const headers = {
|
|
115
130
|
'Content-Type': 'application/json',
|
|
116
131
|
'X-Request-Id': request.requestId,
|
|
117
|
-
...(
|
|
132
|
+
...(appResponse.headers || {}),
|
|
133
|
+
...res.headers,
|
|
118
134
|
};
|
|
119
135
|
const originHeader = request.headers['origin'] || request.headers['Origin'];
|
|
120
136
|
let origin;
|
|
@@ -135,13 +151,13 @@ class LambdaAdapter {
|
|
|
135
151
|
}
|
|
136
152
|
const body = JSON.stringify({
|
|
137
153
|
success: statusCode < 400,
|
|
138
|
-
data:
|
|
154
|
+
data: appResponse.data,
|
|
139
155
|
timestamp: new Date().toISOString(),
|
|
140
156
|
});
|
|
141
157
|
const commonResponse = {
|
|
142
158
|
statusCode,
|
|
143
159
|
headers,
|
|
144
|
-
body:
|
|
160
|
+
body: appResponse.data,
|
|
145
161
|
timestamp: new Date().toISOString(),
|
|
146
162
|
};
|
|
147
163
|
switch (eventType) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Response = void 0;
|
|
4
|
+
class Response {
|
|
5
|
+
headers;
|
|
6
|
+
constructor(request) {
|
|
7
|
+
this.headers = { ...request.headers };
|
|
8
|
+
}
|
|
9
|
+
setHeader(header, value) {
|
|
10
|
+
this.headers[header] = value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.Response = Response;
|
|
@@ -101,6 +101,16 @@ class HttpServer extends Socket_1.Socket {
|
|
|
101
101
|
const startTime = Date.now();
|
|
102
102
|
try {
|
|
103
103
|
const request = await this.createRequest(req);
|
|
104
|
+
let handledCors = { permitted: true, continue: true };
|
|
105
|
+
if (this.config.cors) {
|
|
106
|
+
handledCors = (0, _utils_1.handleCORS)(request, res, this.config.cors);
|
|
107
|
+
}
|
|
108
|
+
if (!handledCors.permitted) {
|
|
109
|
+
return this.sendResponse(res, { status: 403, message: 'CORS: Origin not allowed' }, startTime);
|
|
110
|
+
}
|
|
111
|
+
if (!handledCors.continue && handledCors.permitted) {
|
|
112
|
+
return this.sendResponse(res, { status: 204 }, startTime);
|
|
113
|
+
}
|
|
104
114
|
let appRequest = await this.applyMiddlewares(request, req, res);
|
|
105
115
|
let data = await this.findController(appRequest, req, res);
|
|
106
116
|
const isError = !_constants_1.OK_STATUSES.includes(data.status);
|
|
@@ -12,6 +12,7 @@ function Server(config = {}) {
|
|
|
12
12
|
...config,
|
|
13
13
|
controllers: [...(existingConfig.controllers || []), ...(config.controllers || [])],
|
|
14
14
|
middlewares: [...(existingConfig.middlewares ?? []), ...(config.middlewares ?? [])],
|
|
15
|
+
cors: config.cors,
|
|
15
16
|
interceptors: existingConfig.interceptor ?? config.interceptor,
|
|
16
17
|
};
|
|
17
18
|
Reflect.defineMetadata(_constants_1.SERVER_CONFIG_KEY, mergedConfig, target);
|
package/dist/constants.d.ts
CHANGED
|
@@ -20,3 +20,4 @@ export declare const OK_STATUSES: number[];
|
|
|
20
20
|
export declare const TO_VALIDATE: string[];
|
|
21
21
|
export declare const STATISTIC: Record<'controllers' | 'routes', number>;
|
|
22
22
|
export declare const INCREMENT_STATISTIC: (prop: "controllers" | "routes") => void;
|
|
23
|
+
export declare const CORS_METADATA = "cors:config";
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.INCREMENT_STATISTIC = exports.STATISTIC = exports.TO_VALIDATE = exports.OK_STATUSES = exports.STOPPED = exports.CATCH = exports.INTECEPT = exports.WS_SERVICE_KEY = exports.WS_TOPIC_KEY = exports.WS_METADATA_KEY = exports.USE_MIDDLEWARE = exports.SERVER_MODULES_KEY = exports.SERVER_CONFIG_KEY = exports.OK_METADATA_KEY = exports.ENDPOINT = exports.INTERCEPTOR = exports.CONTROLLERS = exports.MIDDLEWARES = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PREFIX = exports.APP_METADATA_KEY = exports.PARAM_METADATA_KEY = void 0;
|
|
3
|
+
exports.CORS_METADATA = exports.INCREMENT_STATISTIC = exports.STATISTIC = exports.TO_VALIDATE = exports.OK_STATUSES = exports.STOPPED = exports.CATCH = exports.INTECEPT = exports.WS_SERVICE_KEY = exports.WS_TOPIC_KEY = exports.WS_METADATA_KEY = exports.USE_MIDDLEWARE = exports.SERVER_MODULES_KEY = exports.SERVER_CONFIG_KEY = exports.OK_METADATA_KEY = exports.ENDPOINT = exports.INTERCEPTOR = exports.CONTROLLERS = exports.MIDDLEWARES = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PREFIX = exports.APP_METADATA_KEY = exports.PARAM_METADATA_KEY = void 0;
|
|
4
4
|
exports.PARAM_METADATA_KEY = 'design:parameters';
|
|
5
5
|
exports.APP_METADATA_KEY = 'app:configuration';
|
|
6
6
|
exports.ROUTE_PREFIX = 'route:prefix';
|
|
@@ -34,3 +34,4 @@ const INCREMENT_STATISTIC = (prop) => {
|
|
|
34
34
|
exports.STATISTIC[prop] = exports.STATISTIC[prop] + 1;
|
|
35
35
|
};
|
|
36
36
|
exports.INCREMENT_STATISTIC = INCREMENT_STATISTIC;
|
|
37
|
+
exports.CORS_METADATA = 'cors:config';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppRequest, ControllerClass, ControllerConfig, ControllerInstance, InterceptorCB,
|
|
1
|
+
import { AppRequest, ControllerClass, ControllerConfig, ControllerInstance, InterceptorCB, RouteContext } from '../types/index.js';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
3
|
import 'reflect-metadata';
|
|
4
4
|
/**
|
|
@@ -40,13 +40,5 @@ export declare function Controller(config: string | ControllerConfig, middleware
|
|
|
40
40
|
}>;
|
|
41
41
|
handleRequest: (appRequest: AppRequest, request?: IncomingMessage, response?: ServerResponse) => Promise<any>;
|
|
42
42
|
routeWalker(context: RouteContext): Promise<any>;
|
|
43
|
-
getAllMethods(obj: any): string[];
|
|
44
|
-
findRouteInController(instance: any, path: string, route: string, method: string): {
|
|
45
|
-
name: string;
|
|
46
|
-
pathParams: Record<string, string>;
|
|
47
|
-
priority: number;
|
|
48
|
-
methodMiddlewares: MiddlewareCB[];
|
|
49
|
-
methodInterceptors: InterceptorCB[];
|
|
50
|
-
};
|
|
51
43
|
};
|
|
52
44
|
} & T;
|
package/dist/core/Controller.js
CHANGED
|
@@ -104,6 +104,7 @@ function Controller(config, middlewares = []) {
|
|
|
104
104
|
interceptor: Reflect.getMetadata(_constants_1.INTERCEPTOR, proto),
|
|
105
105
|
subControllers: Reflect.getMetadata(_constants_1.CONTROLLERS, proto) || [],
|
|
106
106
|
errorHandler: Reflect.getMetadata(_constants_1.CATCH, proto),
|
|
107
|
+
cors: Reflect.getMetadata(_constants_1.CORS_METADATA, proto),
|
|
107
108
|
},
|
|
108
109
|
path: (appRequest.url.pathname ?? '').replace(/^\/+/g, ''),
|
|
109
110
|
method: appRequest.method.toUpperCase(),
|
|
@@ -112,6 +113,7 @@ function Controller(config, middlewares = []) {
|
|
|
112
113
|
response,
|
|
113
114
|
middlewareChain: [],
|
|
114
115
|
interceptorChain: [],
|
|
116
|
+
corsChain: [Reflect.getMetadata(_constants_1.CORS_METADATA, proto)],
|
|
115
117
|
errorHandlerChain: [Reflect.getMetadata(_constants_1.CATCH, proto)],
|
|
116
118
|
subPath: Reflect.getMetadata(_constants_1.ROUTE_PREFIX, proto) || '',
|
|
117
119
|
};
|
|
@@ -128,6 +130,7 @@ function Controller(config, middlewares = []) {
|
|
|
128
130
|
interceptor: Reflect.getMetadata(_constants_1.INTERCEPTOR, SubController.prototype),
|
|
129
131
|
errorHandler: Reflect.getMetadata(_constants_1.CATCH, SubController),
|
|
130
132
|
subControllers: Reflect.getMetadata(_constants_1.CONTROLLERS, SubController.prototype) || [],
|
|
133
|
+
cors: Reflect.getMetadata(_constants_1.CORS_METADATA, SubController.prototype) || [],
|
|
131
134
|
};
|
|
132
135
|
const fullSubPath = [subPath, subMeta.routePrefix]
|
|
133
136
|
.filter(Boolean)
|
|
@@ -143,21 +146,38 @@ function Controller(config, middlewares = []) {
|
|
|
143
146
|
middlewareChain: [...context.middlewareChain, ...controllerMeta.middlewares],
|
|
144
147
|
errorHandlerChain: [...context.errorHandlerChain, subMeta.errorHandler].filter((el) => !!el),
|
|
145
148
|
interceptorChain: [...context.interceptorChain, controllerMeta.interceptor].filter((el) => !!el),
|
|
149
|
+
corsChain: [...context.corsChain, subMeta.cors].filter((el) => !!el),
|
|
146
150
|
});
|
|
147
151
|
if (subResult && subResult.status !== 404) {
|
|
148
152
|
return subResult;
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
|
-
const routeMatch =
|
|
156
|
+
const routeMatch = (0, _utils_1.findRouteInController)(controllerInstance, subPath, path, method);
|
|
153
157
|
if (routeMatch) {
|
|
154
|
-
const { name, pathParams, methodMiddlewares,
|
|
158
|
+
const { name, pathParams, methodMiddlewares, cors } = routeMatch;
|
|
159
|
+
let payload = { ...context.appRequest, params: pathParams };
|
|
160
|
+
const handledCors = context.corsChain
|
|
161
|
+
.concat(cors ?? [])
|
|
162
|
+
.flat()
|
|
163
|
+
.reduce((acc, conf) => {
|
|
164
|
+
const cors = (0, _utils_1.handleCORS)(payload, context.response, conf);
|
|
165
|
+
return {
|
|
166
|
+
permitted: acc.permitted && cors.permitted,
|
|
167
|
+
continue: acc.continue && cors.continue,
|
|
168
|
+
};
|
|
169
|
+
}, { permitted: true, continue: true });
|
|
170
|
+
if (!handledCors.permitted) {
|
|
171
|
+
return { status: 403, message: 'CORS: Origin not allowed' };
|
|
172
|
+
}
|
|
173
|
+
if (!handledCors.continue && handledCors.permitted) {
|
|
174
|
+
return { status: 204 };
|
|
175
|
+
}
|
|
155
176
|
const allMiddlewares = [
|
|
156
177
|
...context.middlewareChain,
|
|
157
178
|
...controllerMeta.middlewares,
|
|
158
179
|
...methodMiddlewares,
|
|
159
180
|
];
|
|
160
|
-
let payload = { ...context.appRequest, params: pathParams };
|
|
161
181
|
for (const mw of allMiddlewares) {
|
|
162
182
|
const mwResult = await mw(payload, context.request, context.response);
|
|
163
183
|
payload = mwResult ?? payload;
|
|
@@ -180,60 +200,6 @@ function Controller(config, middlewares = []) {
|
|
|
180
200
|
}
|
|
181
201
|
return null;
|
|
182
202
|
}
|
|
183
|
-
getAllMethods(obj) {
|
|
184
|
-
let methods = new Set();
|
|
185
|
-
let current = Object.getPrototypeOf(obj);
|
|
186
|
-
while (current && current !== Object.prototype) {
|
|
187
|
-
Object.getOwnPropertyNames(current).forEach((name) => {
|
|
188
|
-
if (name !== 'constructor' && typeof current[name] === 'function') {
|
|
189
|
-
methods.add(name);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
current = Object.getPrototypeOf(current);
|
|
193
|
-
}
|
|
194
|
-
return Array.from(methods);
|
|
195
|
-
}
|
|
196
|
-
findRouteInController(instance, path, route, method) {
|
|
197
|
-
const prototype = Object.getPrototypeOf(instance);
|
|
198
|
-
const propertyNames = this.getAllMethods(instance);
|
|
199
|
-
const matches = [];
|
|
200
|
-
for (const name of propertyNames) {
|
|
201
|
-
if ([
|
|
202
|
-
'constructor',
|
|
203
|
-
'getResponse',
|
|
204
|
-
'routeWalker',
|
|
205
|
-
'getAllMethods',
|
|
206
|
-
'findRouteInController',
|
|
207
|
-
].includes(name))
|
|
208
|
-
continue;
|
|
209
|
-
const endpointMeta = Reflect.getMetadata(_constants_1.ENDPOINT, prototype, name) || [];
|
|
210
|
-
if (endpointMeta.length === 0)
|
|
211
|
-
continue;
|
|
212
|
-
const [httpMethod, routePattern] = endpointMeta;
|
|
213
|
-
if (httpMethod !== method && httpMethod !== 'USE') {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (httpMethod === 'USE') {
|
|
217
|
-
let useRoute = route.split('/');
|
|
218
|
-
useRoute.pop();
|
|
219
|
-
route = useRoute.join('/');
|
|
220
|
-
}
|
|
221
|
-
const current = [path, routePattern].join('/').replace(/\/+/g, '/');
|
|
222
|
-
const pathParams = (0, _utils_1.matchRoute)(current, route);
|
|
223
|
-
if (pathParams) {
|
|
224
|
-
const priority = httpMethod === 'USE' ? 0 : Object.keys(pathParams).length > 0 ? 1 : 2;
|
|
225
|
-
matches.push({
|
|
226
|
-
name,
|
|
227
|
-
pathParams,
|
|
228
|
-
priority,
|
|
229
|
-
methodMiddlewares: Reflect.getMetadata(_constants_1.MIDDLEWARES, prototype, name) || [],
|
|
230
|
-
methodInterceptors: Reflect.getMetadata(_constants_1.INTERCEPTOR, prototype, name) || [],
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
matches.sort((a, b) => b.priority - a.priority);
|
|
235
|
-
return matches[0] || null;
|
|
236
|
-
}
|
|
237
203
|
};
|
|
238
204
|
};
|
|
239
205
|
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module provides centralized exports for controller and endpoint decorators,
|
|
5
5
|
* as well as related types and utility functions used throughout the core framework.
|
|
6
6
|
*/
|
|
7
|
-
export { AppRequest, EndpointResponse, ErrorCB, HttpError, IController, InterceptorCB, IWebSocketService, MiddlewareCB, ResponseWithStatus, Router, WebSocketClient, WebSocketEvent, WebSocketMessage, } from '../types/index.js';
|
|
7
|
+
export { AppRequest, CORSConfig, EndpointResponse, ErrorCB, HttpError, IController, InterceptorCB, IWebSocketService, MiddlewareCB, ResponseWithStatus, Router, WebSocketClient, WebSocketEvent, WebSocketMessage, } from '../types/index.js';
|
|
8
8
|
export * from './Controller';
|
|
9
9
|
export * from './Endpoint';
|
|
10
10
|
export * from './utils';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CORS = CORS;
|
|
4
|
+
const _constants_1 = require("../../constants.js");
|
|
5
|
+
const _types_1 = require("../../types/index.js");
|
|
6
|
+
function CORS(config = {}) {
|
|
7
|
+
return function (target, propertyKey, descriptor) {
|
|
8
|
+
const defaultConfig = {
|
|
9
|
+
origin: '*',
|
|
10
|
+
optionsSuccessStatus: 204,
|
|
11
|
+
methods: Object.keys(_types_1.HTTP_METHODS),
|
|
12
|
+
};
|
|
13
|
+
const finalConfig = { ...defaultConfig, ...config };
|
|
14
|
+
if (propertyKey && descriptor) {
|
|
15
|
+
Reflect.defineMetadata(_constants_1.CORS_METADATA, finalConfig, target, propertyKey);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
Reflect.defineMetadata(_constants_1.CORS_METADATA, finalConfig, target.prototype || target);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./cors"), exports);
|
|
17
18
|
__exportStar(require("./extractors"), exports);
|
|
18
19
|
__exportStar(require("./helpers"), exports);
|
|
19
20
|
__exportStar(require("./middlewares"), exports);
|
package/dist/examples/app.js
CHANGED
|
@@ -15,7 +15,8 @@ let Root = class Root {
|
|
|
15
15
|
};
|
|
16
16
|
exports.Root = Root;
|
|
17
17
|
exports.Root = Root = __decorate([
|
|
18
|
-
(0, core_1.Controller)({ prefix: 'api', controllers: [user_1.User] })
|
|
18
|
+
(0, core_1.Controller)({ prefix: 'api', controllers: [user_1.User] }),
|
|
19
|
+
(0, core_1.CORS)({ origin: '*' })
|
|
19
20
|
], Root);
|
|
20
21
|
let App = class App {
|
|
21
22
|
};
|
|
@@ -26,9 +27,14 @@ exports.App = App = __decorate([
|
|
|
26
27
|
websocket: { enabled: true },
|
|
27
28
|
interceptor: (data) => data,
|
|
28
29
|
errorHandler: (err) => err,
|
|
30
|
+
cors: { origin: '*' },
|
|
29
31
|
}),
|
|
30
32
|
(0, http_1.Port)(3000),
|
|
31
|
-
(0, core_1.Use)((data) =>
|
|
32
|
-
|
|
33
|
+
(0, core_1.Use)((data) => {
|
|
34
|
+
return data;
|
|
35
|
+
}),
|
|
36
|
+
(0, core_1.Use)((data) => {
|
|
37
|
+
return data;
|
|
38
|
+
}),
|
|
33
39
|
(0, core_1.Catch)((err) => err)
|
|
34
40
|
], App);
|
|
@@ -27,7 +27,7 @@ let UserMetadata = class UserMetadata {
|
|
|
27
27
|
async getUserMetadata(params) {
|
|
28
28
|
return params;
|
|
29
29
|
}
|
|
30
|
-
async createMeta(body, params) {
|
|
30
|
+
async createMeta(mult, body, params) {
|
|
31
31
|
return { body, params };
|
|
32
32
|
}
|
|
33
33
|
};
|
|
@@ -41,12 +41,20 @@ __decorate([
|
|
|
41
41
|
], UserMetadata.prototype, "getUserMetadata", null);
|
|
42
42
|
__decorate([
|
|
43
43
|
(0, core_1.POST)('/:meta'),
|
|
44
|
-
__param(0, (0, core_1.
|
|
45
|
-
__param(1, (0, core_1.
|
|
44
|
+
__param(0, (0, core_1.Multipart)()),
|
|
45
|
+
__param(1, (0, core_1.Body)()),
|
|
46
|
+
__param(2, (0, core_1.Params)(DTO, 'meta')),
|
|
46
47
|
__metadata("design:type", Function),
|
|
47
|
-
__metadata("design:paramtypes", [Object, Object]),
|
|
48
|
+
__metadata("design:paramtypes", [Object, Object, Object]),
|
|
48
49
|
__metadata("design:returntype", Promise)
|
|
49
50
|
], UserMetadata.prototype, "createMeta", null);
|
|
50
51
|
exports.UserMetadata = UserMetadata = __decorate([
|
|
51
|
-
(0, core_1.Controller)({
|
|
52
|
+
(0, core_1.Controller)({
|
|
53
|
+
prefix: 'metadata',
|
|
54
|
+
middlewares: [
|
|
55
|
+
(req) => {
|
|
56
|
+
return req;
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
})
|
|
52
60
|
], UserMetadata);
|
package/dist/types/common.d.ts
CHANGED
|
@@ -38,19 +38,6 @@ export type EndpointResponse<T = any> = {
|
|
|
38
38
|
data?: T;
|
|
39
39
|
error?: any;
|
|
40
40
|
};
|
|
41
|
-
export type AxiosQuery = {
|
|
42
|
-
data?: {
|
|
43
|
-
[key: string]: any;
|
|
44
|
-
};
|
|
45
|
-
headers?: {
|
|
46
|
-
[key: string]: any;
|
|
47
|
-
};
|
|
48
|
-
params?: {
|
|
49
|
-
[key: string]: any;
|
|
50
|
-
};
|
|
51
|
-
url: string;
|
|
52
|
-
method: 'POST' | 'GET' | 'PATCH' | 'DELETE';
|
|
53
|
-
};
|
|
54
41
|
export interface IController {
|
|
55
42
|
handleRequest: Router;
|
|
56
43
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
2
|
import { AppRequest, ErrorCB, HTTP_METHODS, InterceptorCB, MiddlewareCB } from './common';
|
|
3
|
+
import { CORSConfig } from './cors';
|
|
3
4
|
export type ControllerClass = {
|
|
4
5
|
new (...args: any[]): any;
|
|
5
6
|
};
|
|
@@ -16,6 +17,7 @@ export type ControllerMetadata = {
|
|
|
16
17
|
interceptor?: InterceptorCB;
|
|
17
18
|
subControllers: ControllerClass[];
|
|
18
19
|
errorHandler?: ErrorCB;
|
|
20
|
+
cors?: CORSConfig;
|
|
19
21
|
};
|
|
20
22
|
export interface ControllerConfig {
|
|
21
23
|
prefix: string;
|
|
@@ -33,6 +35,7 @@ export type RouteContext = {
|
|
|
33
35
|
response?: ServerResponse;
|
|
34
36
|
middlewareChain: MiddlewareCB[];
|
|
35
37
|
interceptorChain: InterceptorCB[];
|
|
38
|
+
corsChain: CORSConfig[];
|
|
36
39
|
errorHandlerChain: ErrorCB[];
|
|
37
40
|
subPath: string;
|
|
38
41
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CORSConfig {
|
|
2
|
+
origin?: string | string[] | ((origin: string) => boolean);
|
|
3
|
+
methods?: string[];
|
|
4
|
+
allowedHeaders?: string[];
|
|
5
|
+
exposedHeaders?: string[];
|
|
6
|
+
credentials?: boolean;
|
|
7
|
+
maxAge?: number;
|
|
8
|
+
preflightContinue?: boolean;
|
|
9
|
+
optionsSuccessStatus?: number;
|
|
10
|
+
}
|
package/dist/types/http.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ErrorCB, InterceptorCB, MiddlewareCB } from './common';
|
|
2
|
+
import { CORSConfig } from './cors';
|
|
2
3
|
export interface ServerConfig {
|
|
3
4
|
port?: number;
|
|
4
5
|
host?: string;
|
|
@@ -6,6 +7,7 @@ export interface ServerConfig {
|
|
|
6
7
|
interceptor?: InterceptorCB;
|
|
7
8
|
errorHandler?: ErrorCB;
|
|
8
9
|
controllers?: (new (...args: any[]) => any)[];
|
|
10
|
+
cors?: CORSConfig;
|
|
9
11
|
websocket?: {
|
|
10
12
|
enabled: boolean;
|
|
11
13
|
path?: string;
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./common"), exports);
|
|
18
18
|
__exportStar(require("./controller"), exports);
|
|
19
|
+
__exportStar(require("./cors"), exports);
|
|
19
20
|
__exportStar(require("./http"), exports);
|
|
20
21
|
__exportStar(require("./lambda"), exports);
|
|
21
22
|
__exportStar(require("./websocket"), exports);
|
package/dist/types/lambda.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export interface LambdaRequest {
|
|
|
56
56
|
url: URL;
|
|
57
57
|
event: NormalizedEvent;
|
|
58
58
|
_startTime: number;
|
|
59
|
+
routeInfo?: {
|
|
60
|
+
controller: any;
|
|
61
|
+
methodName: string;
|
|
62
|
+
pathParams: Record<string, string>;
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
export interface LambdaResponse {
|
|
61
66
|
statusCode: number;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import { ControllerInstance, ControllerMethods } from '../types/index.js';
|
|
1
|
+
import { ControllerInstance, ControllerMethods, CORSConfig, MiddlewareCB } from '../types/index.js';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
3
|
export declare const executeControllerMethod: (controller: ControllerInstance, propertyName: string, payload: any, request?: IncomingMessage, response?: ServerResponse) => Promise<any>;
|
|
4
4
|
export declare const getControllerMethods: (controller: ControllerInstance) => ControllerMethods;
|
|
5
|
+
export declare const getAllMethods: (obj: any) => string[];
|
|
6
|
+
export declare const findRouteInController: (instance: any, path: string, route: string, method: string) => {
|
|
7
|
+
name: string;
|
|
8
|
+
pathParams: Record<string, string>;
|
|
9
|
+
priority: number;
|
|
10
|
+
methodMiddlewares: MiddlewareCB[];
|
|
11
|
+
cors?: CORSConfig;
|
|
12
|
+
};
|
package/dist/utils/controller.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getControllerMethods = exports.executeControllerMethod = void 0;
|
|
3
|
+
exports.findRouteInController = exports.getAllMethods = exports.getControllerMethods = exports.executeControllerMethod = void 0;
|
|
4
|
+
const _constants_1 = require("../constants.js");
|
|
4
5
|
const _types_1 = require("../types/index.js");
|
|
5
|
-
const _utils_1 = require("./index.js");
|
|
6
6
|
const WebsocketService_1 = require("../app/http/websocket/WebsocketService");
|
|
7
|
+
const helper_1 = require("./helper");
|
|
8
|
+
const multipart_1 = require("./multipart");
|
|
7
9
|
const validate_1 = require("./validate");
|
|
8
|
-
const _constants_1 = require("../constants.js");
|
|
9
10
|
const getBodyAndMultipart = (payload) => {
|
|
10
11
|
let body = payload.body;
|
|
11
12
|
let multipart;
|
|
12
|
-
if (
|
|
13
|
+
if (multipart_1.MultipartProcessor.isMultipart({ headers: payload.headers })) {
|
|
13
14
|
try {
|
|
14
|
-
const { fields, files } =
|
|
15
|
+
const { fields, files } = multipart_1.MultipartProcessor.parse({
|
|
15
16
|
body: payload.rawBody || payload.body,
|
|
16
17
|
headers: payload.headers,
|
|
17
18
|
isBase64Encoded: payload.isBase64Encoded,
|
|
@@ -110,3 +111,59 @@ const getControllerMethods = (controller) => {
|
|
|
110
111
|
return methods.sort((a, b) => (a.httpMethod === _types_1.HTTP_METHODS.USE ? 1 : -1));
|
|
111
112
|
};
|
|
112
113
|
exports.getControllerMethods = getControllerMethods;
|
|
114
|
+
const getAllMethods = (obj) => {
|
|
115
|
+
let methods = new Set();
|
|
116
|
+
let current = Object.getPrototypeOf(obj);
|
|
117
|
+
while (current && current !== Object.prototype) {
|
|
118
|
+
Object.getOwnPropertyNames(current).forEach((name) => {
|
|
119
|
+
if (name !== 'constructor' && typeof current[name] === 'function') {
|
|
120
|
+
methods.add(name);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
current = Object.getPrototypeOf(current);
|
|
124
|
+
}
|
|
125
|
+
return Array.from(methods);
|
|
126
|
+
};
|
|
127
|
+
exports.getAllMethods = getAllMethods;
|
|
128
|
+
const findRouteInController = (instance, path, route, method) => {
|
|
129
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
130
|
+
const propertyNames = (0, exports.getAllMethods)(instance);
|
|
131
|
+
const matches = [];
|
|
132
|
+
for (const name of propertyNames) {
|
|
133
|
+
if ([
|
|
134
|
+
'constructor',
|
|
135
|
+
'getResponse',
|
|
136
|
+
'routeWalker',
|
|
137
|
+
'getAllMethods',
|
|
138
|
+
'findRouteInController',
|
|
139
|
+
].includes(name))
|
|
140
|
+
continue;
|
|
141
|
+
const endpointMeta = Reflect.getMetadata(_constants_1.ENDPOINT, prototype, name) || [];
|
|
142
|
+
if (endpointMeta.length === 0)
|
|
143
|
+
continue;
|
|
144
|
+
const [httpMethod, routePattern] = endpointMeta;
|
|
145
|
+
if (httpMethod !== method && httpMethod !== 'USE') {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (httpMethod === 'USE') {
|
|
149
|
+
let useRoute = route.split('/');
|
|
150
|
+
useRoute.pop();
|
|
151
|
+
route = useRoute.join('/');
|
|
152
|
+
}
|
|
153
|
+
const current = [path, routePattern].join('/').replace(/\/+/g, '/');
|
|
154
|
+
const pathParams = (0, helper_1.matchRoute)(current, route);
|
|
155
|
+
if (pathParams) {
|
|
156
|
+
const priority = httpMethod === 'USE' ? 0 : Object.keys(pathParams).length > 0 ? 1 : 2;
|
|
157
|
+
matches.push({
|
|
158
|
+
name,
|
|
159
|
+
pathParams,
|
|
160
|
+
priority,
|
|
161
|
+
cors: Reflect.getMetadata(_constants_1.CORS_METADATA, prototype, name),
|
|
162
|
+
methodMiddlewares: Reflect.getMetadata(_constants_1.MIDDLEWARES, prototype, name) || [],
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
matches.sort((a, b) => b.priority - a.priority);
|
|
167
|
+
return matches[0] || null;
|
|
168
|
+
};
|
|
169
|
+
exports.findRouteInController = findRouteInController;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CORSConfig } from '../types/index.js';
|
|
2
|
+
export declare function handleCORS(req: any, res: any, config: CORSConfig): {
|
|
3
|
+
permitted: boolean;
|
|
4
|
+
continue: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function isPreflightRequest(req: any): boolean;
|
|
7
|
+
export declare const getCORSConfig: (controller: any, methodName?: string) => CORSConfig | null;
|
|
8
|
+
export declare const getCORSHeaders: (req: any, config: CORSConfig) => Record<string, string>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCORSHeaders = exports.getCORSConfig = void 0;
|
|
4
|
+
exports.handleCORS = handleCORS;
|
|
5
|
+
exports.isPreflightRequest = isPreflightRequest;
|
|
6
|
+
const _constants_1 = require("../constants.js");
|
|
7
|
+
function handleCORS(req, res, config) {
|
|
8
|
+
const origin = req.headers.origin || req.headers.Origin || req.url.origin;
|
|
9
|
+
function isOriginAllowed() {
|
|
10
|
+
if (!origin)
|
|
11
|
+
return false;
|
|
12
|
+
if (config.origin === '*')
|
|
13
|
+
return true;
|
|
14
|
+
if (typeof config.origin === 'string')
|
|
15
|
+
return config.origin === origin;
|
|
16
|
+
if (Array.isArray(config.origin))
|
|
17
|
+
return config.origin.includes(origin);
|
|
18
|
+
if (typeof config.origin === 'function')
|
|
19
|
+
return config.origin(origin);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (origin && !isOriginAllowed()) {
|
|
23
|
+
res.statusCode = 403;
|
|
24
|
+
res.setHeader('Content-Type', 'application/json');
|
|
25
|
+
return { permitted: false, continue: false };
|
|
26
|
+
}
|
|
27
|
+
if (req.method === 'OPTIONS') {
|
|
28
|
+
if (origin) {
|
|
29
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
30
|
+
}
|
|
31
|
+
else if (config.origin === '*') {
|
|
32
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
33
|
+
}
|
|
34
|
+
if (config.methods) {
|
|
35
|
+
res.setHeader('Access-Control-Allow-Methods', config.methods.join(', '));
|
|
36
|
+
}
|
|
37
|
+
if (config.allowedHeaders) {
|
|
38
|
+
res.setHeader('Access-Control-Allow-Headers', config.allowedHeaders.join(', '));
|
|
39
|
+
}
|
|
40
|
+
if (config.credentials) {
|
|
41
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
42
|
+
}
|
|
43
|
+
if (config.maxAge) {
|
|
44
|
+
res.setHeader('Access-Control-Max-Age', config.maxAge.toString());
|
|
45
|
+
}
|
|
46
|
+
res.statusCode = config.optionsSuccessStatus || 204;
|
|
47
|
+
return { permitted: true, continue: false };
|
|
48
|
+
}
|
|
49
|
+
if (origin) {
|
|
50
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
51
|
+
if (config.credentials) {
|
|
52
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
53
|
+
}
|
|
54
|
+
if (config.exposedHeaders) {
|
|
55
|
+
res.setHeader('Access-Control-Expose-Headers', config.exposedHeaders.join(', '));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { permitted: true, continue: true };
|
|
59
|
+
}
|
|
60
|
+
function isPreflightRequest(req) {
|
|
61
|
+
return (req.method === 'OPTIONS' && req.headers['access-control-request-method'] && req.headers.origin);
|
|
62
|
+
}
|
|
63
|
+
const getCORSConfig = (controller, methodName) => {
|
|
64
|
+
if (methodName) {
|
|
65
|
+
const methodConfig = Reflect.getMetadata(_constants_1.CORS_METADATA, controller, methodName);
|
|
66
|
+
if (methodConfig) {
|
|
67
|
+
return methodConfig;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (controller && controller.constructor) {
|
|
71
|
+
const classConfig = Reflect.getMetadata(_constants_1.CORS_METADATA, controller.constructor.prototype);
|
|
72
|
+
if (classConfig) {
|
|
73
|
+
return classConfig;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
};
|
|
78
|
+
exports.getCORSConfig = getCORSConfig;
|
|
79
|
+
const getCORSHeaders = (req, config) => {
|
|
80
|
+
const headers = {};
|
|
81
|
+
const origin = req?.headers?.origin || req?.headers?.Origin;
|
|
82
|
+
if (!origin)
|
|
83
|
+
return headers;
|
|
84
|
+
let allowedOrigin = null;
|
|
85
|
+
if (config.origin === '*') {
|
|
86
|
+
allowedOrigin = '*';
|
|
87
|
+
}
|
|
88
|
+
else if (typeof config.origin === 'string') {
|
|
89
|
+
allowedOrigin = config.origin;
|
|
90
|
+
}
|
|
91
|
+
else if (Array.isArray(config.origin)) {
|
|
92
|
+
if (config.origin.includes(origin)) {
|
|
93
|
+
allowedOrigin = origin;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (typeof config.origin === 'function') {
|
|
97
|
+
if (config.origin(origin)) {
|
|
98
|
+
allowedOrigin = origin;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (config.origin === true) {
|
|
102
|
+
allowedOrigin = origin;
|
|
103
|
+
}
|
|
104
|
+
if (allowedOrigin) {
|
|
105
|
+
headers['Access-Control-Allow-Origin'] = allowedOrigin;
|
|
106
|
+
if (config.credentials) {
|
|
107
|
+
headers['Access-Control-Allow-Credentials'] = 'true';
|
|
108
|
+
}
|
|
109
|
+
if (config.exposedHeaders?.length) {
|
|
110
|
+
headers['Access-Control-Expose-Headers'] = config.exposedHeaders.join(', ');
|
|
111
|
+
}
|
|
112
|
+
if (req.method === 'OPTIONS') {
|
|
113
|
+
if (config.methods?.length) {
|
|
114
|
+
headers['Access-Control-Allow-Methods'] = config.methods.join(', ');
|
|
115
|
+
}
|
|
116
|
+
if (config.allowedHeaders?.length) {
|
|
117
|
+
headers['Access-Control-Allow-Headers'] = config.allowedHeaders.join(', ');
|
|
118
|
+
}
|
|
119
|
+
if (config.maxAge) {
|
|
120
|
+
headers['Access-Control-Max-Age'] = config.maxAge.toString();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return headers;
|
|
125
|
+
};
|
|
126
|
+
exports.getCORSHeaders = getCORSHeaders;
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./controller"), exports);
|
|
18
|
+
__exportStar(require("./cors"), exports);
|
|
18
19
|
__exportStar(require("./endpoint"), exports);
|
|
19
20
|
__exportStar(require("./helper"), exports);
|
|
20
21
|
__exportStar(require("./lambda"), exports);
|
package/dist/utils/multipart.js
CHANGED
|
@@ -69,10 +69,11 @@ class MultipartProcessor {
|
|
|
69
69
|
parts.forEach((part) => {
|
|
70
70
|
if (part.filename) {
|
|
71
71
|
const fieldName = part.name || 'file';
|
|
72
|
+
const contentType = this.getContentType(part);
|
|
72
73
|
const fileData = {
|
|
73
74
|
fieldname: fieldName,
|
|
74
75
|
filename: part.filename,
|
|
75
|
-
contentType: part.type,
|
|
76
|
+
contentType: contentType ?? part.type,
|
|
76
77
|
data: part.data,
|
|
77
78
|
size: part.data.length,
|
|
78
79
|
encoding: part.encoding,
|
|
@@ -105,5 +106,101 @@ class MultipartProcessor {
|
|
|
105
106
|
const contentType = request.headers?.['content-type'] || request.headers?.['Content-Type'] || '';
|
|
106
107
|
return contentType.startsWith('multipart/form-data');
|
|
107
108
|
}
|
|
109
|
+
static getContentType(part) {
|
|
110
|
+
const filename = part.filename || '';
|
|
111
|
+
const extension = filename.split('.').pop()?.toLowerCase() ?? '';
|
|
112
|
+
const mimeMap = {
|
|
113
|
+
txt: 'text/plain',
|
|
114
|
+
text: 'text/plain',
|
|
115
|
+
log: 'text/plain',
|
|
116
|
+
md: 'text/markdown',
|
|
117
|
+
csv: 'text/csv',
|
|
118
|
+
ts: 'application/typescript',
|
|
119
|
+
tsx: 'application/typescript',
|
|
120
|
+
js: 'application/javascript',
|
|
121
|
+
jsx: 'application/javascript',
|
|
122
|
+
mjs: 'application/javascript',
|
|
123
|
+
cjs: 'application/javascript',
|
|
124
|
+
json: 'application/json',
|
|
125
|
+
xml: 'application/xml',
|
|
126
|
+
yaml: 'application/yaml',
|
|
127
|
+
yml: 'application/yaml',
|
|
128
|
+
toml: 'application/toml',
|
|
129
|
+
php: 'application/x-httpd-php',
|
|
130
|
+
py: 'text/x-python',
|
|
131
|
+
rb: 'text/x-ruby',
|
|
132
|
+
java: 'text/x-java',
|
|
133
|
+
c: 'text/x-c',
|
|
134
|
+
cpp: 'text/x-c++',
|
|
135
|
+
h: 'text/x-c',
|
|
136
|
+
hpp: 'text/x-c++',
|
|
137
|
+
go: 'text/x-go',
|
|
138
|
+
rs: 'text/x-rust',
|
|
139
|
+
swift: 'text/x-swift',
|
|
140
|
+
kt: 'text/x-kotlin',
|
|
141
|
+
kts: 'text/x-kotlin',
|
|
142
|
+
html: 'text/html',
|
|
143
|
+
htm: 'text/html',
|
|
144
|
+
css: 'text/css',
|
|
145
|
+
scss: 'text/x-scss',
|
|
146
|
+
sass: 'text/x-sass',
|
|
147
|
+
less: 'text/x-less',
|
|
148
|
+
jpg: 'image/jpeg',
|
|
149
|
+
jpeg: 'image/jpeg',
|
|
150
|
+
png: 'image/png',
|
|
151
|
+
gif: 'image/gif',
|
|
152
|
+
webp: 'image/webp',
|
|
153
|
+
svg: 'image/svg+xml',
|
|
154
|
+
ico: 'image/x-icon',
|
|
155
|
+
bmp: 'image/bmp',
|
|
156
|
+
tiff: 'image/tiff',
|
|
157
|
+
tif: 'image/tiff',
|
|
158
|
+
avif: 'image/avif',
|
|
159
|
+
heic: 'image/heic',
|
|
160
|
+
heif: 'image/heif',
|
|
161
|
+
pdf: 'application/pdf',
|
|
162
|
+
doc: 'application/msword',
|
|
163
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
164
|
+
xls: 'application/vnd.ms-excel',
|
|
165
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
166
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
167
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
168
|
+
odt: 'application/vnd.oasis.opendocument.text',
|
|
169
|
+
ods: 'application/vnd.oasis.opendocument.spreadsheet',
|
|
170
|
+
odp: 'application/vnd.oasis.opendocument.presentation',
|
|
171
|
+
zip: 'application/zip',
|
|
172
|
+
rar: 'application/vnd.rar',
|
|
173
|
+
'7z': 'application/x-7z-compressed',
|
|
174
|
+
tar: 'application/x-tar',
|
|
175
|
+
gz: 'application/gzip',
|
|
176
|
+
bz2: 'application/x-bzip2',
|
|
177
|
+
mp3: 'audio/mpeg',
|
|
178
|
+
wav: 'audio/wav',
|
|
179
|
+
ogg: 'audio/ogg',
|
|
180
|
+
m4a: 'audio/mp4',
|
|
181
|
+
flac: 'audio/flac',
|
|
182
|
+
aac: 'audio/aac',
|
|
183
|
+
mp4: 'video/mp4',
|
|
184
|
+
mpg: 'video/mpeg',
|
|
185
|
+
mpeg: 'video/mpeg',
|
|
186
|
+
mov: 'video/quicktime',
|
|
187
|
+
avi: 'video/x-msvideo',
|
|
188
|
+
wmv: 'video/x-ms-wmv',
|
|
189
|
+
flv: 'video/x-flv',
|
|
190
|
+
webm: 'video/webm',
|
|
191
|
+
mkv: 'video/x-matroska',
|
|
192
|
+
m4v: 'video/x-m4v',
|
|
193
|
+
bin: 'application/octet-stream',
|
|
194
|
+
exe: 'application/vnd.microsoft.portable-executable',
|
|
195
|
+
dll: 'application/vnd.microsoft.portable-executable',
|
|
196
|
+
deb: 'application/vnd.debian.binary-package',
|
|
197
|
+
rpm: 'application/x-rpm',
|
|
198
|
+
iso: 'application/x-iso9660-image',
|
|
199
|
+
sh: 'application/x-sh',
|
|
200
|
+
bat: 'application/x-msdos-program',
|
|
201
|
+
ps1: 'application/x-powershell',
|
|
202
|
+
};
|
|
203
|
+
return mimeMap[extension];
|
|
204
|
+
}
|
|
108
205
|
}
|
|
109
206
|
exports.MultipartProcessor = MultipartProcessor;
|
package/dist/utils/server.js
CHANGED