@restura/core 0.2.1 → 1.0.0-beta.1
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.d.mts +517 -472
- package/dist/index.d.ts +517 -472
- package/dist/index.js +387 -348
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +256 -218
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -14
package/dist/index.js
CHANGED
|
@@ -1,39 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
|
-
var __defProps = Object.defineProperties;
|
|
5
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
10
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
12
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
-
var __spreadValues = (a, b) => {
|
|
14
|
-
for (var prop in b || (b = {}))
|
|
15
|
-
if (__hasOwnProp.call(b, prop))
|
|
16
|
-
__defNormalProp(a, prop, b[prop]);
|
|
17
|
-
if (__getOwnPropSymbols)
|
|
18
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
-
if (__propIsEnum.call(b, prop))
|
|
20
|
-
__defNormalProp(a, prop, b[prop]);
|
|
21
|
-
}
|
|
22
|
-
return a;
|
|
23
|
-
};
|
|
24
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
-
var __objRest = (source, exclude) => {
|
|
26
|
-
var target = {};
|
|
27
|
-
for (var prop in source)
|
|
28
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
29
|
-
target[prop] = source[prop];
|
|
30
|
-
if (source != null && __getOwnPropSymbols)
|
|
31
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
32
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
33
|
-
target[prop] = source[prop];
|
|
34
|
-
}
|
|
35
|
-
return target;
|
|
36
|
-
};
|
|
37
8
|
var __export = (target, all) => {
|
|
38
9
|
for (var name in all)
|
|
39
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -92,13 +63,12 @@ var import_winston = __toESM(require("winston"));
|
|
|
92
63
|
var import_logform = require("logform");
|
|
93
64
|
|
|
94
65
|
// src/logger/loggerConfigSchema.ts
|
|
95
|
-
var
|
|
96
|
-
var loggerConfigSchema =
|
|
97
|
-
level:
|
|
66
|
+
var import_v4 = require("zod/v4");
|
|
67
|
+
var loggerConfigSchema = import_v4.z.object({
|
|
68
|
+
level: import_v4.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
|
|
98
69
|
});
|
|
99
70
|
|
|
100
71
|
// src/logger/logger.ts
|
|
101
|
-
var loggerConfig = import_internal.config.validate("logger", loggerConfigSchema);
|
|
102
72
|
var consoleFormat = import_logform.format.combine(
|
|
103
73
|
import_logform.format.timestamp({
|
|
104
74
|
format: "YYYY-MM-DD HH:mm:ss.sss"
|
|
@@ -107,11 +77,11 @@ var consoleFormat = import_logform.format.combine(
|
|
|
107
77
|
import_logform.format.padLevels(),
|
|
108
78
|
import_logform.format.colorize({ all: true }),
|
|
109
79
|
import_logform.format.printf((info) => {
|
|
110
|
-
return `[${info.timestamp}] ${info.level}
|
|
80
|
+
return `[${info.timestamp}] ${info.level} ${info.message}`;
|
|
111
81
|
})
|
|
112
82
|
);
|
|
113
83
|
var logger = import_winston.default.createLogger({
|
|
114
|
-
level:
|
|
84
|
+
level: "info",
|
|
115
85
|
format: import_logform.format.combine(
|
|
116
86
|
import_logform.format.timestamp({
|
|
117
87
|
format: "YYYY-MM-DD HH:mm:ss.sss"
|
|
@@ -119,29 +89,30 @@ var logger = import_winston.default.createLogger({
|
|
|
119
89
|
import_logform.format.errors({ stack: true }),
|
|
120
90
|
import_logform.format.json()
|
|
121
91
|
),
|
|
122
|
-
|
|
123
|
-
transports: [
|
|
124
|
-
//
|
|
125
|
-
// - Write to all logs with level `info` and below to `combined.log`
|
|
126
|
-
// - Write all logs error (and below) to `error.log`.
|
|
127
|
-
// - Write all logs to standard out.
|
|
128
|
-
//
|
|
129
|
-
// new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|
130
|
-
// new winston.transports.File({ filename: 'combined.log' }),
|
|
131
|
-
new import_winston.default.transports.Console({ format: consoleFormat })
|
|
132
|
-
]
|
|
92
|
+
transports: [new import_winston.default.transports.Console({ format: consoleFormat })]
|
|
133
93
|
});
|
|
94
|
+
(async () => {
|
|
95
|
+
try {
|
|
96
|
+
const loggerConfig = await import_internal.config.validate("logger", loggerConfigSchema);
|
|
97
|
+
if (loggerConfig.level) {
|
|
98
|
+
logger.level = loggerConfig.level;
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error("Failed to load logger config:", error);
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Failed to initialize logger configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
134
107
|
|
|
135
108
|
// src/restura/eventManager.ts
|
|
136
109
|
var import_bluebird = __toESM(require("bluebird"));
|
|
137
110
|
var EventManager = class {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
144
|
-
}
|
|
111
|
+
actionHandlers = {
|
|
112
|
+
DATABASE_ROW_DELETE: [],
|
|
113
|
+
DATABASE_ROW_INSERT: [],
|
|
114
|
+
DATABASE_COLUMN_UPDATE: []
|
|
115
|
+
};
|
|
145
116
|
addRowInsertHandler(onInsert, filter) {
|
|
146
117
|
this.actionHandlers.DATABASE_ROW_INSERT.push({
|
|
147
118
|
callback: onInsert,
|
|
@@ -338,6 +309,10 @@ var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
|
|
|
338
309
|
return HtmlStatusCodes2;
|
|
339
310
|
})(HtmlStatusCodes || {});
|
|
340
311
|
var RsError = class _RsError {
|
|
312
|
+
err;
|
|
313
|
+
msg;
|
|
314
|
+
status;
|
|
315
|
+
stack;
|
|
341
316
|
constructor(errCode, message) {
|
|
342
317
|
this.err = errCode;
|
|
343
318
|
this.msg = message || "";
|
|
@@ -490,9 +465,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
490
465
|
var import_path = __toESM(require("path"));
|
|
491
466
|
var import_internal2 = require("@restura/internal");
|
|
492
467
|
var CustomApiFactory = class {
|
|
493
|
-
|
|
494
|
-
this.customApis = {};
|
|
495
|
-
}
|
|
468
|
+
customApis = {};
|
|
496
469
|
async loadApiFiles(baseFolderPath) {
|
|
497
470
|
const apiVersions = ["v1"];
|
|
498
471
|
for (const apiVersion of apiVersions) {
|
|
@@ -506,11 +479,10 @@ var CustomApiFactory = class {
|
|
|
506
479
|
return this.customApis[customApiName];
|
|
507
480
|
}
|
|
508
481
|
async addDirectory(directoryPath, apiVersion) {
|
|
509
|
-
var _a2;
|
|
510
482
|
const entries = await import_fs.default.promises.readdir(directoryPath, {
|
|
511
483
|
withFileTypes: true
|
|
512
484
|
});
|
|
513
|
-
const isTsx2 =
|
|
485
|
+
const isTsx2 = process.argv[1]?.endsWith(".ts");
|
|
514
486
|
const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
|
|
515
487
|
const extension = isTsx2 || isTsNode2 ? "ts" : "js";
|
|
516
488
|
const shouldEndWith = `.api.${apiVersion}.${extension}`;
|
|
@@ -575,6 +547,8 @@ var SqlUtils = class _SqlUtils {
|
|
|
575
547
|
|
|
576
548
|
// src/restura/validators/ResponseValidator.ts
|
|
577
549
|
var ResponseValidator = class _ResponseValidator {
|
|
550
|
+
rootMap;
|
|
551
|
+
database;
|
|
578
552
|
constructor(schema) {
|
|
579
553
|
this.database = schema.database;
|
|
580
554
|
this.rootMap = {};
|
|
@@ -650,7 +624,7 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
650
624
|
if (path5.length === 0 || path5.length > 2 || path5[0] === "") return { validator: "any", isOptional: false };
|
|
651
625
|
const tableName = path5.length == 2 ? path5[0] : name, columnName = path5.length == 2 ? path5[1] : path5[0];
|
|
652
626
|
const table = this.database.find((t) => t.name == tableName);
|
|
653
|
-
const column = table
|
|
627
|
+
const column = table?.columns.find((c) => c.name == columnName);
|
|
654
628
|
if (!table || !column) return { validator: "any", isOptional: false };
|
|
655
629
|
let validator = SqlUtils.convertDatabaseTypeToTypescript(
|
|
656
630
|
column.type,
|
|
@@ -734,10 +708,12 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
734
708
|
var ApiTree = class _ApiTree {
|
|
735
709
|
constructor(namespace, database) {
|
|
736
710
|
this.database = database;
|
|
737
|
-
this.data = [];
|
|
738
711
|
this.namespace = namespace;
|
|
739
712
|
this.children = /* @__PURE__ */ new Map();
|
|
740
713
|
}
|
|
714
|
+
namespace;
|
|
715
|
+
data = [];
|
|
716
|
+
children;
|
|
741
717
|
static createRootNode(database) {
|
|
742
718
|
return new _ApiTree(null, database);
|
|
743
719
|
}
|
|
@@ -881,7 +857,7 @@ var ApiTree = class _ApiTree {
|
|
|
881
857
|
tableName = tableAliasSplit[1];
|
|
882
858
|
table = this.database.find((t) => t.name == tableName);
|
|
883
859
|
}
|
|
884
|
-
const column = table
|
|
860
|
+
const column = table?.columns.find((c) => c.name == columnName);
|
|
885
861
|
if (!table || !column) return { responseType: "any", isNullable: false };
|
|
886
862
|
return {
|
|
887
863
|
responseType: SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value),
|
|
@@ -912,16 +888,17 @@ function apiGenerator(schema) {
|
|
|
912
888
|
${schema.customTypes.join("\n")}
|
|
913
889
|
}`;
|
|
914
890
|
}
|
|
915
|
-
return import_prettier.default.format(apiString,
|
|
916
|
-
parser: "typescript"
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
891
|
+
return import_prettier.default.format(apiString, {
|
|
892
|
+
parser: "typescript",
|
|
893
|
+
...{
|
|
894
|
+
trailingComma: "none",
|
|
895
|
+
tabWidth: 4,
|
|
896
|
+
useTabs: true,
|
|
897
|
+
endOfLine: "lf",
|
|
898
|
+
printWidth: 120,
|
|
899
|
+
singleQuote: true
|
|
900
|
+
}
|
|
901
|
+
});
|
|
925
902
|
}
|
|
926
903
|
|
|
927
904
|
// src/restura/generators/customTypeValidationGenerator.ts
|
|
@@ -932,7 +909,7 @@ var TJS = __toESM(require("typescript-json-schema"));
|
|
|
932
909
|
function customTypeValidationGenerator(currentSchema) {
|
|
933
910
|
const schemaObject = {};
|
|
934
911
|
const customInterfaceNames = currentSchema.customTypes.map((customType) => {
|
|
935
|
-
const matches = customType.match(
|
|
912
|
+
const matches = customType.match(/(?<=interface\s)(\w+)|(?<=type\s)(\w+)/g);
|
|
936
913
|
if (matches && matches.length > 0) return matches[0];
|
|
937
914
|
return "";
|
|
938
915
|
}).filter(Boolean);
|
|
@@ -976,16 +953,17 @@ function modelGenerator(schema) {
|
|
|
976
953
|
modelString += convertTable(table);
|
|
977
954
|
}
|
|
978
955
|
modelString += `}`;
|
|
979
|
-
return import_prettier2.default.format(modelString,
|
|
980
|
-
parser: "typescript"
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
956
|
+
return import_prettier2.default.format(modelString, {
|
|
957
|
+
parser: "typescript",
|
|
958
|
+
...{
|
|
959
|
+
trailingComma: "none",
|
|
960
|
+
tabWidth: 4,
|
|
961
|
+
useTabs: true,
|
|
962
|
+
endOfLine: "lf",
|
|
963
|
+
printWidth: 120,
|
|
964
|
+
singleQuote: true
|
|
965
|
+
}
|
|
966
|
+
});
|
|
989
967
|
}
|
|
990
968
|
function convertTable(table) {
|
|
991
969
|
let modelString = ` export interface ${import_core_utils2.StringUtils.capitalizeFirst(table.name)} {
|
|
@@ -1037,20 +1015,21 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
1037
1015
|
htmlStatusCode = 500;
|
|
1038
1016
|
}
|
|
1039
1017
|
}
|
|
1040
|
-
const errorData =
|
|
1018
|
+
const errorData = {
|
|
1041
1019
|
err: shortError,
|
|
1042
|
-
msg
|
|
1043
|
-
|
|
1020
|
+
msg,
|
|
1021
|
+
...restura.resturaConfig.sendErrorStackTrace && stack ? { stack } : {}
|
|
1022
|
+
};
|
|
1044
1023
|
res.status(htmlStatusCode).send(errorData);
|
|
1045
1024
|
};
|
|
1046
1025
|
next();
|
|
1047
1026
|
}
|
|
1048
1027
|
|
|
1049
|
-
// src/restura/middleware/
|
|
1050
|
-
function
|
|
1028
|
+
// src/restura/middleware/authenticateRequester.ts
|
|
1029
|
+
function authenticateRequester(applicationAuthenticateHandler) {
|
|
1051
1030
|
return (req, res, next) => {
|
|
1052
|
-
applicationAuthenticateHandler(req, res, (
|
|
1053
|
-
req.requesterDetails =
|
|
1031
|
+
applicationAuthenticateHandler(req, res, (authenticatedRequesterDetails) => {
|
|
1032
|
+
req.requesterDetails = { host: req.hostname, ipAddress: req.ip || "", ...authenticatedRequesterDetails };
|
|
1054
1033
|
next();
|
|
1055
1034
|
});
|
|
1056
1035
|
};
|
|
@@ -1082,110 +1061,97 @@ var getMulterUpload = (directory) => {
|
|
|
1082
1061
|
};
|
|
1083
1062
|
|
|
1084
1063
|
// src/restura/schemas/resturaSchema.ts
|
|
1085
|
-
var
|
|
1064
|
+
var import_zod2 = require("zod");
|
|
1086
1065
|
|
|
1087
1066
|
// src/restura/schemas/validatorDataSchema.ts
|
|
1088
|
-
var
|
|
1089
|
-
var validatorDataSchemeValue =
|
|
1090
|
-
var validatorDataSchema =
|
|
1091
|
-
type:
|
|
1067
|
+
var import_zod = require("zod");
|
|
1068
|
+
var validatorDataSchemeValue = import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.string()), import_zod.z.number(), import_zod.z.array(import_zod.z.number())]);
|
|
1069
|
+
var validatorDataSchema = import_zod.z.object({
|
|
1070
|
+
type: import_zod.z.enum(["TYPE_CHECK", "MIN", "MAX", "ONE_OF"]),
|
|
1092
1071
|
value: validatorDataSchemeValue
|
|
1093
1072
|
}).strict();
|
|
1094
1073
|
|
|
1095
1074
|
// src/restura/schemas/resturaSchema.ts
|
|
1096
|
-
var orderBySchema =
|
|
1097
|
-
columnName:
|
|
1098
|
-
order:
|
|
1099
|
-
tableName:
|
|
1075
|
+
var orderBySchema = import_zod2.z.object({
|
|
1076
|
+
columnName: import_zod2.z.string(),
|
|
1077
|
+
order: import_zod2.z.enum(["ASC", "DESC"]),
|
|
1078
|
+
tableName: import_zod2.z.string()
|
|
1100
1079
|
}).strict();
|
|
1101
|
-
var groupBySchema =
|
|
1102
|
-
columnName:
|
|
1103
|
-
tableName:
|
|
1080
|
+
var groupBySchema = import_zod2.z.object({
|
|
1081
|
+
columnName: import_zod2.z.string(),
|
|
1082
|
+
tableName: import_zod2.z.string()
|
|
1104
1083
|
}).strict();
|
|
1105
|
-
var whereDataSchema =
|
|
1106
|
-
tableName:
|
|
1107
|
-
columnName:
|
|
1108
|
-
operator:
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
"<=",
|
|
1113
|
-
">=",
|
|
1114
|
-
"!=",
|
|
1115
|
-
"LIKE",
|
|
1116
|
-
"NOT LIKE",
|
|
1117
|
-
"IN",
|
|
1118
|
-
"NOT IN",
|
|
1119
|
-
"STARTS WITH",
|
|
1120
|
-
"ENDS WITH",
|
|
1121
|
-
"IS",
|
|
1122
|
-
"IS NOT"
|
|
1123
|
-
]).optional(),
|
|
1124
|
-
value: import_zod3.z.string().or(import_zod3.z.number()).optional(),
|
|
1125
|
-
custom: import_zod3.z.string().optional(),
|
|
1126
|
-
conjunction: import_zod3.z.enum(["AND", "OR"]).optional()
|
|
1084
|
+
var whereDataSchema = import_zod2.z.object({
|
|
1085
|
+
tableName: import_zod2.z.string().optional(),
|
|
1086
|
+
columnName: import_zod2.z.string().optional(),
|
|
1087
|
+
operator: import_zod2.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH", "IS", "IS NOT"]).optional(),
|
|
1088
|
+
value: import_zod2.z.string().or(import_zod2.z.number()).optional(),
|
|
1089
|
+
custom: import_zod2.z.string().optional(),
|
|
1090
|
+
conjunction: import_zod2.z.enum(["AND", "OR"]).optional()
|
|
1127
1091
|
}).strict();
|
|
1128
|
-
var assignmentDataSchema =
|
|
1129
|
-
name:
|
|
1130
|
-
value:
|
|
1092
|
+
var assignmentDataSchema = import_zod2.z.object({
|
|
1093
|
+
name: import_zod2.z.string(),
|
|
1094
|
+
value: import_zod2.z.string()
|
|
1131
1095
|
}).strict();
|
|
1132
|
-
var joinDataSchema =
|
|
1133
|
-
table:
|
|
1134
|
-
localColumnName:
|
|
1135
|
-
foreignColumnName:
|
|
1136
|
-
custom:
|
|
1137
|
-
type:
|
|
1138
|
-
alias:
|
|
1096
|
+
var joinDataSchema = import_zod2.z.object({
|
|
1097
|
+
table: import_zod2.z.string(),
|
|
1098
|
+
localColumnName: import_zod2.z.string().optional(),
|
|
1099
|
+
foreignColumnName: import_zod2.z.string().optional(),
|
|
1100
|
+
custom: import_zod2.z.string().optional(),
|
|
1101
|
+
type: import_zod2.z.enum(["LEFT", "INNER"]),
|
|
1102
|
+
alias: import_zod2.z.string().optional()
|
|
1139
1103
|
}).strict();
|
|
1140
|
-
var requestDataSchema =
|
|
1141
|
-
name:
|
|
1142
|
-
required:
|
|
1143
|
-
isNullable:
|
|
1144
|
-
validator:
|
|
1104
|
+
var requestDataSchema = import_zod2.z.object({
|
|
1105
|
+
name: import_zod2.z.string(),
|
|
1106
|
+
required: import_zod2.z.boolean(),
|
|
1107
|
+
isNullable: import_zod2.z.boolean().optional(),
|
|
1108
|
+
validator: import_zod2.z.array(validatorDataSchema)
|
|
1145
1109
|
}).strict();
|
|
1146
|
-
var responseDataSchema =
|
|
1147
|
-
name:
|
|
1148
|
-
selector:
|
|
1149
|
-
subquery:
|
|
1150
|
-
table:
|
|
1151
|
-
joins:
|
|
1152
|
-
where:
|
|
1153
|
-
properties:
|
|
1110
|
+
var responseDataSchema = import_zod2.z.object({
|
|
1111
|
+
name: import_zod2.z.string(),
|
|
1112
|
+
selector: import_zod2.z.string().optional(),
|
|
1113
|
+
subquery: import_zod2.z.object({
|
|
1114
|
+
table: import_zod2.z.string(),
|
|
1115
|
+
joins: import_zod2.z.array(joinDataSchema),
|
|
1116
|
+
where: import_zod2.z.array(whereDataSchema),
|
|
1117
|
+
properties: import_zod2.z.array(import_zod2.z.lazy(() => responseDataSchema)),
|
|
1154
1118
|
// Explicit type for the lazy schema
|
|
1155
1119
|
groupBy: groupBySchema.optional(),
|
|
1156
1120
|
orderBy: orderBySchema.optional()
|
|
1157
1121
|
}).optional(),
|
|
1158
|
-
type:
|
|
1122
|
+
type: import_zod2.z.string().optional()
|
|
1123
|
+
// Type allows you to override the type of the response, used in custom selectors
|
|
1159
1124
|
}).strict();
|
|
1160
|
-
var routeDataBaseSchema =
|
|
1161
|
-
method:
|
|
1162
|
-
name:
|
|
1163
|
-
description:
|
|
1164
|
-
path:
|
|
1165
|
-
roles:
|
|
1125
|
+
var routeDataBaseSchema = import_zod2.z.object({
|
|
1126
|
+
method: import_zod2.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
1127
|
+
name: import_zod2.z.string(),
|
|
1128
|
+
description: import_zod2.z.string(),
|
|
1129
|
+
path: import_zod2.z.string(),
|
|
1130
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1131
|
+
scopes: import_zod2.z.array(import_zod2.z.string())
|
|
1166
1132
|
}).strict();
|
|
1167
1133
|
var standardRouteSchema = routeDataBaseSchema.extend({
|
|
1168
|
-
type:
|
|
1169
|
-
table:
|
|
1170
|
-
joins:
|
|
1171
|
-
assignments:
|
|
1172
|
-
where:
|
|
1173
|
-
request:
|
|
1174
|
-
response:
|
|
1134
|
+
type: import_zod2.z.enum(["ONE", "ARRAY", "PAGED"]),
|
|
1135
|
+
table: import_zod2.z.string(),
|
|
1136
|
+
joins: import_zod2.z.array(joinDataSchema),
|
|
1137
|
+
assignments: import_zod2.z.array(assignmentDataSchema),
|
|
1138
|
+
where: import_zod2.z.array(whereDataSchema),
|
|
1139
|
+
request: import_zod2.z.array(requestDataSchema),
|
|
1140
|
+
response: import_zod2.z.array(responseDataSchema),
|
|
1175
1141
|
groupBy: groupBySchema.optional(),
|
|
1176
1142
|
orderBy: orderBySchema.optional()
|
|
1177
1143
|
}).strict();
|
|
1178
1144
|
var customRouteSchema = routeDataBaseSchema.extend({
|
|
1179
|
-
type:
|
|
1180
|
-
responseType:
|
|
1181
|
-
requestType:
|
|
1182
|
-
request:
|
|
1183
|
-
table:
|
|
1184
|
-
joins:
|
|
1185
|
-
assignments:
|
|
1186
|
-
fileUploadType:
|
|
1145
|
+
type: import_zod2.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
|
|
1146
|
+
responseType: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.enum(["string", "number", "boolean"])]),
|
|
1147
|
+
requestType: import_zod2.z.string().optional(),
|
|
1148
|
+
request: import_zod2.z.array(requestDataSchema).optional(),
|
|
1149
|
+
table: import_zod2.z.undefined(),
|
|
1150
|
+
joins: import_zod2.z.undefined(),
|
|
1151
|
+
assignments: import_zod2.z.undefined(),
|
|
1152
|
+
fileUploadType: import_zod2.z.enum(["SINGLE", "MULTIPLE"]).optional()
|
|
1187
1153
|
}).strict();
|
|
1188
|
-
var postgresColumnNumericTypesSchema =
|
|
1154
|
+
var postgresColumnNumericTypesSchema = import_zod2.z.enum([
|
|
1189
1155
|
"SMALLINT",
|
|
1190
1156
|
// 2 bytes, -32,768 to 32,767
|
|
1191
1157
|
"INTEGER",
|
|
@@ -1205,7 +1171,7 @@ var postgresColumnNumericTypesSchema = import_zod3.z.enum([
|
|
|
1205
1171
|
"BIGSERIAL"
|
|
1206
1172
|
// auto-incrementing big integer
|
|
1207
1173
|
]);
|
|
1208
|
-
var postgresColumnStringTypesSchema =
|
|
1174
|
+
var postgresColumnStringTypesSchema = import_zod2.z.enum([
|
|
1209
1175
|
"CHAR",
|
|
1210
1176
|
// fixed-length, blank-padded
|
|
1211
1177
|
"VARCHAR",
|
|
@@ -1215,7 +1181,7 @@ var postgresColumnStringTypesSchema = import_zod3.z.enum([
|
|
|
1215
1181
|
"BYTEA"
|
|
1216
1182
|
// binary data
|
|
1217
1183
|
]);
|
|
1218
|
-
var postgresColumnDateTypesSchema =
|
|
1184
|
+
var postgresColumnDateTypesSchema = import_zod2.z.enum([
|
|
1219
1185
|
"DATE",
|
|
1220
1186
|
// calendar date (year, month, day)
|
|
1221
1187
|
"TIMESTAMP",
|
|
@@ -1227,13 +1193,13 @@ var postgresColumnDateTypesSchema = import_zod3.z.enum([
|
|
|
1227
1193
|
"INTERVAL"
|
|
1228
1194
|
// time span
|
|
1229
1195
|
]);
|
|
1230
|
-
var postgresColumnJsonTypesSchema =
|
|
1196
|
+
var postgresColumnJsonTypesSchema = import_zod2.z.enum([
|
|
1231
1197
|
"JSON",
|
|
1232
1198
|
// stores JSON data as raw text
|
|
1233
1199
|
"JSONB"
|
|
1234
1200
|
// stores JSON data in a binary format, optimized for query performance
|
|
1235
1201
|
]);
|
|
1236
|
-
var mariaDbColumnNumericTypesSchema =
|
|
1202
|
+
var mariaDbColumnNumericTypesSchema = import_zod2.z.enum([
|
|
1237
1203
|
"BOOLEAN",
|
|
1238
1204
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
1239
1205
|
"TINYINT",
|
|
@@ -1253,7 +1219,7 @@ var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
|
|
|
1253
1219
|
"DOUBLE"
|
|
1254
1220
|
// 8 bytes Stored in 64-bit IEEE-754 floating point format. As such, the number of significant digits is about 15 and the range of values is approximately +/-1e308.
|
|
1255
1221
|
]);
|
|
1256
|
-
var mariaDbColumnStringTypesSchema =
|
|
1222
|
+
var mariaDbColumnStringTypesSchema = import_zod2.z.enum([
|
|
1257
1223
|
"CHAR",
|
|
1258
1224
|
// 1, 2, 4, or 8 bytes Holds letters and special characters of fixed length. Max length is 255. Default and minimum size is 1 byte.
|
|
1259
1225
|
"VARCHAR",
|
|
@@ -1279,7 +1245,7 @@ var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
|
|
|
1279
1245
|
"ENUM"
|
|
1280
1246
|
// Enum type
|
|
1281
1247
|
]);
|
|
1282
|
-
var mariaDbColumnDateTypesSchema =
|
|
1248
|
+
var mariaDbColumnDateTypesSchema = import_zod2.z.enum([
|
|
1283
1249
|
"DATE",
|
|
1284
1250
|
// 4-bytes Date has year, month, and day.
|
|
1285
1251
|
"DATETIME",
|
|
@@ -1289,9 +1255,9 @@ var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
|
|
|
1289
1255
|
"TIMESTAMP"
|
|
1290
1256
|
// 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
|
|
1291
1257
|
]);
|
|
1292
|
-
var columnDataSchema =
|
|
1293
|
-
name:
|
|
1294
|
-
type:
|
|
1258
|
+
var columnDataSchema = import_zod2.z.object({
|
|
1259
|
+
name: import_zod2.z.string(),
|
|
1260
|
+
type: import_zod2.z.union([
|
|
1295
1261
|
postgresColumnNumericTypesSchema,
|
|
1296
1262
|
postgresColumnStringTypesSchema,
|
|
1297
1263
|
postgresColumnDateTypesSchema,
|
|
@@ -1300,24 +1266,25 @@ var columnDataSchema = import_zod3.z.object({
|
|
|
1300
1266
|
mariaDbColumnStringTypesSchema,
|
|
1301
1267
|
mariaDbColumnDateTypesSchema
|
|
1302
1268
|
]),
|
|
1303
|
-
isNullable:
|
|
1304
|
-
roles:
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1269
|
+
isNullable: import_zod2.z.boolean(),
|
|
1270
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1271
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1272
|
+
comment: import_zod2.z.string().optional(),
|
|
1273
|
+
default: import_zod2.z.string().optional(),
|
|
1274
|
+
value: import_zod2.z.string().optional(),
|
|
1275
|
+
isPrimary: import_zod2.z.boolean().optional(),
|
|
1276
|
+
isUnique: import_zod2.z.boolean().optional(),
|
|
1277
|
+
hasAutoIncrement: import_zod2.z.boolean().optional(),
|
|
1278
|
+
length: import_zod2.z.number().optional()
|
|
1312
1279
|
}).strict();
|
|
1313
|
-
var indexDataSchema =
|
|
1314
|
-
name:
|
|
1315
|
-
columns:
|
|
1316
|
-
isUnique:
|
|
1317
|
-
isPrimaryKey:
|
|
1318
|
-
order:
|
|
1280
|
+
var indexDataSchema = import_zod2.z.object({
|
|
1281
|
+
name: import_zod2.z.string(),
|
|
1282
|
+
columns: import_zod2.z.array(import_zod2.z.string()),
|
|
1283
|
+
isUnique: import_zod2.z.boolean(),
|
|
1284
|
+
isPrimaryKey: import_zod2.z.boolean(),
|
|
1285
|
+
order: import_zod2.z.enum(["ASC", "DESC"])
|
|
1319
1286
|
}).strict();
|
|
1320
|
-
var foreignKeyActionsSchema =
|
|
1287
|
+
var foreignKeyActionsSchema = import_zod2.z.enum([
|
|
1321
1288
|
"CASCADE",
|
|
1322
1289
|
// CASCADE action for foreign keys
|
|
1323
1290
|
"SET NULL",
|
|
@@ -1329,39 +1296,41 @@ var foreignKeyActionsSchema = import_zod3.z.enum([
|
|
|
1329
1296
|
"SET DEFAULT"
|
|
1330
1297
|
// SET DEFAULT action for foreign keys
|
|
1331
1298
|
]);
|
|
1332
|
-
var foreignKeyDataSchema =
|
|
1333
|
-
name:
|
|
1334
|
-
column:
|
|
1335
|
-
refTable:
|
|
1336
|
-
refColumn:
|
|
1299
|
+
var foreignKeyDataSchema = import_zod2.z.object({
|
|
1300
|
+
name: import_zod2.z.string(),
|
|
1301
|
+
column: import_zod2.z.string(),
|
|
1302
|
+
refTable: import_zod2.z.string(),
|
|
1303
|
+
refColumn: import_zod2.z.string(),
|
|
1337
1304
|
onDelete: foreignKeyActionsSchema,
|
|
1338
1305
|
onUpdate: foreignKeyActionsSchema
|
|
1339
1306
|
}).strict();
|
|
1340
|
-
var checkConstraintDataSchema =
|
|
1341
|
-
name:
|
|
1342
|
-
check:
|
|
1307
|
+
var checkConstraintDataSchema = import_zod2.z.object({
|
|
1308
|
+
name: import_zod2.z.string(),
|
|
1309
|
+
check: import_zod2.z.string()
|
|
1343
1310
|
}).strict();
|
|
1344
|
-
var tableDataSchema =
|
|
1345
|
-
name:
|
|
1346
|
-
columns:
|
|
1347
|
-
indexes:
|
|
1348
|
-
foreignKeys:
|
|
1349
|
-
checkConstraints:
|
|
1350
|
-
roles:
|
|
1351
|
-
|
|
1311
|
+
var tableDataSchema = import_zod2.z.object({
|
|
1312
|
+
name: import_zod2.z.string(),
|
|
1313
|
+
columns: import_zod2.z.array(columnDataSchema),
|
|
1314
|
+
indexes: import_zod2.z.array(indexDataSchema),
|
|
1315
|
+
foreignKeys: import_zod2.z.array(foreignKeyDataSchema),
|
|
1316
|
+
checkConstraints: import_zod2.z.array(checkConstraintDataSchema),
|
|
1317
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1318
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1319
|
+
notify: import_zod2.z.union([import_zod2.z.literal("ALL"), import_zod2.z.array(import_zod2.z.string())]).optional()
|
|
1352
1320
|
}).strict();
|
|
1353
|
-
var endpointDataSchema =
|
|
1354
|
-
name:
|
|
1355
|
-
description:
|
|
1356
|
-
baseUrl:
|
|
1357
|
-
routes:
|
|
1321
|
+
var endpointDataSchema = import_zod2.z.object({
|
|
1322
|
+
name: import_zod2.z.string(),
|
|
1323
|
+
description: import_zod2.z.string(),
|
|
1324
|
+
baseUrl: import_zod2.z.string(),
|
|
1325
|
+
routes: import_zod2.z.array(import_zod2.z.union([standardRouteSchema, customRouteSchema]))
|
|
1358
1326
|
}).strict();
|
|
1359
|
-
var resturaSchema =
|
|
1360
|
-
database:
|
|
1361
|
-
endpoints:
|
|
1362
|
-
globalParams:
|
|
1363
|
-
roles:
|
|
1364
|
-
|
|
1327
|
+
var resturaSchema = import_zod2.z.object({
|
|
1328
|
+
database: import_zod2.z.array(tableDataSchema),
|
|
1329
|
+
endpoints: import_zod2.z.array(endpointDataSchema),
|
|
1330
|
+
globalParams: import_zod2.z.array(import_zod2.z.string()),
|
|
1331
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1332
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1333
|
+
customTypes: import_zod2.z.array(import_zod2.z.string())
|
|
1365
1334
|
}).strict();
|
|
1366
1335
|
async function isSchemaValid(schemaToCheck) {
|
|
1367
1336
|
try {
|
|
@@ -1376,7 +1345,7 @@ async function isSchemaValid(schemaToCheck) {
|
|
|
1376
1345
|
// src/restura/validators/requestValidator.ts
|
|
1377
1346
|
var import_core_utils3 = require("@redskytech/core-utils");
|
|
1378
1347
|
var import_jsonschema = __toESM(require("jsonschema"));
|
|
1379
|
-
var
|
|
1348
|
+
var import_zod3 = require("zod");
|
|
1380
1349
|
|
|
1381
1350
|
// src/restura/utils/utils.ts
|
|
1382
1351
|
function addQuotesToStrings(variable) {
|
|
@@ -1458,31 +1427,31 @@ function isValidType(type, requestValue) {
|
|
|
1458
1427
|
try {
|
|
1459
1428
|
expectValidType(type, requestValue);
|
|
1460
1429
|
return true;
|
|
1461
|
-
} catch
|
|
1430
|
+
} catch {
|
|
1462
1431
|
return false;
|
|
1463
1432
|
}
|
|
1464
1433
|
}
|
|
1465
1434
|
function expectValidType(type, requestValue) {
|
|
1466
1435
|
if (type === "number") {
|
|
1467
|
-
return
|
|
1436
|
+
return import_zod3.z.number().parse(requestValue);
|
|
1468
1437
|
}
|
|
1469
1438
|
if (type === "string") {
|
|
1470
|
-
return
|
|
1439
|
+
return import_zod3.z.string().parse(requestValue);
|
|
1471
1440
|
}
|
|
1472
1441
|
if (type === "boolean") {
|
|
1473
|
-
return
|
|
1442
|
+
return import_zod3.z.boolean().parse(requestValue);
|
|
1474
1443
|
}
|
|
1475
1444
|
if (type === "string[]") {
|
|
1476
|
-
return
|
|
1445
|
+
return import_zod3.z.array(import_zod3.z.string()).parse(requestValue);
|
|
1477
1446
|
}
|
|
1478
1447
|
if (type === "number[]") {
|
|
1479
|
-
return
|
|
1448
|
+
return import_zod3.z.array(import_zod3.z.number()).parse(requestValue);
|
|
1480
1449
|
}
|
|
1481
1450
|
if (type === "any[]") {
|
|
1482
|
-
return
|
|
1451
|
+
return import_zod3.z.array(import_zod3.z.any()).parse(requestValue);
|
|
1483
1452
|
}
|
|
1484
1453
|
if (type === "object") {
|
|
1485
|
-
return
|
|
1454
|
+
return import_zod3.z.object({}).strict().parse(requestValue);
|
|
1486
1455
|
}
|
|
1487
1456
|
}
|
|
1488
1457
|
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
@@ -1494,7 +1463,7 @@ function performTypeCheck(requestValue, validator, requestParamName) {
|
|
|
1494
1463
|
}
|
|
1495
1464
|
try {
|
|
1496
1465
|
validatorDataSchemeValue.parse(validator.value);
|
|
1497
|
-
} catch
|
|
1466
|
+
} catch {
|
|
1498
1467
|
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
1499
1468
|
}
|
|
1500
1469
|
}
|
|
@@ -1585,18 +1554,18 @@ async function schemaValidation(req, res, next) {
|
|
|
1585
1554
|
}
|
|
1586
1555
|
|
|
1587
1556
|
// src/restura/schemas/resturaConfigSchema.ts
|
|
1588
|
-
var
|
|
1589
|
-
var
|
|
1590
|
-
var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
|
|
1557
|
+
var import_v42 = require("zod/v4");
|
|
1558
|
+
var isTsx = process.argv[1]?.endsWith(".ts");
|
|
1591
1559
|
var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
|
|
1592
1560
|
var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
|
|
1593
|
-
var resturaConfigSchema =
|
|
1594
|
-
authToken:
|
|
1595
|
-
sendErrorStackTrace:
|
|
1596
|
-
schemaFilePath:
|
|
1597
|
-
customApiFolderPath:
|
|
1598
|
-
generatedTypesPath:
|
|
1599
|
-
fileTempCachePath:
|
|
1561
|
+
var resturaConfigSchema = import_v42.z.object({
|
|
1562
|
+
authToken: import_v42.z.string().min(1, "Missing Restura Auth Token"),
|
|
1563
|
+
sendErrorStackTrace: import_v42.z.boolean().default(false),
|
|
1564
|
+
schemaFilePath: import_v42.z.string().default(process.cwd() + "/restura.schema.json"),
|
|
1565
|
+
customApiFolderPath: import_v42.z.string().default(process.cwd() + customApiFolderPath),
|
|
1566
|
+
generatedTypesPath: import_v42.z.string().default(process.cwd() + "/src/@types"),
|
|
1567
|
+
fileTempCachePath: import_v42.z.string().optional(),
|
|
1568
|
+
scratchDatabaseSuffix: import_v42.z.string().optional()
|
|
1600
1569
|
});
|
|
1601
1570
|
|
|
1602
1571
|
// src/restura/sql/PsqlEngine.ts
|
|
@@ -1682,13 +1651,14 @@ function SQL(strings, ...values) {
|
|
|
1682
1651
|
|
|
1683
1652
|
// src/restura/sql/PsqlConnection.ts
|
|
1684
1653
|
var PsqlConnection = class {
|
|
1654
|
+
instanceId;
|
|
1685
1655
|
constructor(instanceId) {
|
|
1686
1656
|
this.instanceId = instanceId || import_crypto.default.randomUUID();
|
|
1687
1657
|
}
|
|
1688
1658
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1689
1659
|
async queryOne(query, options, requesterDetails) {
|
|
1690
1660
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1691
|
-
const meta =
|
|
1661
|
+
const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
|
|
1692
1662
|
this.logSqlStatement(formattedQuery, options, meta);
|
|
1693
1663
|
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1694
1664
|
`;
|
|
@@ -1701,7 +1671,7 @@ var PsqlConnection = class {
|
|
|
1701
1671
|
return response.rows[0];
|
|
1702
1672
|
} catch (error) {
|
|
1703
1673
|
if (RsError.isRsError(error)) throw error;
|
|
1704
|
-
if (
|
|
1674
|
+
if (error?.routine === "_bt_check_unique") {
|
|
1705
1675
|
throw new RsError("DUPLICATE", error.message);
|
|
1706
1676
|
}
|
|
1707
1677
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
@@ -1710,7 +1680,7 @@ var PsqlConnection = class {
|
|
|
1710
1680
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1711
1681
|
async runQuery(query, options, requesterDetails) {
|
|
1712
1682
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1713
|
-
const meta =
|
|
1683
|
+
const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
|
|
1714
1684
|
this.logSqlStatement(formattedQuery, options, meta);
|
|
1715
1685
|
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1716
1686
|
`;
|
|
@@ -1720,7 +1690,7 @@ var PsqlConnection = class {
|
|
|
1720
1690
|
this.logQueryDuration(startTime);
|
|
1721
1691
|
return response.rows;
|
|
1722
1692
|
} catch (error) {
|
|
1723
|
-
if (
|
|
1693
|
+
if (error?.routine === "_bt_check_unique") {
|
|
1724
1694
|
throw new RsError("DUPLICATE", error.message);
|
|
1725
1695
|
}
|
|
1726
1696
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
@@ -1770,13 +1740,20 @@ var PsqlPool = class extends PsqlConnection {
|
|
|
1770
1740
|
super();
|
|
1771
1741
|
this.poolConfig = poolConfig;
|
|
1772
1742
|
this.pool = new Pool(poolConfig);
|
|
1773
|
-
this.queryOne("SELECT NOW();", [], {
|
|
1743
|
+
this.queryOne("SELECT NOW();", [], {
|
|
1744
|
+
isSystemUser: true,
|
|
1745
|
+
role: "",
|
|
1746
|
+
host: "localhost",
|
|
1747
|
+
ipAddress: "",
|
|
1748
|
+
scopes: []
|
|
1749
|
+
}).then(() => {
|
|
1774
1750
|
logger.info("Connected to PostgreSQL database");
|
|
1775
1751
|
}).catch((error) => {
|
|
1776
1752
|
logger.error("Error connecting to database", error);
|
|
1777
1753
|
process.exit(1);
|
|
1778
1754
|
});
|
|
1779
1755
|
}
|
|
1756
|
+
pool;
|
|
1780
1757
|
async query(query, values) {
|
|
1781
1758
|
return this.pool.query(query, values);
|
|
1782
1759
|
}
|
|
@@ -1786,7 +1763,12 @@ var PsqlPool = class extends PsqlConnection {
|
|
|
1786
1763
|
var import_core_utils4 = require("@redskytech/core-utils");
|
|
1787
1764
|
var SqlEngine = class {
|
|
1788
1765
|
async runQueryForRoute(req, routeData, schema) {
|
|
1789
|
-
if (!this.
|
|
1766
|
+
if (!this.canRequesterAccessTable(
|
|
1767
|
+
req.requesterDetails.role,
|
|
1768
|
+
req.requesterDetails.scopes,
|
|
1769
|
+
schema,
|
|
1770
|
+
routeData.table
|
|
1771
|
+
))
|
|
1790
1772
|
throw new RsError("FORBIDDEN", "You do not have permission to access this table");
|
|
1791
1773
|
switch (routeData.method) {
|
|
1792
1774
|
case "POST":
|
|
@@ -1805,7 +1787,7 @@ var SqlEngine = class {
|
|
|
1805
1787
|
if (!tableSchema) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
|
|
1806
1788
|
return tableSchema;
|
|
1807
1789
|
}
|
|
1808
|
-
|
|
1790
|
+
canRequesterAccessColumn(requesterRole, requesterScopes, schema, item, joins) {
|
|
1809
1791
|
if (item.type) return true;
|
|
1810
1792
|
if (item.selector) {
|
|
1811
1793
|
let tableName = item.selector.split(".")[0];
|
|
@@ -1821,26 +1803,36 @@ var SqlEngine = class {
|
|
|
1821
1803
|
const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
|
|
1822
1804
|
if (!columnSchema)
|
|
1823
1805
|
throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1806
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.roles)) {
|
|
1807
|
+
if (!requesterRole) return false;
|
|
1808
|
+
return columnSchema.roles.includes(requesterRole);
|
|
1809
|
+
}
|
|
1810
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.scopes)) {
|
|
1811
|
+
if (!requesterScopes) return false;
|
|
1812
|
+
return columnSchema.scopes.some((scope) => requesterScopes.includes(scope));
|
|
1813
|
+
}
|
|
1814
|
+
return true;
|
|
1828
1815
|
}
|
|
1829
1816
|
if (item.subquery) {
|
|
1830
1817
|
return import_core_utils4.ObjectUtils.isArrayWithData(
|
|
1831
1818
|
item.subquery.properties.filter((nestedItem) => {
|
|
1832
|
-
return this.
|
|
1819
|
+
return this.canRequesterAccessColumn(requesterRole, requesterScopes, schema, nestedItem, joins);
|
|
1833
1820
|
})
|
|
1834
1821
|
);
|
|
1835
1822
|
}
|
|
1836
1823
|
return false;
|
|
1837
1824
|
}
|
|
1838
|
-
|
|
1825
|
+
canRequesterAccessTable(requesterRole, requesterScopes, schema, tableName) {
|
|
1839
1826
|
const tableSchema = this.getTableSchema(schema, tableName);
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1827
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.roles)) {
|
|
1828
|
+
if (!requesterRole) return false;
|
|
1829
|
+
return tableSchema.roles.includes(requesterRole);
|
|
1830
|
+
}
|
|
1831
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.scopes)) {
|
|
1832
|
+
if (!requesterScopes) return false;
|
|
1833
|
+
return tableSchema.scopes.some((scope) => requesterScopes.includes(scope));
|
|
1834
|
+
}
|
|
1835
|
+
return true;
|
|
1844
1836
|
}
|
|
1845
1837
|
replaceParamKeywords(value, routeData, req, sqlParams) {
|
|
1846
1838
|
let returnValue = value;
|
|
@@ -1849,11 +1841,10 @@ var SqlEngine = class {
|
|
|
1849
1841
|
return returnValue;
|
|
1850
1842
|
}
|
|
1851
1843
|
replaceLocalParamKeywords(value, routeData, req, sqlParams) {
|
|
1852
|
-
var _a2;
|
|
1853
1844
|
if (!routeData.request) return value;
|
|
1854
1845
|
const data = req.data;
|
|
1855
1846
|
if (typeof value === "string") {
|
|
1856
|
-
|
|
1847
|
+
value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
|
|
1857
1848
|
const requestParam = routeData.request.find((item) => {
|
|
1858
1849
|
return item.name === param.replace("$", "");
|
|
1859
1850
|
});
|
|
@@ -1866,9 +1857,8 @@ var SqlEngine = class {
|
|
|
1866
1857
|
return value;
|
|
1867
1858
|
}
|
|
1868
1859
|
replaceGlobalParamKeywords(value, routeData, req, sqlParams) {
|
|
1869
|
-
var _a2;
|
|
1870
1860
|
if (typeof value === "string") {
|
|
1871
|
-
|
|
1861
|
+
value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
|
|
1872
1862
|
param = param.replace("#", "");
|
|
1873
1863
|
const globalParamValue = req.requesterDetails[param];
|
|
1874
1864
|
if (!globalParamValue)
|
|
@@ -1958,10 +1948,32 @@ negate = "!"
|
|
|
1958
1948
|
operator = "and"i / "or"i
|
|
1959
1949
|
|
|
1960
1950
|
|
|
1961
|
-
column =
|
|
1962
|
-
|
|
1963
|
-
|
|
1951
|
+
column = first:text rest:("." text)* {
|
|
1952
|
+
const partsArray = [first];
|
|
1953
|
+
if (rest && rest.length > 0) {
|
|
1954
|
+
partsArray.push(...rest.map(item => item[1]));
|
|
1955
|
+
}
|
|
1964
1956
|
|
|
1957
|
+
if (partsArray.length > 3) {
|
|
1958
|
+
throw new SyntaxError('Column path cannot have more than 3 parts (table.column.jsonField)');
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
if (partsArray.length === 1) {
|
|
1962
|
+
return quoteSqlIdentity(partsArray[0]);
|
|
1963
|
+
}
|
|
1964
|
+
const tableName = quoteSqlIdentity(partsArray[0]);
|
|
1965
|
+
|
|
1966
|
+
// If we only have two parts (table.column), use regular dot notation
|
|
1967
|
+
if (partsArray.length === 2) {
|
|
1968
|
+
return tableName + "." + quoteSqlIdentity(partsArray[1]);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// For JSON paths (more than 2 parts), first part is a column, last part uses ->>
|
|
1972
|
+
const jsonColumn = quoteSqlIdentity(partsArray[1]);
|
|
1973
|
+
const lastPart = partsArray[partsArray.length - 1];
|
|
1974
|
+
const result = tableName + "." + jsonColumn + "->>'" + lastPart + "'";
|
|
1975
|
+
return result;
|
|
1976
|
+
}
|
|
1965
1977
|
|
|
1966
1978
|
text = text:[a-z0-9 \\t\\r\\n\\-_:@']i+ { return text.join(""); }
|
|
1967
1979
|
|
|
@@ -1991,19 +2003,24 @@ var filterPsqlParser_default = filterPsqlParser;
|
|
|
1991
2003
|
var { Client, types } = import_pg2.default;
|
|
1992
2004
|
var systemUser = {
|
|
1993
2005
|
role: "",
|
|
2006
|
+
scopes: [],
|
|
1994
2007
|
host: "",
|
|
1995
2008
|
ipAddress: "",
|
|
1996
2009
|
isSystemUser: true
|
|
1997
2010
|
};
|
|
1998
2011
|
var PsqlEngine = class extends SqlEngine {
|
|
1999
|
-
constructor(psqlConnectionPool, shouldListenForDbTriggers = false) {
|
|
2012
|
+
constructor(psqlConnectionPool, shouldListenForDbTriggers = false, scratchDatabaseSuffix = "") {
|
|
2000
2013
|
super();
|
|
2001
2014
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
2002
2015
|
this.setupPgReturnTypes();
|
|
2003
2016
|
if (shouldListenForDbTriggers) {
|
|
2004
2017
|
this.setupTriggerListeners = this.listenForDbTriggers();
|
|
2005
2018
|
}
|
|
2019
|
+
this.scratchDbName = `${psqlConnectionPool.poolConfig.database}_scratch${scratchDatabaseSuffix ? `_${scratchDatabaseSuffix}` : ""}`;
|
|
2006
2020
|
}
|
|
2021
|
+
setupTriggerListeners;
|
|
2022
|
+
triggerClient;
|
|
2023
|
+
scratchDbName = "";
|
|
2007
2024
|
async close() {
|
|
2008
2025
|
if (this.triggerClient) {
|
|
2009
2026
|
await this.triggerClient.end();
|
|
@@ -2132,27 +2149,22 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2132
2149
|
sqlStatements.push(triggers.join("\n"));
|
|
2133
2150
|
return sqlStatements.join("\n\n");
|
|
2134
2151
|
}
|
|
2135
|
-
async
|
|
2136
|
-
var _a2, _b;
|
|
2152
|
+
async getNewPublicSchemaAndScratchPool() {
|
|
2137
2153
|
const scratchDbExists = await this.psqlConnectionPool.runQuery(
|
|
2138
2154
|
`SELECT *
|
|
2139
2155
|
FROM pg_database
|
|
2140
|
-
WHERE datname = '${this.
|
|
2156
|
+
WHERE datname = '${this.scratchDbName}';`,
|
|
2141
2157
|
[],
|
|
2142
2158
|
systemUser
|
|
2143
2159
|
);
|
|
2144
2160
|
if (scratchDbExists.length === 0) {
|
|
2145
|
-
await this.psqlConnectionPool.runQuery(
|
|
2146
|
-
`CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
|
|
2147
|
-
[],
|
|
2148
|
-
systemUser
|
|
2149
|
-
);
|
|
2161
|
+
await this.psqlConnectionPool.runQuery(`CREATE DATABASE ${this.scratchDbName};`, [], systemUser);
|
|
2150
2162
|
}
|
|
2151
2163
|
const scratchPool = new PsqlPool({
|
|
2152
2164
|
host: this.psqlConnectionPool.poolConfig.host,
|
|
2153
2165
|
port: this.psqlConnectionPool.poolConfig.port,
|
|
2154
2166
|
user: this.psqlConnectionPool.poolConfig.user,
|
|
2155
|
-
database: this.
|
|
2167
|
+
database: this.scratchDbName,
|
|
2156
2168
|
password: this.psqlConnectionPool.poolConfig.password,
|
|
2157
2169
|
max: this.psqlConnectionPool.poolConfig.max,
|
|
2158
2170
|
idleTimeoutMillis: this.psqlConnectionPool.poolConfig.idleTimeoutMillis,
|
|
@@ -2165,16 +2177,17 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2165
2177
|
systemUser
|
|
2166
2178
|
);
|
|
2167
2179
|
const schemaComment = await this.psqlConnectionPool.runQuery(
|
|
2168
|
-
`
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2180
|
+
`
|
|
2181
|
+
SELECT pg_description.description
|
|
2182
|
+
FROM pg_description
|
|
2183
|
+
JOIN pg_namespace ON pg_namespace.oid = pg_description.objoid
|
|
2184
|
+
WHERE pg_namespace.nspname = 'public';`,
|
|
2172
2185
|
[],
|
|
2173
2186
|
systemUser
|
|
2174
2187
|
);
|
|
2175
|
-
if (
|
|
2188
|
+
if (schemaComment[0]?.description) {
|
|
2176
2189
|
await scratchPool.runQuery(
|
|
2177
|
-
`COMMENT ON SCHEMA public IS '${
|
|
2190
|
+
`COMMENT ON SCHEMA public IS '${schemaComment[0]?.description}';`,
|
|
2178
2191
|
[],
|
|
2179
2192
|
systemUser
|
|
2180
2193
|
);
|
|
@@ -2182,7 +2195,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2182
2195
|
return scratchPool;
|
|
2183
2196
|
}
|
|
2184
2197
|
async diffDatabaseToSchema(schema) {
|
|
2185
|
-
const scratchPool = await this.
|
|
2198
|
+
const scratchPool = await this.getNewPublicSchemaAndScratchPool();
|
|
2186
2199
|
await this.createDatabaseFromSchema(schema, scratchPool);
|
|
2187
2200
|
const originalClient = new Client({
|
|
2188
2201
|
database: this.psqlConnectionPool.poolConfig.database,
|
|
@@ -2192,7 +2205,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2192
2205
|
port: this.psqlConnectionPool.poolConfig.port
|
|
2193
2206
|
});
|
|
2194
2207
|
const scratchClient = new Client({
|
|
2195
|
-
database: this.
|
|
2208
|
+
database: this.scratchDbName,
|
|
2196
2209
|
user: this.psqlConnectionPool.poolConfig.user,
|
|
2197
2210
|
password: this.psqlConnectionPool.poolConfig.password,
|
|
2198
2211
|
host: this.psqlConnectionPool.poolConfig.host,
|
|
@@ -2207,24 +2220,30 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2207
2220
|
await Promise.all(endPromises);
|
|
2208
2221
|
return diff.join("\n");
|
|
2209
2222
|
}
|
|
2210
|
-
createNestedSelect(req, schema, item, routeData,
|
|
2223
|
+
createNestedSelect(req, schema, item, routeData, sqlParams) {
|
|
2211
2224
|
if (!item.subquery) return "";
|
|
2212
2225
|
if (!import_core_utils5.ObjectUtils.isArrayWithData(
|
|
2213
2226
|
item.subquery.properties.filter((nestedItem) => {
|
|
2214
|
-
return this.
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2227
|
+
return this.canRequesterAccessColumn(
|
|
2228
|
+
req.requesterDetails.role,
|
|
2229
|
+
req.requesterDetails.scopes,
|
|
2230
|
+
schema,
|
|
2231
|
+
nestedItem,
|
|
2232
|
+
[...routeData.joins, ...item.subquery.joins]
|
|
2233
|
+
);
|
|
2218
2234
|
})
|
|
2219
2235
|
)) {
|
|
2220
2236
|
return "'[]'";
|
|
2221
2237
|
}
|
|
2222
2238
|
return `COALESCE((SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
2223
2239
|
${item.subquery.properties.map((nestedItem) => {
|
|
2224
|
-
if (!this.
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2240
|
+
if (!this.canRequesterAccessColumn(
|
|
2241
|
+
req.requesterDetails.role,
|
|
2242
|
+
req.requesterDetails.scopes,
|
|
2243
|
+
schema,
|
|
2244
|
+
nestedItem,
|
|
2245
|
+
[...routeData.joins, ...item.subquery.joins]
|
|
2246
|
+
)) {
|
|
2228
2247
|
return;
|
|
2229
2248
|
}
|
|
2230
2249
|
if (nestedItem.subquery) {
|
|
@@ -2234,7 +2253,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2234
2253
|
schema,
|
|
2235
2254
|
nestedItem,
|
|
2236
2255
|
routeData,
|
|
2237
|
-
userRole,
|
|
2238
2256
|
sqlParams
|
|
2239
2257
|
)}`;
|
|
2240
2258
|
}
|
|
@@ -2243,7 +2261,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2243
2261
|
))
|
|
2244
2262
|
FROM
|
|
2245
2263
|
"${item.subquery.table}"
|
|
2246
|
-
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema,
|
|
2264
|
+
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, sqlParams)}
|
|
2247
2265
|
${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
|
|
2248
2266
|
), '[]')`;
|
|
2249
2267
|
}
|
|
@@ -2253,7 +2271,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2253
2271
|
(routeData.assignments || []).forEach((assignment) => {
|
|
2254
2272
|
parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
2255
2273
|
});
|
|
2256
|
-
const query = insertObjectQuery(routeData.table,
|
|
2274
|
+
const query = insertObjectQuery(routeData.table, { ...req.data, ...parameterObj });
|
|
2257
2275
|
const createdItem = await this.psqlConnectionPool.queryOne(
|
|
2258
2276
|
query,
|
|
2259
2277
|
sqlParams,
|
|
@@ -2268,24 +2286,29 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2268
2286
|
};
|
|
2269
2287
|
const whereData = [whereId];
|
|
2270
2288
|
req.data = { id: insertId };
|
|
2271
|
-
return this.executeGetRequest(req,
|
|
2289
|
+
return this.executeGetRequest(req, { ...routeData, where: whereData }, schema);
|
|
2272
2290
|
}
|
|
2273
2291
|
async executeGetRequest(req, routeData, schema) {
|
|
2274
2292
|
const DEFAULT_PAGED_PAGE_NUMBER = 0;
|
|
2275
2293
|
const DEFAULT_PAGED_PER_PAGE_NUMBER = 25;
|
|
2276
2294
|
const sqlParams = [];
|
|
2277
|
-
const userRole = req.requesterDetails.role;
|
|
2278
2295
|
let sqlStatement = "";
|
|
2279
2296
|
const selectColumns = [];
|
|
2280
2297
|
routeData.response.forEach((item) => {
|
|
2281
|
-
if (item.subquery || this.
|
|
2298
|
+
if (item.subquery || this.canRequesterAccessColumn(
|
|
2299
|
+
req.requesterDetails.role,
|
|
2300
|
+
req.requesterDetails.scopes,
|
|
2301
|
+
schema,
|
|
2302
|
+
item,
|
|
2303
|
+
routeData.joins
|
|
2304
|
+
))
|
|
2282
2305
|
selectColumns.push(item);
|
|
2283
2306
|
});
|
|
2284
2307
|
if (!selectColumns.length) throw new RsError("FORBIDDEN", `You do not have permission to access this data.`);
|
|
2285
2308
|
let selectStatement = "SELECT \n";
|
|
2286
2309
|
selectStatement += ` ${selectColumns.map((item) => {
|
|
2287
2310
|
if (item.subquery) {
|
|
2288
|
-
return `${this.createNestedSelect(req, schema, item, routeData,
|
|
2311
|
+
return `${this.createNestedSelect(req, schema, item, routeData, sqlParams)} AS ${escapeColumnName(
|
|
2289
2312
|
item.name
|
|
2290
2313
|
)}`;
|
|
2291
2314
|
}
|
|
@@ -2300,7 +2323,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2300
2323
|
routeData.table,
|
|
2301
2324
|
routeData,
|
|
2302
2325
|
schema,
|
|
2303
|
-
userRole,
|
|
2304
2326
|
sqlParams
|
|
2305
2327
|
);
|
|
2306
2328
|
sqlStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
@@ -2344,7 +2366,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2344
2366
|
}
|
|
2345
2367
|
async executeUpdateRequest(req, routeData, schema) {
|
|
2346
2368
|
const sqlParams = [];
|
|
2347
|
-
const
|
|
2369
|
+
const { id, ...bodyNoId } = req.body;
|
|
2348
2370
|
const table = schema.database.find((item) => {
|
|
2349
2371
|
return item.name === routeData.table;
|
|
2350
2372
|
});
|
|
@@ -2373,7 +2395,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2373
2395
|
routeData.table,
|
|
2374
2396
|
routeData,
|
|
2375
2397
|
schema,
|
|
2376
|
-
req.requesterDetails.role,
|
|
2377
2398
|
sqlParams
|
|
2378
2399
|
);
|
|
2379
2400
|
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
@@ -2385,10 +2406,15 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
|
2385
2406
|
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
2386
2407
|
return true;
|
|
2387
2408
|
}
|
|
2388
|
-
generateJoinStatements(req, joins, baseTable, routeData, schema,
|
|
2409
|
+
generateJoinStatements(req, joins, baseTable, routeData, schema, sqlParams) {
|
|
2389
2410
|
let joinStatements = "";
|
|
2390
2411
|
joins.forEach((item) => {
|
|
2391
|
-
if (!this.
|
|
2412
|
+
if (!this.canRequesterAccessTable(
|
|
2413
|
+
req.requesterDetails.role,
|
|
2414
|
+
req.requesterDetails.scopes,
|
|
2415
|
+
schema,
|
|
2416
|
+
item.table
|
|
2417
|
+
))
|
|
2392
2418
|
throw new RsError("FORBIDDEN", "You do not have permission to access this table");
|
|
2393
2419
|
if (item.custom) {
|
|
2394
2420
|
const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
|
|
@@ -2443,41 +2469,36 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
|
2443
2469
|
`Invalid where clause in route ${routeData.name}, missing required fields if not custom`
|
|
2444
2470
|
);
|
|
2445
2471
|
let operator = item.operator;
|
|
2446
|
-
let value = item.value;
|
|
2447
2472
|
if (operator === "LIKE") {
|
|
2448
|
-
value = `'%${value}%'`;
|
|
2449
|
-
} else if (operator === "NOT LIKE") {
|
|
2450
|
-
value = `'%${value}%'`;
|
|
2473
|
+
item.value = `'%${item.value}%'`;
|
|
2451
2474
|
} else if (operator === "STARTS WITH") {
|
|
2452
2475
|
operator = "LIKE";
|
|
2453
|
-
value = `'${value}%'`;
|
|
2476
|
+
item.value = `'${item.value}%'`;
|
|
2454
2477
|
} else if (operator === "ENDS WITH") {
|
|
2455
2478
|
operator = "LIKE";
|
|
2456
|
-
value = `'%${value}'`;
|
|
2479
|
+
item.value = `'%${item.value}'`;
|
|
2457
2480
|
}
|
|
2458
|
-
const replacedValue = this.replaceParamKeywords(value, routeData, req, sqlParams);
|
|
2481
|
+
const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
|
|
2459
2482
|
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator.replace("LIKE", "ILIKE")} ${["IN", "NOT IN"].includes(operator) ? `(${replacedValue})` : replacedValue}
|
|
2460
2483
|
`;
|
|
2461
2484
|
});
|
|
2462
2485
|
const data = req.data;
|
|
2463
|
-
if (routeData.type === "PAGED" && !!
|
|
2486
|
+
if (routeData.type === "PAGED" && !!data?.filter) {
|
|
2464
2487
|
let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
2465
|
-
var _a2;
|
|
2466
2488
|
const requestParam = routeData.request.find((item) => {
|
|
2467
2489
|
return item.name === value.replace("$", "");
|
|
2468
2490
|
});
|
|
2469
2491
|
if (!requestParam)
|
|
2470
2492
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
2471
|
-
return
|
|
2493
|
+
return data[requestParam.name]?.toString() || "";
|
|
2472
2494
|
});
|
|
2473
2495
|
statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
2474
|
-
var _a2;
|
|
2475
2496
|
const requestParam = routeData.request.find((item) => {
|
|
2476
2497
|
return item.name === value.replace("#", "");
|
|
2477
2498
|
});
|
|
2478
2499
|
if (!requestParam)
|
|
2479
2500
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
2480
|
-
return
|
|
2501
|
+
return data[requestParam.name]?.toString() || "";
|
|
2481
2502
|
});
|
|
2482
2503
|
statement = filterPsqlParser_default.parse(statement);
|
|
2483
2504
|
if (whereClause.startsWith("WHERE")) {
|
|
@@ -2718,8 +2739,9 @@ var import_internal3 = require("@restura/internal");
|
|
|
2718
2739
|
var import_bluebird3 = __toESM(require("bluebird"));
|
|
2719
2740
|
var os2 = __toESM(require("os"));
|
|
2720
2741
|
var TempCache = class {
|
|
2742
|
+
location;
|
|
2743
|
+
maxDurationDays = 7;
|
|
2721
2744
|
constructor(location) {
|
|
2722
|
-
this.maxDurationDays = 7;
|
|
2723
2745
|
this.location = location || os2.tmpdir();
|
|
2724
2746
|
import_internal3.FileUtils.ensureDir(this.location).catch((e) => {
|
|
2725
2747
|
throw e;
|
|
@@ -2744,15 +2766,24 @@ var TempCache = class {
|
|
|
2744
2766
|
|
|
2745
2767
|
// src/restura/restura.ts
|
|
2746
2768
|
var ResturaEngine = class {
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2769
|
+
// Make public so other modules can access without re-parsing the config
|
|
2770
|
+
resturaConfig;
|
|
2771
|
+
multerCommonUpload;
|
|
2772
|
+
resturaRouter;
|
|
2773
|
+
publicEndpoints = {
|
|
2774
|
+
GET: [],
|
|
2775
|
+
POST: [],
|
|
2776
|
+
PUT: [],
|
|
2777
|
+
PATCH: [],
|
|
2778
|
+
DELETE: []
|
|
2779
|
+
};
|
|
2780
|
+
expressApp;
|
|
2781
|
+
schema;
|
|
2782
|
+
responseValidator;
|
|
2783
|
+
authenticationHandler;
|
|
2784
|
+
customTypeValidation;
|
|
2785
|
+
psqlConnectionPool;
|
|
2786
|
+
psqlEngine;
|
|
2756
2787
|
/**
|
|
2757
2788
|
* Initializes the Restura engine with the provided Express application.
|
|
2758
2789
|
*
|
|
@@ -2760,11 +2791,11 @@ var ResturaEngine = class {
|
|
|
2760
2791
|
* @returns A promise that resolves when the initialization is complete.
|
|
2761
2792
|
*/
|
|
2762
2793
|
async init(app, authenticationHandler, psqlConnectionPool) {
|
|
2763
|
-
this.resturaConfig = import_internal4.config.validate("restura", resturaConfigSchema);
|
|
2794
|
+
this.resturaConfig = await import_internal4.config.validate("restura", resturaConfigSchema);
|
|
2764
2795
|
this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
|
|
2765
2796
|
new TempCache(this.resturaConfig.fileTempCachePath);
|
|
2766
2797
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
2767
|
-
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
|
|
2798
|
+
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true, this.resturaConfig.scratchDatabaseSuffix);
|
|
2768
2799
|
await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
|
|
2769
2800
|
this.authenticationHandler = authenticationHandler;
|
|
2770
2801
|
app.use((0, import_compression.default)());
|
|
@@ -2773,7 +2804,7 @@ var ResturaEngine = class {
|
|
|
2773
2804
|
app.use((0, import_cookie_parser.default)());
|
|
2774
2805
|
app.disable("x-powered-by");
|
|
2775
2806
|
app.use("/", addApiResponseFunctions);
|
|
2776
|
-
app.use("/api/",
|
|
2807
|
+
app.use("/api/", authenticateRequester(this.authenticationHandler));
|
|
2777
2808
|
app.use("/restura", this.resturaAuthentication);
|
|
2778
2809
|
app.put(
|
|
2779
2810
|
"/restura/v1/schema",
|
|
@@ -2794,7 +2825,7 @@ var ResturaEngine = class {
|
|
|
2794
2825
|
}
|
|
2795
2826
|
/**
|
|
2796
2827
|
* Determines if a given endpoint is public based on the HTTP method and full URL. This
|
|
2797
|
-
* is determined on whether the endpoint in the schema has no roles assigned to it.
|
|
2828
|
+
* is determined on whether the endpoint in the schema has no roles or scopes assigned to it.
|
|
2798
2829
|
*
|
|
2799
2830
|
* @param method - The HTTP method (e.g., 'GET', 'POST', 'PUT', 'PATCH', 'DELETE').
|
|
2800
2831
|
* @param fullUrl - The full URL of the endpoint.
|
|
@@ -2883,7 +2914,8 @@ var ResturaEngine = class {
|
|
|
2883
2914
|
route.path = route.path.startsWith("/") ? route.path : `/${route.path}`;
|
|
2884
2915
|
route.path = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
|
|
2885
2916
|
const fullUrl = `${baseUrl}${route.path}`;
|
|
2886
|
-
if (route.roles.length === 0
|
|
2917
|
+
if (route.roles.length === 0 && route.scopes.length === 0)
|
|
2918
|
+
this.publicEndpoints[route.method].push(fullUrl);
|
|
2887
2919
|
this.resturaRouter[route.method.toLowerCase()](
|
|
2888
2920
|
route.path,
|
|
2889
2921
|
// <-- Notice we only use path here since the baseUrl is already added to the router.
|
|
@@ -2933,10 +2965,10 @@ var ResturaEngine = class {
|
|
|
2933
2965
|
);
|
|
2934
2966
|
this.generateResturaGlobalTypes(import_path5.default.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
|
|
2935
2967
|
}
|
|
2936
|
-
async getSchema(
|
|
2968
|
+
async getSchema(_req, res) {
|
|
2937
2969
|
res.send({ data: this.schema });
|
|
2938
2970
|
}
|
|
2939
|
-
async getSchemaAndTypes(
|
|
2971
|
+
async getSchemaAndTypes(_req, res) {
|
|
2940
2972
|
try {
|
|
2941
2973
|
const schema = await this.getLatestFileSystemSchema();
|
|
2942
2974
|
const apiText = await apiGenerator(schema);
|
|
@@ -2947,8 +2979,7 @@ var ResturaEngine = class {
|
|
|
2947
2979
|
}
|
|
2948
2980
|
}
|
|
2949
2981
|
async getMulterFilesIfAny(req, res, routeData) {
|
|
2950
|
-
|
|
2951
|
-
if (!((_a2 = req.header("content-type")) == null ? void 0 : _a2.includes("multipart/form-data"))) return;
|
|
2982
|
+
if (!req.header("content-type")?.includes("multipart/form-data")) return;
|
|
2952
2983
|
if (!this.isCustomRoute(routeData)) return;
|
|
2953
2984
|
if (!routeData.fileUploadType) {
|
|
2954
2985
|
throw new RsError("BAD_REQUEST", "File upload type not defined for route");
|
|
@@ -3011,16 +3042,17 @@ var ResturaEngine = class {
|
|
|
3011
3042
|
await customFunction(req, res, routeData);
|
|
3012
3043
|
}
|
|
3013
3044
|
async storeFileSystemSchema() {
|
|
3014
|
-
const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema),
|
|
3015
|
-
parser: "json"
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3045
|
+
const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), {
|
|
3046
|
+
parser: "json",
|
|
3047
|
+
...{
|
|
3048
|
+
trailingComma: "none",
|
|
3049
|
+
tabWidth: 4,
|
|
3050
|
+
useTabs: true,
|
|
3051
|
+
endOfLine: "lf",
|
|
3052
|
+
printWidth: 120,
|
|
3053
|
+
singleQuote: true
|
|
3054
|
+
}
|
|
3055
|
+
});
|
|
3024
3056
|
import_fs4.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
|
|
3025
3057
|
}
|
|
3026
3058
|
resetPublicEndpoints() {
|
|
@@ -3033,9 +3065,13 @@ var ResturaEngine = class {
|
|
|
3033
3065
|
};
|
|
3034
3066
|
}
|
|
3035
3067
|
validateAuthorization(req, routeData) {
|
|
3036
|
-
const
|
|
3037
|
-
|
|
3038
|
-
if (
|
|
3068
|
+
const requesterRole = req.requesterDetails.role;
|
|
3069
|
+
const requesterScopes = req.requesterDetails.scopes;
|
|
3070
|
+
if (routeData.roles.length === 0 && routeData.scopes.length === 0) return;
|
|
3071
|
+
if (requesterRole && routeData.roles.length > 0 && !routeData.roles.includes(requesterRole))
|
|
3072
|
+
throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
|
|
3073
|
+
if (requesterScopes.length > 0 && routeData.scopes.length > 0 && !routeData.scopes.some((scope) => requesterScopes.includes(scope)))
|
|
3074
|
+
throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
|
|
3039
3075
|
}
|
|
3040
3076
|
getRouteData(method, baseUrl, path5) {
|
|
3041
3077
|
const endpoint = this.schema.endpoints.find((item) => {
|
|
@@ -3089,6 +3125,9 @@ var PsqlTransaction = class extends PsqlConnection {
|
|
|
3089
3125
|
this.connectPromise = this.client.connect();
|
|
3090
3126
|
this.beginTransactionPromise = this.beginTransaction();
|
|
3091
3127
|
}
|
|
3128
|
+
client;
|
|
3129
|
+
beginTransactionPromise;
|
|
3130
|
+
connectPromise;
|
|
3092
3131
|
async close() {
|
|
3093
3132
|
if (this.client) {
|
|
3094
3133
|
await this.client.end();
|