@restura/core 0.1.0-alpha.19 → 0.1.0-alpha.20

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 CHANGED
@@ -49,12 +49,17 @@ import { z } from "zod";
49
49
  var loggerConfigSchema = z.object({
50
50
  level: z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
51
51
  });
52
+ var _a;
53
+ var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
54
+ var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
55
+ var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
52
56
  var resturaConfigSchema = z.object({
53
57
  authToken: z.string().min(1, "Missing Restura Auth Token"),
54
58
  sendErrorStackTrace: z.boolean().default(false),
55
59
  schemaFilePath: z.string().default(process.cwd() + "/restura.schema.json"),
56
- customApiFolderPath: z.string().default(process.cwd() + "/dist/api"),
57
- generatedTypesPath: z.string().default(process.cwd() + "/src/@types")
60
+ customApiFolderPath: z.string().default(process.cwd() + customApiFolderPath),
61
+ generatedTypesPath: z.string().default(process.cwd() + "/src/@types"),
62
+ fileTempCachePath: z.string().optional()
58
63
  });
59
64
 
60
65
  // src/logger/logger.ts
@@ -214,8 +219,8 @@ import compression from "compression";
214
219
  import cookieParser from "cookie-parser";
215
220
  import { createHash } from "crypto";
216
221
  import * as express from "express";
217
- import fs3 from "fs";
218
- import path3 from "path";
222
+ import fs4 from "fs";
223
+ import path4 from "path";
219
224
  import pg3 from "pg";
220
225
  import * as prettier3 from "prettier";
221
226
 
@@ -310,9 +315,9 @@ var ResponseValidator = class _ResponseValidator {
310
315
  return { validator: "any" };
311
316
  }
