@hemia/core 0.0.4 → 0.0.5
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/hemia-core.esm.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import express, { Router } from 'express';
|
|
3
|
-
import { METADATA_KEYS, ParamType,
|
|
3
|
+
import { METADATA_KEYS, ParamType, ApiResponse, isRedirectResponse } from '@hemia/common';
|
|
4
4
|
import { TRACE_METADATA_KEY } from '@hemia/trace-manager';
|
|
5
5
|
import { traceMiddleware } from '@hemia/app-context';
|
|
6
6
|
import { plainToInstance } from 'class-transformer';
|
|
7
|
+
import multer from 'multer';
|
|
7
8
|
import { injectable, inject } from 'inversify';
|
|
8
9
|
import { AUTH_SERVICE_ID, AuthService } from '@hemia/auth-sdk';
|
|
9
10
|
|
|
@@ -80,7 +81,7 @@ class ResponseSerializer {
|
|
|
80
81
|
* @param container Instancia del Container de Inversify
|
|
81
82
|
* @param controllerIdentifiers Array de Symbols (o Clases si usas self-binding)
|
|
82
83
|
*/
|
|
83
|
-
async function registerRoutes(app, container, controllerIdentifiers, onTraceFinishCallback) {
|
|
84
|
+
async function registerRoutes(app, container, controllerIdentifiers, onTraceFinishCallback, multerOptions) {
|
|
84
85
|
for (const identifier of controllerIdentifiers) {
|
|
85
86
|
const instance = await container.getAsync(identifier);
|
|
86
87
|
const prototype = Object.getPrototypeOf(instance);
|
|
@@ -102,6 +103,14 @@ async function registerRoutes(app, container, controllerIdentifiers, onTraceFini
|
|
|
102
103
|
// Leer metadata de headers y redirect
|
|
103
104
|
const headersMetadata = Reflect.getMetadata(METADATA_KEYS.HEADERS, ControllerClass, route.methodName) || [];
|
|
104
105
|
const redirectMetadata = Reflect.getMetadata(METADATA_KEYS.REDIRECT, ControllerClass, route.methodName);
|
|
106
|
+
const needsFile = paramsMetadata.some(p => p.type === ParamType.FILE);
|
|
107
|
+
const needsFiles = paramsMetadata.some(p => p.type === ParamType.FILES);
|
|
108
|
+
if (needsFiles) {
|
|
109
|
+
handlers.push(multer(multerOptions).array('files'));
|
|
110
|
+
}
|
|
111
|
+
else if (needsFile) {
|
|
112
|
+
handlers.push(multer(multerOptions).single('file'));
|
|
113
|
+
}
|
|
105
114
|
const mainHandler = async (req, res, next) => {
|
|
106
115
|
try {
|
|
107
116
|
const args = [];
|
|
@@ -138,11 +147,59 @@ async function registerRoutes(app, container, controllerIdentifiers, onTraceFini
|
|
|
138
147
|
case ParamType.SESSION:
|
|
139
148
|
args[param.index] = req.session;
|
|
140
149
|
break;
|
|
150
|
+
case ParamType.COOKIES:
|
|
151
|
+
args[param.index] = param.data ? req.cookies?.[param.data] : req.cookies;
|
|
152
|
+
break;
|
|
153
|
+
case ParamType.FILE:
|
|
154
|
+
args[param.index] = req.file;
|
|
155
|
+
break;
|
|
156
|
+
case ParamType.FILES:
|
|
157
|
+
args[param.index] = req.files;
|
|
158
|
+
break;
|
|
159
|
+
case ParamType.USER:
|
|
160
|
+
args[param.index] = req.user;
|
|
161
|
+
break;
|
|
162
|
+
case ParamType.IP:
|
|
163
|
+
args[param.index] = req.ip;
|
|
164
|
+
break;
|
|
165
|
+
case ParamType.LOCALE:
|
|
166
|
+
args[param.index] = req.acceptsLanguages()[0];
|
|
167
|
+
break;
|
|
168
|
+
case ParamType.HOST:
|
|
169
|
+
args[param.index] = req.hostname;
|
|
170
|
+
break;
|
|
171
|
+
case ParamType.PERMISSIONS:
|
|
172
|
+
args[param.index] = req.permissions;
|
|
173
|
+
break;
|
|
174
|
+
case ParamType.CONTEXT:
|
|
175
|
+
args[param.index] = req.context;
|
|
176
|
+
break;
|
|
141
177
|
default: args[param.index] = undefined;
|
|
142
178
|
}
|
|
143
179
|
});
|
|
144
180
|
}
|
|
181
|
+
// CUSTOM PARAMETERS
|
|
182
|
+
const customs = Reflect.getMetadata(METADATA_KEYS.CUSTOMS, instance, route.methodName) || [];
|
|
183
|
+
for (const { index, key } of customs) {
|
|
184
|
+
args[index] = req.customData?.[key];
|
|
185
|
+
}
|
|
186
|
+
// TRANSFORMACIÓN DE PARÁMETROS
|
|
187
|
+
const transformers = Reflect.getMetadata(METADATA_KEYS.TRANSFORMERS, instance, route.methodName) || [];
|
|
188
|
+
for (const { index, transformer } of transformers) {
|
|
189
|
+
args[index] = transformer(args[index]);
|
|
190
|
+
}
|
|
191
|
+
// VALIDACIÓN DE PARÁMETROS
|
|
192
|
+
const validators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, instance, route.methodName) || [];
|
|
193
|
+
for (const { index, validator } of validators) {
|
|
194
|
+
const value = args[index];
|
|
195
|
+
const result = validator(value);
|
|
196
|
+
if (result === false) {
|
|
197
|
+
return res.status(400).json(ApiResponse.error('Validation failed', undefined, 400));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// EJECUCIÓN DEL CONTROLLER
|
|
145
201
|
const result = await instance[route.methodName](...args);
|
|
202
|
+
// SERIALIZACIÓN DE RESULTADO
|
|
146
203
|
const serializeDto = Reflect.getMetadata(METADATA_KEYS.SERIALIZE, methodHandler) || Reflect.getMetadata(METADATA_KEYS.SERIALIZE, ControllerClass);
|
|
147
204
|
let finalResult = result;
|
|
148
205
|
if (serializeDto) {
|
|
@@ -264,8 +321,8 @@ class HemiaFactory {
|
|
|
264
321
|
*/
|
|
265
322
|
static async create(container, controllers, options = {}) {
|
|
266
323
|
const app = express();
|
|
267
|
-
app.use(express.json());
|
|
268
|
-
app.use(express.urlencoded({ extended: true }));
|
|
324
|
+
app.use(express.json(options.jsonOptions));
|
|
325
|
+
app.use(express.urlencoded({ extended: true, ...options.urlencodedOptions }));
|
|
269
326
|
if (options.middlewares && Array.isArray(options.middlewares)) {
|
|
270
327
|
options.middlewares.forEach(middleware => {
|
|
271
328
|
app.use(middleware);
|
|
@@ -296,7 +353,14 @@ class HemiaFactory {
|
|
|
296
353
|
container.bind(controllerClass).toSelf();
|
|
297
354
|
}
|
|
298
355
|
});
|
|
299
|
-
await registerRoutes(app, container, controllers, options.onTraceFinish || (() => { }));
|
|
356
|
+
await registerRoutes(app, container, controllers, options.onTraceFinish || (() => { }), options.multerOptions);
|
|
357
|
+
app.use((err, req, res, next) => {
|
|
358
|
+
console.error('[Hemia] Unhandled error:', err);
|
|
359
|
+
res.status(err.status || 500).json({
|
|
360
|
+
error: err.message || 'Internal Server Error',
|
|
361
|
+
details: err.details || undefined,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
300
364
|
if (options.logger !== false) {
|
|
301
365
|
console.log(`[Hemia] Application initialized with ${controllers.length} controllers.`);
|
|
302
366
|
}
|
package/dist/hemia-core.js
CHANGED
|
@@ -6,6 +6,7 @@ var common = require('@hemia/common');
|
|
|
6
6
|
var traceManager = require('@hemia/trace-manager');
|
|
7
7
|
var appContext = require('@hemia/app-context');
|
|
8
8
|
var classTransformer = require('class-transformer');
|
|
9
|
+
var multer = require('multer');
|
|
9
10
|
var inversify = require('inversify');
|
|
10
11
|
var authSdk = require('@hemia/auth-sdk');
|
|
11
12
|
|
|
@@ -82,7 +83,7 @@ class ResponseSerializer {
|
|
|
82
83
|
* @param container Instancia del Container de Inversify
|
|
83
84
|
* @param controllerIdentifiers Array de Symbols (o Clases si usas self-binding)
|
|
84
85
|
*/
|
|
85
|
-
async function registerRoutes(app, container, controllerIdentifiers, onTraceFinishCallback) {
|
|
86
|
+
async function registerRoutes(app, container, controllerIdentifiers, onTraceFinishCallback, multerOptions) {
|
|
86
87
|
for (const identifier of controllerIdentifiers) {
|
|
87
88
|
const instance = await container.getAsync(identifier);
|
|
88
89
|
const prototype = Object.getPrototypeOf(instance);
|
|
@@ -104,6 +105,14 @@ async function registerRoutes(app, container, controllerIdentifiers, onTraceFini
|
|
|
104
105
|
// Leer metadata de headers y redirect
|
|
105
106
|
const headersMetadata = Reflect.getMetadata(common.METADATA_KEYS.HEADERS, ControllerClass, route.methodName) || [];
|
|
106
107
|
const redirectMetadata = Reflect.getMetadata(common.METADATA_KEYS.REDIRECT, ControllerClass, route.methodName);
|
|
108
|
+
const needsFile = paramsMetadata.some(p => p.type === common.ParamType.FILE);
|
|
109
|
+
const needsFiles = paramsMetadata.some(p => p.type === common.ParamType.FILES);
|
|
110
|
+
if (needsFiles) {
|
|
111
|
+
handlers.push(multer(multerOptions).array('files'));
|
|
112
|
+
}
|
|
113
|
+
else if (needsFile) {
|
|
114
|
+
handlers.push(multer(multerOptions).single('file'));
|
|
115
|
+
}
|
|
107
116
|
const mainHandler = async (req, res, next) => {
|
|
108
117
|
try {
|
|
109
118
|
const args = [];
|
|
@@ -140,11 +149,59 @@ async function registerRoutes(app, container, controllerIdentifiers, onTraceFini
|
|
|
140
149
|
case common.ParamType.SESSION:
|
|
141
150
|
args[param.index] = req.session;
|
|
142
151
|
break;
|
|
152
|
+
case common.ParamType.COOKIES:
|
|
153
|
+
args[param.index] = param.data ? req.cookies?.[param.data] : req.cookies;
|
|
154
|
+
break;
|
|
155
|
+
case common.ParamType.FILE:
|
|
156
|
+
args[param.index] = req.file;
|
|
157
|
+
break;
|
|
158
|
+
case common.ParamType.FILES:
|
|
159
|
+
args[param.index] = req.files;
|
|
160
|
+
break;
|
|
161
|
+
case common.ParamType.USER:
|
|
162
|
+
args[param.index] = req.user;
|
|
163
|
+
break;
|
|
164
|
+
case common.ParamType.IP:
|
|
165
|
+
args[param.index] = req.ip;
|
|
166
|
+
break;
|
|
167
|
+
case common.ParamType.LOCALE:
|
|
168
|
+
args[param.index] = req.acceptsLanguages()[0];
|
|
169
|
+
break;
|
|
170
|
+
case common.ParamType.HOST:
|
|
171
|
+
args[param.index] = req.hostname;
|
|
172
|
+
break;
|
|
173
|
+
case common.ParamType.PERMISSIONS:
|
|
174
|
+
args[param.index] = req.permissions;
|
|
175
|
+
break;
|
|
176
|
+
case common.ParamType.CONTEXT:
|
|
177
|
+
args[param.index] = req.context;
|
|
178
|
+
break;
|
|
143
179
|
default: args[param.index] = undefined;
|
|
144
180
|
}
|
|
145
181
|
});
|
|
146
182
|
}
|
|
183
|
+
// CUSTOM PARAMETERS
|
|
184
|
+
const customs = Reflect.getMetadata(common.METADATA_KEYS.CUSTOMS, instance, route.methodName) || [];
|
|
185
|
+
for (const { index, key } of customs) {
|
|
186
|
+
args[index] = req.customData?.[key];
|
|
187
|
+
}
|
|
188
|
+
// TRANSFORMACIÓN DE PARÁMETROS
|
|
189
|
+
const transformers = Reflect.getMetadata(common.METADATA_KEYS.TRANSFORMERS, instance, route.methodName) || [];
|
|
190
|
+
for (const { index, transformer } of transformers) {
|
|
191
|
+
args[index] = transformer(args[index]);
|
|
192
|
+
}
|
|
193
|
+
// VALIDACIÓN DE PARÁMETROS
|
|
194
|
+
const validators = Reflect.getMetadata(common.METADATA_KEYS.VALIDATORS, instance, route.methodName) || [];
|
|
195
|
+
for (const { index, validator } of validators) {
|
|
196
|
+
const value = args[index];
|
|
197
|
+
const result = validator(value);
|
|
198
|
+
if (result === false) {
|
|
199
|
+
return res.status(400).json(common.ApiResponse.error('Validation failed', undefined, 400));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// EJECUCIÓN DEL CONTROLLER
|
|
147
203
|
const result = await instance[route.methodName](...args);
|
|
204
|
+
// SERIALIZACIÓN DE RESULTADO
|
|
148
205
|
const serializeDto = Reflect.getMetadata(common.METADATA_KEYS.SERIALIZE, methodHandler) || Reflect.getMetadata(common.METADATA_KEYS.SERIALIZE, ControllerClass);
|
|
149
206
|
let finalResult = result;
|
|
150
207
|
if (serializeDto) {
|
|
@@ -266,8 +323,8 @@ class HemiaFactory {
|
|
|
266
323
|
*/
|
|
267
324
|
static async create(container, controllers, options = {}) {
|
|
268
325
|
const app = express();
|
|
269
|
-
app.use(express.json());
|
|
270
|
-
app.use(express.urlencoded({ extended: true }));
|
|
326
|
+
app.use(express.json(options.jsonOptions));
|
|
327
|
+
app.use(express.urlencoded({ extended: true, ...options.urlencodedOptions }));
|
|
271
328
|
if (options.middlewares && Array.isArray(options.middlewares)) {
|
|
272
329
|
options.middlewares.forEach(middleware => {
|
|
273
330
|
app.use(middleware);
|
|
@@ -298,7 +355,14 @@ class HemiaFactory {
|
|
|
298
355
|
container.bind(controllerClass).toSelf();
|
|
299
356
|
}
|
|
300
357
|
});
|
|
301
|
-
await registerRoutes(app, container, controllers, options.onTraceFinish || (() => { }));
|
|
358
|
+
await registerRoutes(app, container, controllers, options.onTraceFinish || (() => { }), options.multerOptions);
|
|
359
|
+
app.use((err, req, res, next) => {
|
|
360
|
+
console.error('[Hemia] Unhandled error:', err);
|
|
361
|
+
res.status(err.status || 500).json({
|
|
362
|
+
error: err.message || 'Internal Server Error',
|
|
363
|
+
details: err.details || undefined,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
302
366
|
if (options.logger !== false) {
|
|
303
367
|
console.log(`[Hemia] Application initialized with ${controllers.length} controllers.`);
|
|
304
368
|
}
|
|
@@ -7,4 +7,4 @@ export type TraceFinishCallback = (payloads: any, context: any, res: Response) =
|
|
|
7
7
|
* @param container Instancia del Container de Inversify
|
|
8
8
|
* @param controllerIdentifiers Array de Symbols (o Clases si usas self-binding)
|
|
9
9
|
*/
|
|
10
|
-
export declare function registerRoutes(app: Express, container: Container, controllerIdentifiers: any[], onTraceFinishCallback: TraceFinishCallback): Promise<void>;
|
|
10
|
+
export declare function registerRoutes(app: Express, container: Container, controllerIdentifiers: any[], onTraceFinishCallback: TraceFinishCallback, multerOptions?: any): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hemia/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Core utilities for Hemia projects",
|
|
5
5
|
"main": "dist/hemia-core.js",
|
|
6
6
|
"module": "dist/hemia-core.esm.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@rollup/plugin-commonjs": "^26.0.1",
|
|
18
18
|
"@rollup/plugin-json": "^6.1.0",
|
|
19
19
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
20
|
-
"@hemia/common": "^0.0.
|
|
20
|
+
"@hemia/common": "^0.0.7",
|
|
21
21
|
"@hemia/app-context": "^0.0.6",
|
|
22
22
|
"@hemia/trace-manager": "^0.0.9",
|
|
23
23
|
"@hemia/auth-sdk": "^0.0.9",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@types/jest": "^29.5.14",
|
|
28
28
|
"@types/node": "^22.3.0",
|
|
29
29
|
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
30
|
+
"@types/multer": "^2.0.0",
|
|
30
31
|
"class-transformer": "^0.5.1",
|
|
31
32
|
"events": "^3.3.0",
|
|
32
33
|
"jest": "^29.7.0",
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
36
37
|
"ts-jest": "^29.2.5",
|
|
37
38
|
"ts-node": "^8.9.0",
|
|
38
|
-
"typescript": "^5.5.4"
|
|
39
|
+
"typescript": "^5.5.4",
|
|
40
|
+
"multer": "^2.0.2"
|
|
39
41
|
},
|
|
40
42
|
"author": "",
|
|
41
43
|
"license": "ISC",
|
|
@@ -46,6 +48,10 @@
|
|
|
46
48
|
"class-transformer": "^0.5.1",
|
|
47
49
|
"express": "^5.0.0",
|
|
48
50
|
"inversify": "^7.0.0",
|
|
49
|
-
"reflect-metadata": "^0.2.2"
|
|
51
|
+
"reflect-metadata": "^0.2.2",
|
|
52
|
+
"multer": "^2.0.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
|
|
50
56
|
}
|
|
51
57
|
}
|