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.mjs ADDED
@@ -0,0 +1,825 @@
1
+ // src/RouterWrapper.ts
2
+ import { Router } from "express";
3
+
4
+ // src/_utils.ts
5
+ import { Logger } from "utils-logger-av";
6
+ var MyLogger = class extends 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 = error?.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 = (errorsList ?? {})[err.message] ?? null;
34
+ if (!error) res.status(500).send({ message: err.message });
35
+ else res.status(error.status ?? 500).send({ message: error.responseMessage ?? err.message });
36
+ };
37
+
38
+ // src/DynamicDbRouter.ts
39
+ import { EntityMetadataNotFoundError, Equal, Like } from "typeorm";
40
+
41
+ // src/DbConnector.ts
42
+ import "reflect-metadata";
43
+ import { DataSource } from "typeorm";
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 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: sync ?? false,
58
+ logging: false,
59
+ migrations
60
+ });
61
+ await AppDataSource.initialize();
62
+ if (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: bodyObj.required ?? false,
200
+ description: bodyObj.description,
201
+ content: {
202
+ [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 (options[_path].get?.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 = 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: options?.page ?? 0,
320
+ pageSize: options?.pageSize ?? 10,
321
+ order: options?.order ?? "ASC",
322
+ orderBy: options?.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
+ ...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) => _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 = 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 options.getParameters ?? []) {
400
+ const val = 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] = Like(`%${val}%`);
404
+ else searchQuery[param.name] = Equal(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: pageSize ?? 0,
412
+ skip: (page ?? 0) * (pageSize ?? 0),
413
+ order: { [orderBy ?? options.primaryKey]: order ?? "ASC" },
414
+ select: options.returningProps
415
+ });
416
+ res.send(entities);
417
+ } catch (err) {
418
+ errorCatcher(res, err, void 0, `[GET] / => ${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]: Equal(id), ...secureSearchQuery },
432
+ select: options.returningProps
433
+ });
434
+ res.send(singleEntity);
435
+ } catch (err) {
436
+ errorCatcher(res, err, void 0, `[GET] /:id => ${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 || options.bodyParameters.properties?.[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] / => ${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]: Equal(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 || options.bodyParameters.properties?.[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 => ${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]: Equal(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 => ${err.message ?? err}`);
518
+ }
519
+ });
520
+ return options.router;
521
+ };
522
+ _DynamicDbRouter.addDbRouterSwagger = (options) => {
523
+ if (!options.primaryKey) options.primaryKey = "id";
524
+ const avoidList = 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 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
+ ...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
+ ...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] = Equal(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 = Router();
604
+ if (this.data.dbRouting) {
605
+ const avoidOptionsForDefinedApis = [];
606
+ if (this.data.apis?.["/"]?.GET) avoidOptionsForDefinedApis.push("LIST");
607
+ if (this.data.apis?.["/:id"]?.GET) avoidOptionsForDefinedApis.push("GET");
608
+ if (this.data.apis?.["/"]?.POST) avoidOptionsForDefinedApis.push("POST");
609
+ if (this.data.apis?.["/:id"]?.PUT) avoidOptionsForDefinedApis.push("PUT");
610
+ if (this.data.apis?.["/:id"]?.DELETE) avoidOptionsForDefinedApis.push("DELETE");
611
+ for (const avoid of 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 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 ((this.data.apis?.[path2]?.[method] ?? void 0) === void 0) continue;
636
+ const currentMethod = this.data.apis[path2][method];
637
+ const middlewares = 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(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} => ${err.message ?? err}`);
648
+ res.status(500).send(err?.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: this.data.dbRouting?.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
+ import express from "express";
689
+ import http from "http";
690
+ import path from "path";
691
+ import swaggerUi from "swagger-ui-express";
692
+ var Starter = class {
693
+ constructor(options) {
694
+ this.init = async (options) => {
695
+ const app = express();
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 options.plugins ?? []) app.use(plugin);
701
+ for (const router of 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", swaggerUi.serve, swaggerUi.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 = path.resolve(process.cwd(), "client");
717
+ app.use(express.static(clientPath));
718
+ app.get("/apiUrl", (_, res) => res.send(options.projectConfig.API_URL));
719
+ if (options.tokenOptions?.api) {
720
+ app.get(options.tokenOptions.api.path ?? "/api/auth", options.tokenOptions.api.callback);
721
+ }
722
+ app.get("*", (_, res) => {
723
+ res.sendFile(path.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 = http.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
+ import crypto from "crypto";
750
+ import jose from "node-jose";
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 = req.headers.authorization?.split("Bearer ")?.[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 = jose.JWK.createKeyStore();
771
+ const encryptionKey = this.deriveEncryptionKey();
772
+ const key = await jose.JWK.asKey({
773
+ kty: "oct",
774
+ k: jose.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 crypto.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 jose.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 jose.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
+ export {
815
+ RouterWrapper,
816
+ Starter,
817
+ Swagger,
818
+ Token,
819
+ apiNok,
820
+ apiOk,
821
+ errorCatcher,
822
+ fullLogNok,
823
+ fullLogOk,
824
+ log
825
+ };