312
317
  getTypeFromTable(selector, name) {
313
- const path4 = selector.split(".");
314
- if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { validator: "any", isOptional: false };
315
- const tableName = path4.length == 2 ? path4[0] : name, columnName = path4.length == 2 ? path4[1] : path4[0];
318
+ const path5 = selector.split(".");
319
+ if (path5.length === 0 || path5.length > 2 || path5[0] === "") return { validator: "any", isOptional: false };
320
+ const tableName = path5.length == 2 ? path5[0] : name, columnName = path5.length == 2 ? path5[1] : path5[0];
316
321
  const table = this.database.find((t) => t.name == tableName);
317
322
  const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
318
323
  if (!table || !column) return { validator: "any", isOptional: false };
@@ -528,10 +533,10 @@ var ApiTree = class _ApiTree {
528
533
  return `${p.name}:${responseType}${array ? "[]" : ""}${isNullable ? " | null" : ""}`;
529
534
  }
530
535
  getTypeFromTable(selector, name) {
531
- const path4 = selector.split(".");
532
- if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { responseType: "any", isNullable: false };
533
- let tableName = path4.length == 2 ? path4[0] : name;
534
- const columnName = path4.length == 2 ? path4[1] : path4[0];
536
+ const path5 = selector.split(".");
537
+ if (path5.length === 0 || path5.length > 2 || path5[0] === "") return { responseType: "any", isNullable: false };
538
+ let tableName = path5.length == 2 ? path5[0] : name;
539
+ const columnName = path5.length == 2 ? path5[1] : path5[0];
535
540
  let table = this.database.find((t) => t.name == tableName);
536
541
  if (!table && tableName.includes("_")) {
537
542
  const tableAliasSplit = tableName.split("_");
@@ -546,8 +551,8 @@ var ApiTree = class _ApiTree {
546
551
  };
547
552
  }
548
553
  };
549
- function pathToNamespaces(path4) {
550
- return path4.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
554
+ function pathToNamespaces(path5) {
555
+ return path5.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
551
556
  }
552
557
  function apiGenerator(schema, schemaHash) {
553
558
  let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
@@ -583,6 +588,8 @@ function apiGenerator(schema, schemaHash) {
583
588
  // src/restura/customApiFactory.ts
584
589
  import fs from "fs";
585
590
  import path from "path";
591
+ import Bluebird from "bluebird";
592
+ import { fileUtils } from "@restura/internal";
586
593
  var CustomApiFactory = class {
587
594
  constructor() {
588
595
  this.customApis = {};
@@ -591,7 +598,8 @@ var CustomApiFactory = class {
591
598
  const apiVersions = ["v1"];
592
599
  for (const apiVersion of apiVersions) {
593
600
  const apiVersionFolderPath = path.join(baseFolderPath, apiVersion);
594
- if (!fs.existsSync(apiVersionFolderPath)) continue;
601
+ const directoryExists = await fileUtils.existDir(apiVersionFolderPath);
602
+ if (!directoryExists) continue;
595
603
  await this.addDirectory(apiVersionFolderPath, apiVersion);
596
604
  }
597
605
  }
@@ -599,12 +607,17 @@ var CustomApiFactory = class {
599
607
  return this.customApis[customApiName];
600
608
  }
601
609
  async addDirectory(directoryPath, apiVersion) {
602
- const entries = fs.readdirSync(directoryPath, {
610
+ var _a2;
611
+ const entries = await fs.promises.readdir(directoryPath, {
603
612
  withFileTypes: true
604
613
  });
605
- for (const entry of entries) {
614
+ const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
615
+ const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
616
+ const extension = isTsx2 || isTsNode2 ? "ts" : "js";
617
+ const shouldEndWith = `.api.${apiVersion}.${extension}`;
618
+ await Bluebird.map(entries, async (entry) => {
606
619
  if (entry.isFile()) {
607
- if (entry.name.endsWith(`.api.${apiVersion}.js`) === false) continue;
620
+ if (entry.name.endsWith(shouldEndWith) === false) return;
608
621
  try {
609
622
  const importPath = `${path.join(directoryPath, entry.name)}`;
610
623
  const ApiImport = await import(importPath);
@@ -616,7 +629,7 @@ var CustomApiFactory = class {
616
629
  console.error(e);
617
630
  }
618
631
  }
619
- }
632
+ });
620
633
  }
621
634
  bindMethodsToInstance(instance) {
622
635
  const proto = Object.getPrototypeOf(instance);
@@ -1417,11 +1430,11 @@ var SqlEngine = class {
1417
1430
  return returnValue;
1418
1431
  }
1419
1432
  replaceLocalParamKeywords(value, routeData, req, sqlParams) {
1420
- var _a;
1433
+ var _a2;
1421
1434
  if (!routeData.request) return value;
1422
1435
  const data = req.data;
1423
1436
  if (typeof value === "string") {
1424
- (_a = value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a.forEach((param) => {
1437
+ (_a2 = value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a2.forEach((param) => {
1425
1438
  const requestParam = routeData.request.find((item) => {
1426
1439
  return item.name === param.replace("$", "");
1427
1440
  });
@@ -1434,9 +1447,9 @@ var SqlEngine = class {
1434
1447
  return value;
1435
1448
  }
1436
1449
  replaceGlobalParamKeywords(value, routeData, req, sqlParams) {
1437
- var _a;
1450
+ var _a2;
1438
1451
  if (typeof value === "string") {
1439
- (_a = value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a.forEach((param) => {
1452
+ (_a2 = value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a2.forEach((param) => {
1440
1453
  param = param.replace("#", "");
1441
1454
  const globalParamValue = req.requesterDetails[param];
1442
1455
  if (!globalParamValue)
@@ -1552,7 +1565,7 @@ var filterPsqlParser = peg.generate(filterSqlGrammar, {
1552
1565
  var filterPsqlParser_default = filterPsqlParser;
1553
1566
 
1554
1567
  // src/restura/eventManager.ts
1555
- import Bluebird from "bluebird";
1568
+ import Bluebird2 from "bluebird";
1556
1569
  var EventManager = class {
1557
1570
  constructor() {
1558
1571
  this.actionHandlers = {
@@ -1589,7 +1602,7 @@ var EventManager = class {
1589
1602
  }
1590
1603
  }
1591
1604
  async fireInsertActions(data, triggerResult) {
1592
- await Bluebird.map(
1605
+ await Bluebird2.map(
1593
1606
  this.actionHandlers.DATABASE_ROW_INSERT,
1594
1607
  ({ callback, filter }) => {
1595
1608
  if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
@@ -1605,7 +1618,7 @@ var EventManager = class {
1605
1618
  );
1606
1619
  }
1607
1620
  async fireDeleteActions(data, triggerResult) {
1608
- await Bluebird.map(
1621
+ await Bluebird2.map(
1609
1622
  this.actionHandlers.DATABASE_ROW_DELETE,
1610
1623
  ({ callback, filter }) => {
1611
1624
  if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
@@ -1620,7 +1633,7 @@ var EventManager = class {
1620
1633
  );
1621
1634
  }
1622
1635
  async fireUpdateActions(data, triggerResult) {
1623
- await Bluebird.map(
1636
+ await Bluebird2.map(
1624
1637
  this.actionHandlers.DATABASE_COLUMN_UPDATE,
1625
1638
  ({ callback, filter }) => {
1626
1639
  if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
@@ -1794,16 +1807,19 @@ var PsqlEngine = class extends SqlEngine {
1794
1807
  return enums.join("\n") + "\n" + sqlStatements.join("\n\n");
1795
1808
  }
1796
1809
  async getScratchPool() {
1797
- await this.psqlConnectionPool.runQuery(
1798
- `DROP DATABASE IF EXISTS ${this.psqlConnectionPool.poolConfig.database}_scratch`,
1799
- [],
1800
- systemUser
1801
- );
1802
- await this.psqlConnectionPool.runQuery(
1803
- `CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
1810
+ const response = await this.psqlConnectionPool.runQuery(
1811
+ `SELECT * FROM pg_database
1812
+ WHERE datname = '${this.psqlConnectionPool.poolConfig.database}_scratch'`,
1804
1813
  [],
1805
1814
  systemUser
1806
1815
  );
1816
+ if (response.length === 0) {
1817
+ await this.psqlConnectionPool.runQuery(
1818
+ `CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
1819
+ [],
1820
+ systemUser
1821
+ );
1822
+ }
1807
1823
  const scratchPool = new PsqlPool({
1808
1824
  host: this.psqlConnectionPool.poolConfig.host,
1809
1825
  port: this.psqlConnectionPool.poolConfig.port,
@@ -1814,6 +1830,12 @@ var PsqlEngine = class extends SqlEngine {
1814
1830
  idleTimeoutMillis: this.psqlConnectionPool.poolConfig.idleTimeoutMillis,
1815
1831
  connectionTimeoutMillis: this.psqlConnectionPool.poolConfig.connectionTimeoutMillis
1816
1832
  });
1833
+ await scratchPool.runQuery(`drop schema public cascade;`, [], systemUser);
1834
+ await scratchPool.runQuery(
1835
+ `create schema public authorization ${this.psqlConnectionPool.poolConfig.user};`,
1836
+ [],
1837
+ systemUser
1838
+ );
1817
1839
  return scratchPool;
1818
1840
  }
1819
1841
  async diffDatabaseToSchema(schema) {
@@ -1833,17 +1855,13 @@ var PsqlEngine = class extends SqlEngine {
1833
1855
  host: this.psqlConnectionPool.poolConfig.host,
1834
1856
  port: this.psqlConnectionPool.poolConfig.port
1835
1857
  });
1836
- await originalClient.connect();
1837
- await scratchClient.connect();
1838
- const info1 = await pgInfo({
1839
- client: originalClient
1840
- });
1841
- const info2 = await pgInfo({
1842
- client: scratchClient
1843
- });
1858
+ const promises = [originalClient.connect(), scratchClient.connect()];
1859
+ await Promise.all(promises);
1860
+ const infoPromises = [pgInfo({ client: originalClient }), pgInfo({ client: scratchClient })];
1861
+ const [info1, info2] = await Promise.all(infoPromises);
1844
1862
  const diff = getDiff(info1, info2);
1845
- await originalClient.end();
1846
- await scratchClient.end();
1863
+ const endPromises = [originalClient.end(), scratchClient.end()];
1864
+ await Promise.all(endPromises);
1847
1865
  return diff.join("\n");
1848
1866
  }
1849
1867
  createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
@@ -1966,7 +1984,7 @@ var PsqlEngine = class extends SqlEngine {
1966
1984
  );
1967
1985
  const totalQuery = `SELECT COUNT(${routeData.groupBy ? `DISTINCT ${routeData.groupBy.tableName}.${routeData.groupBy.columnName}` : "*"}) AS total
1968
1986
  ${sqlStatement};`;
1969
- const totalPromise = await this.psqlConnectionPool.runQuery(
1987
+ const totalPromise = this.psqlConnectionPool.runQuery(
1970
1988
  totalQuery,
1971
1989
  sqlParams,
1972
1990
  req.requesterDetails
@@ -1983,7 +2001,7 @@ var PsqlEngine = class extends SqlEngine {
1983
2001
  }
1984
2002
  async executeUpdateRequest(req, routeData, schema) {
1985
2003
  const sqlParams = [];
1986
- const _a = req.body, { id } = _a, bodyNoId = __objRest(_a, ["id"]);
2004
+ const _a2 = req.body, { id } = _a2, bodyNoId = __objRest(_a2, ["id"]);
1987
2005
  const table = schema.database.find((item) => {
1988
2006
  return item.name === routeData.table;
1989
2007
  });
@@ -2098,22 +2116,22 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
2098
2116
  const data = req.data;
2099
2117
  if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
2100
2118
  let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
2101
- var _a;
2119
+ var _a2;
2102
2120
  const requestParam = routeData.request.find((item) => {
2103
2121
  return item.name === value.replace("$", "");
2104
2122
  });
2105
2123
  if (!requestParam)
2106
2124
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
2107
- return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
2125
+ return ((_a2 = data[requestParam.name]) == null ? void 0 : _a2.toString()) || "";
2108
2126
  });
2109
2127
  statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
2110
- var _a;
2128
+ var _a2;
2111
2129
  const requestParam = routeData.request.find((item) => {
2112
2130
  return item.name === value.replace("#", "");
2113
2131
  });
2114
2132
  if (!requestParam)
2115
2133
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
2116
- return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
2134
+ return ((_a2 = data[requestParam.name]) == null ? void 0 : _a2.toString()) || "";
2117
2135
  });
2118
2136
  statement = filterPsqlParser_default.parse(statement);
2119
2137
  if (whereClause.startsWith("WHERE")) {
@@ -2235,13 +2253,13 @@ var CompareSchema = class {
2235
2253
  const originalClone = cloneDeep(originalEndpoints);
2236
2254
  const diffObj = [];
2237
2255
  newEndPoints.forEach((endPoint) => {
2238
- const { path: path4, method } = endPoint;
2256
+ const { path: path5, method } = endPoint;
2239
2257
  const endPointIndex = originalClone.findIndex((original) => {
2240
2258
  return original.path === endPoint.path && original.method === endPoint.method;
2241
2259
  });
2242
2260
  if (endPointIndex === -1) {
2243
2261
  diffObj.push({
2244
- name: `${method} ${path4}`,
2262
+ name: `${method} ${path5}`,
2245
2263
  changeType: "NEW"
2246
2264
  });
2247
2265
  } else {
@@ -2250,7 +2268,7 @@ var CompareSchema = class {
2250
2268
  });
2251
2269
  if (original === -1) {
2252
2270
  diffObj.push({
2253
- name: `${method} ${path4}`,
2271
+ name: `${method} ${path5}`,
2254
2272
  changeType: "MODIFIED"
2255
2273
  });
2256
2274
  }
@@ -2258,9 +2276,9 @@ var CompareSchema = class {
2258
2276
  }
2259
2277
  });
2260
2278
  originalClone.forEach((original) => {
2261
- const { path: path4, method } = original;
2279
+ const { path: path5, method } = original;
2262
2280
  diffObj.push({
2263
- name: `${method} ${path4}`,
2281
+ name: `${method} ${path5}`,
2264
2282
  changeType: "DELETED"
2265
2283
  });
2266
2284
  });
@@ -2285,6 +2303,63 @@ __decorateClass([
2285
2303
  var compareSchema = new CompareSchema();
2286
2304
  var compareSchema_default = compareSchema;
2287
2305
 
2306
+ // src/restura/middleware/getMulterUploadSingleton.ts
2307
+ import multer from "multer";
2308
+ import { extname } from "path";
2309
+ import * as os from "os";
2310
+ var OneHundredMB = 100 * 1024 * 1024;
2311
+ var commonUpload = null;
2312
+ var getMulterUploadSingleton = (directory) => {
2313
+ if (commonUpload) return commonUpload;
2314
+ const storage = multer.diskStorage({
2315
+ destination: directory || os.tmpdir(),
2316
+ filename: function(request, file, cb) {
2317
+ const extension = extname(file.originalname);
2318
+ const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e3);
2319
+ cb(null, `${uniqueName}${extension}`);
2320
+ }
2321
+ });
2322
+ commonUpload = multer({
2323
+ storage,
2324
+ limits: {
2325
+ fileSize: OneHundredMB
2326
+ }
2327
+ });
2328
+ return commonUpload;
2329
+ };
2330
+
2331
+ // src/restura/utils/TempCache.ts
2332
+ import fs3 from "fs";
2333
+ import path3 from "path";
2334
+ import { DateUtils } from "@redskytech/core-utils";
2335
+ import Bluebird3 from "bluebird";
2336
+ import * as os2 from "os";
2337
+ import { fileUtils as fileUtils2 } from "@restura/internal";
2338
+ var TempCache = class {
2339
+ constructor(location) {
2340
+ this.maxDurationDays = 7;
2341
+ this.location = location || os2.tmpdir();
2342
+ fileUtils2.ensureDir(this.location).catch((e) => {
2343
+ throw e;
2344
+ });
2345
+ }
2346
+ async cleanup() {
2347
+ const fileList = await fs3.promises.readdir(this.location);
2348
+ await Bluebird3.map(
2349
+ fileList,
2350
+ async (file) => {
2351
+ const fullFilePath = path3.join(this.location, file);
2352
+ const fileStats = await fs3.promises.stat(fullFilePath);
2353
+ if (DateUtils.daysBetweenStartAndEndDates(new Date(fileStats.mtimeMs), /* @__PURE__ */ new Date()) > this.maxDurationDays) {
2354
+ logger.info(`Deleting old temp file: ${file}`);
2355
+ await fs3.promises.unlink(fullFilePath);
2356
+ }
2357
+ },
2358
+ { concurrency: 10 }
2359
+ );
2360
+ }
2361
+ };
2362
+
2288
2363
  // src/restura/restura.ts
2289
2364
  var { types } = pg3;
2290
2365
  var ResturaEngine = class {
@@ -2305,6 +2380,8 @@ var ResturaEngine = class {
2305
2380
  */
2306
2381
  async init(app, authenticationHandler, psqlConnectionPool) {
2307
2382
  this.resturaConfig = config2.validate("restura", resturaConfigSchema);
2383
+ this.multerCommonUpload = getMulterUploadSingleton(this.resturaConfig.fileTempCachePath);
2384
+ new TempCache(this.resturaConfig.fileTempCachePath);
2308
2385
  this.psqlConnectionPool = psqlConnectionPool;
2309
2386
  this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
2310
2387
  setupPgReturnTypes();
@@ -2371,7 +2448,7 @@ var ResturaEngine = class {
2371
2448
  * @returns A promise that resolves when the API has been successfully generated and written to the output file.
2372
2449
  */
2373
2450
  async generateApiFromSchema(outputFile, providedSchema) {
2374
- fs3.writeFileSync(
2451
+ fs4.writeFileSync(
2375
2452
  outputFile,
2376
2453
  await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2377
2454
  );
@@ -2384,7 +2461,7 @@ var ResturaEngine = class {
2384
2461
  * @returns A promise that resolves when the model has been successfully written to the output file.
2385
2462
  */
2386
2463
  async generateModelFromSchema(outputFile, providedSchema) {
2387
- fs3.writeFileSync(
2464
+ fs4.writeFileSync(
2388
2465
  outputFile,
2389
2466
  await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2390
2467
  );
@@ -2396,11 +2473,11 @@ var ResturaEngine = class {
2396
2473
  * @throws {Error} If the schema file is missing or the schema is not valid.
2397
2474
  */
2398
2475
  async getLatestFileSystemSchema() {
2399
- if (!fs3.existsSync(this.resturaConfig.schemaFilePath)) {
2476
+ if (!fs4.existsSync(this.resturaConfig.schemaFilePath)) {
2400
2477
  logger.error(`Missing restura schema file, expected path: ${this.resturaConfig.schemaFilePath}`);
2401
2478
  throw new Error("Missing restura schema file");
2402
2479
  }
2403
- const schemaFileData = fs3.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
2480
+ const schemaFileData = fs4.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
2404
2481
  const schema = ObjectUtils5.safeParse(schemaFileData);
2405
2482
  const isValid = await isSchemaValid(schema);
2406
2483
  if (!isValid) {
@@ -2419,11 +2496,11 @@ var ResturaEngine = class {
2419
2496
  * - `modelCreatedSchemaHash`: The hash extracted from the generated `models.d.ts` file.
2420
2497
  */
2421
2498
  async getHashes(providedSchema) {
2422
- var _a, _b, _c, _d;
2499
+ var _a2, _b, _c, _d;
2423
2500
  const schemaHash = await this.generateHashForSchema(providedSchema);
2424
- const apiFile = fs3.readFileSync(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2425
- const apiCreatedSchemaHash = (_b = (_a = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a[1]) != null ? _b : "";
2426
- const modelFile = fs3.readFileSync(path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2501
+ const apiFile = fs4.readFileSync(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2502
+ const apiCreatedSchemaHash = (_b = (_a2 = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a2[1]) != null ? _b : "";
2503
+ const modelFile = fs4.readFileSync(path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2427
2504
  const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
2428
2505
  return {
2429
2506
  schemaHash,
@@ -2459,27 +2536,27 @@ var ResturaEngine = class {
2459
2536
  logger.info(`Restura loaded (${routeCount}) endpoint${routeCount > 1 ? "s" : ""}`);
2460
2537
  }
2461
2538
  async validateGeneratedTypesFolder() {
2462
- if (!fs3.existsSync(this.resturaConfig.generatedTypesPath)) {
2463
- fs3.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
2539
+ if (!fs4.existsSync(this.resturaConfig.generatedTypesPath)) {
2540
+ fs4.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
2464
2541
  }
2465
- const hasApiFile = fs3.existsSync(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2466
- const hasModelsFile = fs3.existsSync(path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2542
+ const hasApiFile = fs4.existsSync(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2543
+ const hasModelsFile = fs4.existsSync(path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2467
2544
  if (!hasApiFile) {
2468
- await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2545
+ await this.generateApiFromSchema(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2469
2546
  }
2470
2547
  if (!hasModelsFile) {
2471
2548
  await this.generateModelFromSchema(
2472
- path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2549
+ path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2473
2550
  this.schema
2474
2551
  );
2475
2552
  }
2476
2553
  const hashes = await this.getHashes(this.schema);
2477
2554
  if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
2478
- await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2555
+ await this.generateApiFromSchema(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2479
2556
  }
2480
2557
  if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
2481
2558
  await this.generateModelFromSchema(
2482
- path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2559
+ path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2483
2560
  this.schema
2484
2561
  );
2485
2562
  }
@@ -2509,9 +2586,9 @@ var ResturaEngine = class {
2509
2586
  }
2510
2587
  }
2511
2588
  async updateTypes() {
2512
- await this.generateApiFromSchema(path3.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2589
+ await this.generateApiFromSchema(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2513
2590
  await this.generateModelFromSchema(
2514
- path3.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2591
+ path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2515
2592
  this.schema
2516
2593
  );
2517
2594
  }
@@ -2529,10 +2606,30 @@ var ResturaEngine = class {
2529
2606
  res.status(400).send({ error: err });
2530
2607
  }
2531
2608
  }
2609
+ async getMulterFilesIfAny(req, res, routeData) {
2610
+ var _a2;
2611
+ if (!((_a2 = req.header("content-type")) == null ? void 0 : _a2.includes("multipart/form-data"))) return;
2612
+ if (!this.isCustomRoute(routeData)) return;
2613
+ if (!routeData.fileUploadType) {
2614
+ throw new RsError("BAD_REQUEST", "File upload type not defined for route");
2615
+ }
2616
+ const multerFileUploadFunction = routeData.fileUploadType === "MULTIPLE" ? this.multerCommonUpload.array("files") : this.multerCommonUpload.single("file");
2617
+ return new Promise((resolve2, reject) => {
2618
+ multerFileUploadFunction(req, res, (err) => {
2619
+ if (err) {
2620
+ logger.warn("Multer error: " + err);
2621
+ reject(err);
2622
+ }
2623
+ if (req.body["data"]) req.body = JSON.parse(req.body["data"]);
2624
+ resolve2();
2625
+ });
2626
+ });
2627
+ }
2532
2628
  async executeRouteLogic(req, res, next) {
2533
2629
  try {
2534
2630
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
2535
2631
  this.validateAuthorization(req, routeData);
2632
+ await this.getMulterFilesIfAny(req, res, routeData);
2536
2633
  validateRequestParams(req, routeData, this.customTypeValidation);
2537
2634
  if (this.isCustomRoute(routeData)) {
2538
2635
  await this.runCustomRouteLogic(req, res, routeData);
@@ -2569,7 +2666,8 @@ var ResturaEngine = class {
2569
2666
  return acc + StringUtils3.capitalizeFirst(cur);
2570
2667
  }, "")}`;
2571
2668
  const customFunction = customApi[functionName];
2572
- if (!customFunction) throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented`);
2669
+ if (!customFunction)
2670
+ throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented ${functionName}`);
2573
2671
  await customFunction(req, res, routeData);
2574
2672
  }
2575
2673
  async generateHashForSchema(providedSchema) {
@@ -2596,7 +2694,7 @@ var ResturaEngine = class {
2596
2694
  printWidth: 120,
2597
2695
  singleQuote: true
2598
2696
  }));
2599
- fs3.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
2697
+ fs4.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
2600
2698
  }
2601
2699
  resetPublicEndpoints() {
2602
2700
  this.publicEndpoints = {
@@ -2613,13 +2711,13 @@ var ResturaEngine = class {
2613
2711
  if (!routeData.roles.includes(role))
2614
2712
  throw new RsError("UNAUTHORIZED", "Not authorized to access this endpoint");
2615
2713
  }
2616
- getRouteData(method, baseUrl, path4) {
2714
+ getRouteData(method, baseUrl, path5) {
2617
2715
  const endpoint = this.schema.endpoints.find((item) => {
2618
2716
  return item.baseUrl === baseUrl;
2619
2717
  });
2620
2718
  if (!endpoint) throw new RsError("NOT_FOUND", "Route not found");
2621
2719
  const route = endpoint.routes.find((item) => {
2622
- return item.method === method && item.path === path4;
2720
+ return item.method === method && item.path === path5;
2623
2721
  });
2624
2722
  if (!route) throw new RsError("NOT_FOUND", "Route not found");
2625
2723
  return route;
@@ -2640,6 +2738,9 @@ __decorateClass([
2640
2738
  __decorateClass([
2641
2739
  boundMethod
2642
2740
  ], ResturaEngine.prototype, "getSchemaAndTypes", 1);
2741
+ __decorateClass([
2742
+ boundMethod
2743
+ ], ResturaEngine.prototype, "getMulterFilesIfAny", 1);
2643
2744
  __decorateClass([
2644
2745
  boundMethod
2645
2746
  ], ResturaEngine.prototype, "executeRouteLogic", 1);