expresso-macchiato 0.3.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/dist/index.js ADDED
@@ -0,0 +1,825 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/RouterWrapper.ts
2
+ var _express = require('express'); var _express2 = _interopRequireDefault(_express);
3
+
4
+ // src/_utils.ts
5
+ var _utilsloggerav = require('utils-logger-av');
6
+ var MyLogger = class extends _utilsloggerav.Logger {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.setFilePath = (filePath) => {
10
+ this.logFilePath = filePath;
11
+ };
12
+ this.fullLogOk = (service, message) => {
13
+ this.ok(`[${service.toUpperCase()}]: ${message}`);
14
+ this.logFile(`[${service.toUpperCase()}]: ${message}`);
15
+ };
16
+ this.fullLogNok = (service, error, ...args) => {
17
+ const errMessage = _nullishCoalesce(_optionalChain([error, 'optionalAccess', _2 => _2.message]), () => ( error));
18
+ this.nok(`[${service.toUpperCase()}]: ${errMessage}`);
19
+ this.logFile(`[${service.toUpperCase()}]: ${errMessage}, ${args.join(",")}`, "error");
20
+ };
21
+ }
22
+ };
23
+ var log = new MyLogger({ primaryColor: "cyan", logFilePath: process.env.ERROR_FILE_PATH });
24
+ var { fullLogOk, fullLogNok } = log;
25
+ var apiOk = (res, status = 200, contentType) => ({ result: res, status, contentType, isOk: true });
26
+ var apiNok = (err, status = 500) => ({ result: err, status, isOk: false });
27
+ var errorCatcher = (res, err, errorsList, errorString) => {
28
+ if (errorString) fullLogNok("api-dynamicdb", errorString);
29
+ if (err instanceof Error === false) {
30
+ res.status(500).send({ message: err });
31
+ return;
32
+ }
33
+ const error = _nullishCoalesce((_nullishCoalesce(errorsList, () => ( {})))[err.message], () => ( null));
34
+ if (!error) res.status(500).send({ message: err.message });
35
+ else res.status(_nullishCoalesce(error.status, () => ( 500))).send({ message: _nullishCoalesce(error.responseMessage, () => ( err.message)) });
36
+ };
37
+
38
+ // src/DynamicDbRouter.ts
39
+ var _typeorm = require('typeorm');
40
+
41
+ // src/DbConnector.ts
42
+ require('reflect-metadata');
43
+
44
+ var _DbConnector = class _DbConnector {
45
+ constructor(projectConfig) {
46
+ this.connect = async (entities, migrations, sync) => {
47
+ try {
48
+ if (!this.projectConfig.DB_DIALECT || !this.projectConfig.DB_NAME) throw new Error("[DB-CONNECTOR] Missing configs =>DB_DIALECT || DB_NAME");
49
+ const AppDataSource = new (0, _typeorm.DataSource)({
50
+ type: this.projectConfig.DB_DIALECT,
51
+ host: this.projectConfig.DB_HOST,
52
+ port: this.projectConfig.DB_PORT,
53
+ username: this.projectConfig.DB_USER,
54
+ password: this.projectConfig.DB_PASSWORD,
55
+ database: this.projectConfig.DB_NAME,
56
+ entities,
57
+ synchronize: _nullishCoalesce(sync, () => ( false)),
58
+ logging: false,
59
+ migrations
60
+ });
61
+ await AppDataSource.initialize();
62
+ if (_nullishCoalesce(sync, () => ( false))) await AppDataSource.synchronize();
63
+ else {
64
+ const executedMigrations = await AppDataSource.query(`SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'`);
65
+ const executedMigrationNames = executedMigrations.map((migration) => migration.name);
66
+ if (migrations) {
67
+ log.teal("[DB-CONNECTOR] Running migrations:");
68
+ for (let migration of migrations) {
69
+ if (executedMigrationNames.includes(migration)) log.gray(`- Migration ${migration} already executed, skipping.`);
70
+ else {
71
+ log.yellow(`- Executing migration ${migration}`);
72
+ await AppDataSource.runMigrations();
73
+ }
74
+ }
75
+ log.teal("[DB-CONNECTOR] Migrations done");
76
+ }
77
+ }
78
+ _DbConnector.DataSource = AppDataSource;
79
+ log.magenta(`[DB-CONNECTOR] ORM CONNECTED`);
80
+ } catch (err) {
81
+ log.logError(err);
82
+ }
83
+ };
84
+ this.projectConfig = projectConfig;
85
+ }
86
+ };
87
+ _DbConnector.getDataSource = () => _DbConnector.DataSource;
88
+ var DbConnector = _DbConnector;
89
+
90
+ // src/Swagger.ts
91
+ var _Swagger = class _Swagger {
92
+ };
93
+ // --- PRIVATE
94
+ _Swagger.fromExpressParamsToSwagger = (str) => str.replace(/:(\w+)(?=\/|$)/g, "{$1}");
95
+ _Swagger.restMethodToSwaggerKeyMapping = {
96
+ "GET": "get",
97
+ "POST": "post",
98
+ "PUT": "put",
99
+ "DELETE": "delete"
100
+ };
101
+ // ------------------------------------------------------- //
102
+ // ------------------------------------------------------- //
103
+ // ----------------------- GENERIC ----------------------- //
104
+ // ------------------------------------------------------- //
105
+ // ------------------------------------------------------- //
106
+ _Swagger.addServer = (server) => _Swagger.apiDocument.servers.push(server);
107
+ // Call at init to add current server
108
+ _Swagger.generateOpenAPIDocument = () => JSON.parse(JSON.stringify(_Swagger.apiDocument, null, 2));
109
+ // Generates json to be served in api
110
+ _Swagger.apiDocument = // static document to be populated
111
+ {
112
+ openapi: "3.0.4",
113
+ servers: [],
114
+ info: { title: "Dynamically Generated API", version: "1.0.0" },
115
+ paths: {},
116
+ components: {
117
+ schemas: {},
118
+ securitySchemes: {
119
+ bearerAuth: {
120
+ type: "http",
121
+ scheme: "bearer",
122
+ bearerFormat: "JWT"
123
+ }
124
+ }
125
+ },
126
+ security: [
127
+ { "bearerAuth": [] }
128
+ ]
129
+ };
130
+ // ------------------------------------------------------- //
131
+ // ------------------------------------------------------- //
132
+ // ----------------------- METHODS ----------------------- //
133
+ // ------------------------------------------------------- //
134
+ // ------------------------------------------------------- //
135
+ /**
136
+ * @Description
137
+ * - ❌ ROUTER-WRAPPER.
138
+ * - 🛠️ ADD-API-PATH
139
+ * - This method is not directly to insert inside the structur of addApiPath but is useful to create Schemas while not using the routerWrapper
140
+ */
141
+ _Swagger.addSchema = (schema, properties) => {
142
+ if (!_Swagger.apiDocument.components || !_Swagger.apiDocument.components.schemas) _Swagger.apiDocument.components = { schemas: {} };
143
+ if (!_Swagger.apiDocument.components.schemas[schema]) _Swagger.apiDocument.components.schemas[schema] = {
144
+ type: "object",
145
+ properties
146
+ };
147
+ };
148
+ /**
149
+ * @Description
150
+ * - ❌ ROUTER-WRAPPER.
151
+ * - 🛠️ ADD-API-PATH
152
+ * - This method is thought to reduce the code in the addApiPath method
153
+ * - You can create a schema while declaring it and associating to the path calling this method
154
+ */
155
+ _Swagger.getBasicPost = (schema, required, parameters, properties, description) => ({
156
+ responses: {},
157
+ parameters,
158
+ requestBody: {
159
+ required,
160
+ description,
161
+ schemaName: schema,
162
+ schema: {
163
+ [schema]: {
164
+ type: "object",
165
+ properties
166
+ }
167
+ }
168
+ }
169
+ });
170
+ /**
171
+ * @Description
172
+ * - ❌ **ROUTER-WRAPPER.**
173
+ * - 🛠️ **ADD-API-PATH**
174
+ * - This method is thought to reduce the code in the addApiPath method
175
+ */
176
+ _Swagger.getBasicGet = (parameters) => ({
177
+ responses: {},
178
+ parameters
179
+ });
180
+ /**
181
+ * @Description
182
+ * - ❌ ROUTER-WRAPPER.
183
+ * - This method is useful if you don't want to use the router wrapper but add your swagger schema anyway.
184
+ * - **It is thought to be used inside the library**, iterating through the paths of the router wrapper.
185
+ * - **Devs can't use inside the parameters of a RouterWrapper constructor**
186
+ * - You can create the structure of a specified path and method, with a schema name that should be created elsewhere
187
+ */
188
+ _Swagger.addSingleApiPath = (tag, basePath, _path, method, parameters, bodyObj, responses = {}) => {
189
+ const path2 = `${_Swagger.fromExpressParamsToSwagger(basePath)}${_Swagger.fromExpressParamsToSwagger(_path)}`;
190
+ const transcribedMethod = _Swagger.restMethodToSwaggerKeyMapping[method];
191
+ if (!_Swagger.apiDocument.paths[path2]) _Swagger.apiDocument.paths[path2] = {};
192
+ if (_Swagger.apiDocument.paths[path2][transcribedMethod]) return;
193
+ _Swagger.apiDocument.paths[path2][transcribedMethod] = {
194
+ responses,
195
+ parameters,
196
+ tags: [tag]
197
+ };
198
+ if (bodyObj) _Swagger.apiDocument.paths[path2][transcribedMethod].requestBody = {
199
+ required: _nullishCoalesce(bodyObj.required, () => ( false)),
200
+ description: bodyObj.description,
201
+ content: {
202
+ [_nullishCoalesce(bodyObj.comType, () => ( "application/json"))]: {
203
+ schema: { "$ref": "#/components/schemas/" + bodyObj.schema }
204
+ }
205
+ }
206
+ };
207
+ };
208
+ /**
209
+ * @Description
210
+ * - ❌ ROUTER-WRAPPER.
211
+ * - 🛠️ ADD-API-PATH
212
+ * - **This method is useful if you don't want to use the router wrapper but add your swagger schema anyway**.
213
+ * - You can use other other methods of this class to ease the writing
214
+ * - It is thought to create a full section of apis (hypotetically under one single tag) **while you create the schema in the meanwhile**
215
+ */
216
+ _Swagger._addApiPath = (tag, basePath, options) => {
217
+ const finalPaths = {};
218
+ const finalSchemas = {};
219
+ for (const _path in options) {
220
+ const path2 = `${basePath}${_path}`;
221
+ if (_optionalChain([options, 'access', _3 => _3[_path], 'access', _4 => _4.get, 'optionalAccess', _5 => _5.tags])) options[_path].get.tags = [tag];
222
+ finalPaths[path2] = {
223
+ get: options[_path].get,
224
+ $ref: options[_path].$ref,
225
+ head: options[_path].head,
226
+ parameters: options[_path].parameters
227
+ };
228
+ if (options[_path].get) options[_path].get.tags = [tag];
229
+ if (options[_path].post && options[_path].post.requestBody) {
230
+ finalPaths[path2].post = {
231
+ responses: {},
232
+ parameters: options[_path].post.parameters,
233
+ tags: [tag],
234
+ summary: options[_path].post.summary,
235
+ requestBody: {
236
+ required: options[_path].post.requestBody.required,
237
+ description: options[_path].post.requestBody.description,
238
+ content: {
239
+ "application/json": { schema: { "$ref": "#/components/schemas/" + options[_path].post.requestBody.schemaName } }
240
+ }
241
+ }
242
+ };
243
+ for (const key in options[_path].post.requestBody.schema) {
244
+ if (!finalSchemas[key]) finalSchemas[key] = options[_path].post.requestBody.schema[key];
245
+ }
246
+ }
247
+ if (options[_path].put && options[_path].put.requestBody) {
248
+ finalPaths[path2].put = {
249
+ responses: {},
250
+ tags: [tag],
251
+ parameters: options[_path].put.parameters,
252
+ summary: options[_path].put.summary,
253
+ requestBody: {
254
+ required: options[_path].put.requestBody.required,
255
+ description: options[_path].put.requestBody.description,
256
+ content: {
257
+ "application/json": { schema: { "$ref": "#/components/schemas/" + options[_path].put.requestBody.schemaName } }
258
+ }
259
+ }
260
+ };
261
+ for (const key in options[_path].put.requestBody.schema) {
262
+ if (!finalSchemas[key]) finalSchemas[key] = options[_path].put.requestBody.schema[key];
263
+ }
264
+ }
265
+ if (options[_path].delete) {
266
+ finalPaths[path2].delete = {
267
+ responses: {},
268
+ tags: [tag],
269
+ parameters: options[_path].delete.parameters,
270
+ summary: options[_path].delete.summary
271
+ };
272
+ if (options[_path].delete.requestBody) {
273
+ const finalComType = _nullishCoalesce(options[_path].delete.requestBody.comType, () => ( "application/json"));
274
+ finalPaths[path2].delete.requestBody = {
275
+ required: options[_path].delete.requestBody.required,
276
+ description: options[_path].delete.requestBody.description,
277
+ content: {
278
+ [finalComType]: {
279
+ schema: { "$ref": "#/components/schemas/" + options[_path].delete.requestBody.schemaName }
280
+ }
281
+ }
282
+ };
283
+ for (const key in options[_path].delete.requestBody.schema) {
284
+ if (!finalSchemas[key]) finalSchemas[key] = options[_path].delete.requestBody.schema[key];
285
+ }
286
+ }
287
+ }
288
+ }
289
+ for (const path2 in finalPaths) {
290
+ for (const method in finalPaths[path2]) {
291
+ const methodKey = method;
292
+ if (!_Swagger.apiDocument.paths[path2]) _Swagger.apiDocument.paths[path2] = {};
293
+ if (!_Swagger.apiDocument.paths[path2][methodKey]) _Swagger.apiDocument.paths[path2][methodKey] = finalPaths[path2][methodKey];
294
+ }
295
+ }
296
+ for (const schema in finalSchemas) {
297
+ if (!_Swagger.apiDocument.components || !_Swagger.apiDocument.components.schemas) _Swagger.apiDocument.components = { schemas: {} };
298
+ if (!_Swagger.apiDocument.components.schemas[schema]) _Swagger.apiDocument.components.schemas[schema] = finalSchemas[schema];
299
+ }
300
+ };
301
+ // ------------------------------------------------------- //
302
+ // ------------------------------------------------------- //
303
+ // ------------------------ MICRO ------------------------ //
304
+ // ------------------------------------------------------- //
305
+ // ------------------------------------------------------- //
306
+ /**
307
+ * @Description
308
+ * - ✅ ROUTER-WRAPPER
309
+ * - Returns the tipical /:id parameter
310
+ */
311
+ _Swagger.getIdParam = (pkLabel = "id") => ({ name: pkLabel, in: "path", required: true });
312
+ /**
313
+ * @Description
314
+ * - ✅ ROUTER-WRAPPER
315
+ * - Returns the tipical list pagination parameters
316
+ */
317
+ _Swagger.getPaginationParams = (options) => {
318
+ const listOptions = {
319
+ page: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _6 => _6.page]), () => ( 0)),
320
+ pageSize: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _7 => _7.pageSize]), () => ( 10)),
321
+ order: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _8 => _8.order]), () => ( "ASC")),
322
+ orderBy: _optionalChain([options, 'optionalAccess', _9 => _9.orderBy])
323
+ };
324
+ return Object.entries(listOptions).map(([key, val]) => ({ name: key, in: "query", default: val, required: false }));
325
+ };
326
+ /**
327
+ * @Description
328
+ * - ✅ ROUTER-WRAPPER
329
+ * - Creates the swagger schema expected by your api
330
+ */
331
+ _Swagger.createMultipartSchema = (name = "file", props) => ({
332
+ type: "object",
333
+ properties: {
334
+ [name]: { type: "string", format: "binary" },
335
+ ..._nullishCoalesce(props, () => ( {}))
336
+ }
337
+ });
338
+ /**
339
+ * @Description
340
+ * - ✅ ROUTER-WRAPPER
341
+ * - Creates the swagger schema expected by your api
342
+ */
343
+ _Swagger.createSchema = (props, required) => {
344
+ const finalSchemaObject = { type: "object", properties: {}, required };
345
+ for (const prop in props) {
346
+ if (typeof props[prop] === "string") finalSchemaObject.properties[prop] = { type: props[prop] };
347
+ else finalSchemaObject.properties[prop] = props[prop];
348
+ }
349
+ return finalSchemaObject;
350
+ };
351
+ var Swagger = _Swagger;
352
+
353
+ // src/DynamicDbRouter.ts
354
+ var _DynamicDbRouter = class _DynamicDbRouter {
355
+ };
356
+ _DynamicDbRouter.setTokenInstance = (tokenInstance) => {
357
+ _DynamicDbRouter.tokenInstance = tokenInstance;
358
+ };
359
+ _DynamicDbRouter.listOptionsKeys = ["pageSize", "page", "orderBy", "order"];
360
+ _DynamicDbRouter.getTypefromReflection = (typeormType) => _nullishCoalesce(_DynamicDbRouter.typeormToSwaggerMapper[typeormType], () => ( typeormType));
361
+ _DynamicDbRouter.typeormToSwaggerMapper = {
362
+ "text": "string",
363
+ "char varying": "string",
364
+ "char": "string",
365
+ "character varying": "string",
366
+ "character": "string",
367
+ "varchar": "string",
368
+ "varying character": "string",
369
+ "date": "date",
370
+ "datemultirange": "date",
371
+ "datetime": "date",
372
+ "int": "number",
373
+ "integer": "number",
374
+ "smallint": "number",
375
+ "bigint": "number",
376
+ "float": "number",
377
+ "real": "number",
378
+ "long": "number",
379
+ "double": "number",
380
+ "bit": "boolean",
381
+ "bool": "boolean",
382
+ "blob": "file"
383
+ };
384
+ _DynamicDbRouter.createDbRouter = (options) => {
385
+ if (!options.primaryKey) options.primaryKey = "id";
386
+ const avoidList = _nullishCoalesce(options.avoid, () => ( []));
387
+ if (!avoidList.includes("LIST")) options.router.get("/", async (req, res) => {
388
+ try {
389
+ let payload = null;
390
+ if (options.secure) {
391
+ if (_DynamicDbRouter.tokenInstance) payload = await _DynamicDbRouter.tokenInstance.authorize(req);
392
+ else throw new Error("Api secure option on but no token instance has been provided in the starter");
393
+ }
394
+ const listOptions = {};
395
+ for (const key in req.query) {
396
+ if (_DynamicDbRouter.listOptionsKeys.includes(key)) listOptions[key] = req.query[key];
397
+ }
398
+ const searchQuery = {};
399
+ for (const param of _nullishCoalesce(options.getParameters, () => ( []))) {
400
+ const val = _nullishCoalesce(req.query[param.name], () => ( void 0));
401
+ if (param.required && !val) throw new Error(`Params Error: ${param.name} required`);
402
+ if (val !== void 0) {
403
+ if (param.like) searchQuery[param.name] = _typeorm.Like.call(void 0, `%${val}%`);
404
+ else searchQuery[param.name] = _typeorm.Equal.call(void 0, val);
405
+ }
406
+ }
407
+ const secureSearchQuery = _DynamicDbRouter.setSecureParams("LIST", payload, options.secure);
408
+ const { page, pageSize, order, orderBy } = listOptions;
409
+ const entities = await options.entity.find({
410
+ where: { ...searchQuery, ...secureSearchQuery },
411
+ take: _nullishCoalesce(pageSize, () => ( 0)),
412
+ skip: (_nullishCoalesce(page, () => ( 0))) * (_nullishCoalesce(pageSize, () => ( 0))),
413
+ order: { [_nullishCoalesce(orderBy, () => ( options.primaryKey))]: _nullishCoalesce(order, () => ( "ASC")) },
414
+ select: options.returningProps
415
+ });
416
+ res.send(entities);
417
+ } catch (err) {
418
+ errorCatcher(res, err, void 0, `[GET] / => ${_nullishCoalesce(err.message, () => ( err))}`);
419
+ }
420
+ });
421
+ if (!avoidList.includes("GET")) options.router.get("/:id", async (req, res) => {
422
+ try {
423
+ let payload = null;
424
+ if (options.secure) {
425
+ if (_DynamicDbRouter.tokenInstance) payload = await _DynamicDbRouter.tokenInstance.authorize(req);
426
+ else throw new Error("Api secure option on but no token instance has been provided in the starter");
427
+ }
428
+ const secureSearchQuery = _DynamicDbRouter.setSecureParams("GET", payload, options.secure);
429
+ const id = req.params.id;
430
+ const singleEntity = await options.entity.findOne({
431
+ where: { [options.primaryKey]: _typeorm.Equal.call(void 0, id), ...secureSearchQuery },
432
+ select: options.returningProps
433
+ });
434
+ res.send(singleEntity);
435
+ } catch (err) {
436
+ errorCatcher(res, err, void 0, `[GET] /:id => ${_nullishCoalesce(err.message, () => ( err))}`);
437
+ }
438
+ });
439
+ if (!avoidList.includes("POST")) options.router.post("/", async (req, res) => {
440
+ try {
441
+ let payload = null;
442
+ if (options.secure) {
443
+ if (_DynamicDbRouter.tokenInstance) payload = await _DynamicDbRouter.tokenInstance.authorize(req);
444
+ else throw new Error("Api secure option on but no token instance has been provided in the starter");
445
+ }
446
+ const secureSearchQuery = _DynamicDbRouter.setSecureParams("POST", payload, options.secure);
447
+ const finalBody = {};
448
+ const body = req.body;
449
+ for (const key in body) {
450
+ if (key !== options.primaryKey && (!options.bodyParameters || _optionalChain([options, 'access', _10 => _10.bodyParameters, 'access', _11 => _11.properties, 'optionalAccess', _12 => _12[key]]))) {
451
+ finalBody[key] = body[key];
452
+ }
453
+ }
454
+ for (const key in secureSearchQuery) {
455
+ finalBody[key] = secureSearchQuery[key];
456
+ }
457
+ const newEntity = await options.entity.create(finalBody).save();
458
+ const result = {};
459
+ for (const key in newEntity) {
460
+ if (!options.returningProps || options.returningProps.includes(key)) result[key] = newEntity[key];
461
+ }
462
+ res.send(result);
463
+ } catch (err) {
464
+ errorCatcher(res, err, void 0, `[POST] / => ${_nullishCoalesce(err.message, () => ( err))}`);
465
+ }
466
+ });
467
+ if (!avoidList.includes("PUT")) options.router.put("/:id", async (req, res) => {
468
+ try {
469
+ let payload = null;
470
+ if (options.secure) {
471
+ if (_DynamicDbRouter.tokenInstance) payload = await _DynamicDbRouter.tokenInstance.authorize(req);
472
+ else throw new Error("Api secure option on but no token instance has been provided in the starter");
473
+ }
474
+ const secureSearchQuery = _DynamicDbRouter.setSecureParams("PUT", payload, options.secure);
475
+ const id = req.params.id;
476
+ const body = req.body;
477
+ const singleEntity = await options.entity.findOneBy({ [options.primaryKey]: _typeorm.Equal.call(void 0, id) });
478
+ if (singleEntity === null) throw new Error("Entity not found");
479
+ for (const key in body) {
480
+ if (key !== options.primaryKey && key in singleEntity) {
481
+ if (!options.bodyParameters || _optionalChain([options, 'access', _13 => _13.bodyParameters, 'access', _14 => _14.properties, 'optionalAccess', _15 => _15[key]])) {
482
+ singleEntity[key] = body[key];
483
+ }
484
+ }
485
+ }
486
+ for (const key in secureSearchQuery) {
487
+ singleEntity[key] = body[key];
488
+ }
489
+ await options.entity.save(singleEntity);
490
+ const result = {};
491
+ for (const key in singleEntity) {
492
+ if (!options.returningProps || options.returningProps.includes(key)) result[key] = singleEntity[key];
493
+ }
494
+ res.send(result);
495
+ } catch (err) {
496
+ errorCatcher(res, err, void 0, `[PUT] /:id => ${_nullishCoalesce(err.message, () => ( err))}`);
497
+ }
498
+ });
499
+ if (!avoidList.includes("DELETE")) options.router.delete("/:id", async (req, res) => {
500
+ try {
501
+ let payload = null;
502
+ if (options.secure) {
503
+ if (_DynamicDbRouter.tokenInstance) payload = await _DynamicDbRouter.tokenInstance.authorize(req);
504
+ else throw new Error("Api secure option on but no token instance has been provided in the starter");
505
+ }
506
+ const secureSearchQuery = _DynamicDbRouter.setSecureParams("DELETE", payload, options.secure);
507
+ const id = req.params.id;
508
+ const singleEntity = await options.entity.findOneBy({ [options.primaryKey]: _typeorm.Equal.call(void 0, id), ...secureSearchQuery });
509
+ if (singleEntity === null) throw new Error("Entity not found");
510
+ await options.entity.remove(singleEntity);
511
+ const result = {};
512
+ for (const key in singleEntity) {
513
+ if (!options.returningProps || options.returningProps.includes(key)) result[key] = singleEntity[key];
514
+ }
515
+ res.send(singleEntity);
516
+ } catch (err) {
517
+ errorCatcher(res, err, void 0, `[DELETE] /:id => ${_nullishCoalesce(err.message, () => ( err))}`);
518
+ }
519
+ });
520
+ return options.router;
521
+ };
522
+ _DynamicDbRouter.addDbRouterSwagger = (options) => {
523
+ if (!options.primaryKey) options.primaryKey = "id";
524
+ const avoidList = _nullishCoalesce(options.avoid, () => ( []));
525
+ try {
526
+ let schemaProperties = {};
527
+ if (options.bodyParameters && options.bodyParameters) {
528
+ const defaultOverridingSchema = options.bodyParameters;
529
+ for (const param in defaultOverridingSchema.properties) {
530
+ const paramValue = defaultOverridingSchema.properties[param];
531
+ schemaProperties[param] = {
532
+ type: paramValue.type,
533
+ required: paramValue.required
534
+ };
535
+ }
536
+ } else {
537
+ const metadata = DbConnector.getDataSource().getMetadata(options.entity);
538
+ for (const col of metadata.columns) {
539
+ if (col.propertyName === options.primaryKey) continue;
540
+ schemaProperties[col.propertyName] = {
541
+ type: _DynamicDbRouter.getTypefromReflection(col.type.toString())
542
+ };
543
+ }
544
+ }
545
+ if (!avoidList.includes("POST") || !avoidList.includes("PUT")) {
546
+ Swagger.addSchema(options.tag, schemaProperties);
547
+ }
548
+ } catch (err) {
549
+ if (err instanceof _typeorm.EntityMetadataNotFoundError)
550
+ log.logError(`[${options.entity.name}] Metadata required for db routing but not connected on the database`);
551
+ }
552
+ if (!avoidList.includes("LIST")) {
553
+ const listOptions = { page: 0, pageSize: 10, order: "ASC", orderBy: options.primaryKey };
554
+ Swagger.addSingleApiPath(options.tag, options.basePath, "/", "GET", [
555
+ ...Object.entries(listOptions).map(([key, val]) => ({ name: key, in: "query", default: val, required: false })),
556
+ ..._nullishCoalesce(options.getParameters, () => ( []))
557
+ ]);
558
+ }
559
+ if (!avoidList.includes("GET")) {
560
+ Swagger.addSingleApiPath(options.tag, options.basePath, `/{${options.primaryKey}}`, "GET", [
561
+ { name: options.primaryKey, in: "path", required: true },
562
+ ..._nullishCoalesce(options.getParameters, () => ( []))
563
+ ]);
564
+ }
565
+ if (!avoidList.includes("POST")) {
566
+ Swagger.addSingleApiPath(options.tag, options.basePath, "/", "POST", void 0, { schema: options.tag });
567
+ }
568
+ if (!avoidList.includes("PUT")) {
569
+ Swagger.addSingleApiPath(
570
+ options.tag,
571
+ options.basePath,
572
+ `/{${options.primaryKey}}`,
573
+ "PUT",
574
+ [{ name: options.primaryKey, in: "path", required: true }],
575
+ { schema: options.tag }
576
+ );
577
+ }
578
+ if (!avoidList.includes("DELETE")) {
579
+ Swagger.addSingleApiPath(options.tag, options.basePath, `/{${options.primaryKey}}`, "DELETE", [
580
+ { name: options.primaryKey, in: "path", required: true }
581
+ ]);
582
+ }
583
+ };
584
+ _DynamicDbRouter.setSecureParams = (method, payload, secure) => {
585
+ const searchQuery = {};
586
+ if (secure !== void 0 && typeof secure === "object" && payload !== null) {
587
+ for (const secureParam in secure) {
588
+ const secureParamVal = secure[secureParam];
589
+ if (secureParamVal.methods === "*" || secureParamVal.methods.includes(method) && payload[secureParamVal.tokenKey]) {
590
+ if (["LIST", "GET", "DELETE"].includes(method)) searchQuery[secureParam] = _typeorm.Equal.call(void 0, payload[secureParamVal.tokenKey]);
591
+ else searchQuery[secureParam] = payload[secureParamVal.tokenKey];
592
+ }
593
+ }
594
+ }
595
+ return searchQuery;
596
+ };
597
+ var DynamicDbRouter = _DynamicDbRouter;
598
+
599
+ // src/RouterWrapper.ts
600
+ var RouterWrapper = class {
601
+ constructor(data) {
602
+ this.createExpressRouter = () => {
603
+ const newRouter = _express.Router.call(void 0, );
604
+ if (this.data.dbRouting) {
605
+ const avoidOptionsForDefinedApis = [];
606
+ if (_optionalChain([this, 'access', _16 => _16.data, 'access', _17 => _17.apis, 'optionalAccess', _18 => _18["/"], 'optionalAccess', _19 => _19.GET])) avoidOptionsForDefinedApis.push("LIST");
607
+ if (_optionalChain([this, 'access', _20 => _20.data, 'access', _21 => _21.apis, 'optionalAccess', _22 => _22["/:id"], 'optionalAccess', _23 => _23.GET])) avoidOptionsForDefinedApis.push("GET");
608
+ if (_optionalChain([this, 'access', _24 => _24.data, 'access', _25 => _25.apis, 'optionalAccess', _26 => _26["/"], 'optionalAccess', _27 => _27.POST])) avoidOptionsForDefinedApis.push("POST");
609
+ if (_optionalChain([this, 'access', _28 => _28.data, 'access', _29 => _29.apis, 'optionalAccess', _30 => _30["/:id"], 'optionalAccess', _31 => _31.PUT])) avoidOptionsForDefinedApis.push("PUT");
610
+ if (_optionalChain([this, 'access', _32 => _32.data, 'access', _33 => _33.apis, 'optionalAccess', _34 => _34["/:id"], 'optionalAccess', _35 => _35.DELETE])) avoidOptionsForDefinedApis.push("DELETE");
611
+ for (const avoid of _nullishCoalesce(this.data.dbRouting.avoid, () => ( []))) {
612
+ if (!avoidOptionsForDefinedApis.includes(avoid)) avoidOptionsForDefinedApis.push(avoid);
613
+ }
614
+ const { entity, bodyParameters, getParameters, primaryKey, secure, returningProps } = this.data.dbRouting;
615
+ DynamicDbRouter.createDbRouter({
616
+ entity,
617
+ bodyParameters,
618
+ getParameters,
619
+ primaryKey,
620
+ router: newRouter,
621
+ basePath: this.data.basePath,
622
+ tag: this.data.tag,
623
+ avoid: avoidOptionsForDefinedApis,
624
+ secure,
625
+ returningProps
626
+ });
627
+ }
628
+ if (this.data.swaggerNewSchemas) {
629
+ for (const schema in this.data.swaggerNewSchemas) Swagger.addSchema(schema, this.data.swaggerNewSchemas[schema].properties);
630
+ }
631
+ for (const _path in _nullishCoalesce(this.data.apis, () => ( {}))) {
632
+ const path2 = _path.startsWith("/") ? _path : `/${_path}`;
633
+ if (!this.data.apis || !this.data.apis[path2]) continue;
634
+ for (const method in this.data.apis[path2]) {
635
+ if ((_nullishCoalesce(_optionalChain([this, 'access', _36 => _36.data, 'access', _37 => _37.apis, 'optionalAccess', _38 => _38[path2], 'optionalAccess', _39 => _39[method]]), () => ( void 0))) === void 0) continue;
636
+ const currentMethod = this.data.apis[path2][method];
637
+ const middlewares = _nullishCoalesce(currentMethod.middlewares, () => ( []));
638
+ const callBackFunction = async (req, res) => {
639
+ try {
640
+ const handlerRes = await currentMethod.handler(req, res);
641
+ if (handlerRes.contentType) {
642
+ res.set("Content-Type", handlerRes.contentType);
643
+ res.status(_nullishCoalesce(handlerRes.status, () => ( 200))).send(handlerRes.result);
644
+ } else if (typeof handlerRes.result === "object") res.status(handlerRes.status).json(handlerRes.result);
645
+ else res.status(handlerRes.status).send(handlerRes.result);
646
+ } catch (err) {
647
+ fullLogNok("api", `[${method}] ${path2} => ${_nullishCoalesce(err.message, () => ( err))}`);
648
+ res.status(500).send(_nullishCoalesce(_optionalChain([err, 'optionalAccess', _40 => _40.message]), () => ( err)));
649
+ }
650
+ };
651
+ if (method === "GET") newRouter.get(path2, middlewares, callBackFunction);
652
+ else if (method === "POST") newRouter.post(path2, middlewares, callBackFunction);
653
+ else if (method === "PUT") newRouter.put(path2, middlewares, callBackFunction);
654
+ else if (method === "DELETE") newRouter.delete(path2, middlewares, callBackFunction);
655
+ Swagger.addSingleApiPath(
656
+ this.data.tag,
657
+ this.data.basePath,
658
+ path2,
659
+ method,
660
+ currentMethod.swaggerParameters,
661
+ currentMethod.swaggerBody,
662
+ currentMethod.swaggerResponses
663
+ );
664
+ }
665
+ }
666
+ if (this.data.dbRouting) {
667
+ const { entity, bodyParameters, getParameters, primaryKey } = this.data.dbRouting;
668
+ DynamicDbRouter.addDbRouterSwagger({
669
+ entity,
670
+ bodyParameters,
671
+ getParameters,
672
+ primaryKey,
673
+ router: newRouter,
674
+ basePath: this.data.basePath,
675
+ tag: this.data.tag,
676
+ avoid: this.data.dbRouting.avoid,
677
+ secure: _optionalChain([this, 'access', _41 => _41.data, 'access', _42 => _42.dbRouting, 'optionalAccess', _43 => _43.secure])
678
+ });
679
+ }
680
+ return newRouter;
681
+ };
682
+ this.data = data;
683
+ this.basePath = data.basePath.startsWith("/") ? data.basePath : `/${data.basePath}`;
684
+ }
685
+ };
686
+
687
+ // src/Starter.ts
688
+
689
+ var _http = require('http'); var _http2 = _interopRequireDefault(_http);
690
+ var _path2 = require('path'); var _path3 = _interopRequireDefault(_path2);
691
+ var _swaggeruiexpress = require('swagger-ui-express'); var _swaggeruiexpress2 = _interopRequireDefault(_swaggeruiexpress);
692
+ var Starter = class {
693
+ constructor(options) {
694
+ this.init = async (options) => {
695
+ const app = _express2.default.call(void 0, );
696
+ if (options.db) {
697
+ await this.initDb(options.projectConfig, options.db.entities, options.db.migrations, options.db.sync);
698
+ if (options.db.afterDbConnection) await options.db.afterDbConnection();
699
+ }
700
+ for (const plugin of _nullishCoalesce(options.plugins, () => ( []))) app.use(plugin);
701
+ for (const router of _nullishCoalesce(options.routers, () => ( []))) app.use(router.basePath, router.createExpressRouter());
702
+ if (options.swagger === void 0 || options.swagger) {
703
+ Swagger.addServer({ url: `http://127.0.0.1:${options.projectConfig.SERVER_PORT}` });
704
+ app.get("/swagger", (_, res) => {
705
+ const openAPIDocument = Swagger.generateOpenAPIDocument();
706
+ res.json(openAPIDocument);
707
+ });
708
+ app.use("/swagger-ui", _swaggeruiexpress2.default.serve, _swaggeruiexpress2.default.setup(void 0, {
709
+ swaggerOptions: {
710
+ url: "/swagger"
711
+ }
712
+ }));
713
+ }
714
+ if (options.tokenOptions) DynamicDbRouter.setTokenInstance(options.tokenOptions.tokenInstance);
715
+ if (options.clientPath) {
716
+ const clientPath = _path3.default.resolve(process.cwd(), "client");
717
+ app.use(_express2.default.static(clientPath));
718
+ app.get("/apiUrl", (_, res) => res.send(options.projectConfig.API_URL));
719
+ if (_optionalChain([options, 'access', _44 => _44.tokenOptions, 'optionalAccess', _45 => _45.api])) {
720
+ app.get(_nullishCoalesce(options.tokenOptions.api.path, () => ( "/api/auth")), options.tokenOptions.api.callback);
721
+ }
722
+ app.get("*", (_, res) => {
723
+ res.sendFile(_path3.default.join(clientPath, "index.html"));
724
+ });
725
+ }
726
+ if (options.socket === void 0 || options.socket === false) {
727
+ if (options.beforeStartListening) options.beforeStartListening(app);
728
+ app.listen(options.projectConfig.SERVER_PORT, () => log.base("Server started.", `Listening on port ${options.projectConfig.SERVER_PORT}`));
729
+ } else if (options.socket === true) {
730
+ const server = _http2.default.createServer(app);
731
+ if (options.beforeStartListening) options.beforeStartListening(app, server);
732
+ server.listen(options.projectConfig.SERVER_PORT, () => log.base("Server started.", `Listening on port ${options.projectConfig.SERVER_PORT}`));
733
+ }
734
+ };
735
+ this.initDb = async (projectConfig, entities, migrations, sync) => {
736
+ const connector = new DbConnector(projectConfig);
737
+ await connector.connect(entities, migrations, sync);
738
+ };
739
+ try {
740
+ log.setFilePath(options.projectConfig.ERROR_FILE_PATH);
741
+ this.init(options);
742
+ } catch (err) {
743
+ log.logError(err);
744
+ }
745
+ }
746
+ };
747
+
748
+ // src/Token.ts
749
+ var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
750
+ var _nodejose = require('node-jose'); var _nodejose2 = _interopRequireDefault(_nodejose);
751
+ var Token = class {
752
+ constructor(tokenPayload) {
753
+ this.keyStore = null;
754
+ // Metodo per autorizzare una richiesta
755
+ this.authorize = async (req) => {
756
+ const token = _nullishCoalesce(_optionalChain([req, 'access', _46 => _46.headers, 'access', _47 => _47.authorization, 'optionalAccess', _48 => _48.split, 'call', _49 => _49("Bearer "), 'optionalAccess', _50 => _50[1]]), () => ( null));
757
+ if (token === null) throw new Error("UNAUTH");
758
+ const payload = await this.verifyJWE(token);
759
+ return payload;
760
+ };
761
+ if (!tokenPayload.SECRET_KEY) throw new Error("SECRET_KEY is required if you want to use the Token Class");
762
+ this.SECRET_KEY = tokenPayload.SECRET_KEY;
763
+ this.ExpTime = tokenPayload.ExpTime;
764
+ this.EncAlgorithm = tokenPayload.EncAlgorithm;
765
+ this.KeyLength = tokenPayload.KeyLength;
766
+ }
767
+ // Inizializza la KeyStore
768
+ async getKey() {
769
+ if (!this.keyStore) {
770
+ this.keyStore = _nodejose2.default.JWK.createKeyStore();
771
+ const encryptionKey = this.deriveEncryptionKey();
772
+ const key = await _nodejose2.default.JWK.asKey({
773
+ kty: "oct",
774
+ k: _nodejose2.default.util.base64url.encode(encryptionKey)
775
+ });
776
+ await this.keyStore.add(key);
777
+ }
778
+ return this.keyStore.all({ use: "enc" })[0];
779
+ }
780
+ // Metodo per derivare chiave di cifratura
781
+ deriveEncryptionKey() {
782
+ return _crypto2.default.createHash("sha256").update(this.SECRET_KEY).digest().slice(0, this.KeyLength);
783
+ }
784
+ // Metodo per generare token cifrato (JWE)
785
+ async generateJWE(payload) {
786
+ const key = await this.getKey();
787
+ const input = Buffer.from(JSON.stringify({
788
+ ...payload,
789
+ iat: Math.floor(Date.now() / 1e3),
790
+ exp: Math.floor(Date.now() / 1e3) + this.ExpTime
791
+ }));
792
+ const jwe = await _nodejose2.default.JWE.createEncrypt({
793
+ format: "compact",
794
+ fields: { alg: "dir", enc: this.EncAlgorithm }
795
+ }, key).update(input).final();
796
+ return jwe;
797
+ }
798
+ // Metodo per decifrare token JWE
799
+ async verifyJWE(token) {
800
+ try {
801
+ const key = await this.getKey();
802
+ const result = await _nodejose2.default.JWE.createDecrypt(key).decrypt(token);
803
+ const payload = JSON.parse(result.plaintext.toString());
804
+ const now = Math.floor(Date.now() / 1e3);
805
+ if (payload.exp && payload.exp < now) {
806
+ throw new Error("Token expired");
807
+ }
808
+ return payload;
809
+ } catch (error) {
810
+ throw new Error("Token decryption failed");
811
+ }
812
+ }
813
+ };
814
+
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+ exports.RouterWrapper = RouterWrapper; exports.Starter = Starter; exports.Swagger = Swagger; exports.Token = Token; exports.apiNok = apiNok; exports.apiOk = apiOk; exports.errorCatcher = errorCatcher; exports.fullLogNok = fullLogNok; exports.fullLogOk = fullLogOk; exports.log = log;