@restura/core 0.2.1 → 1.0.0-beta.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.d.mts +517 -472
- package/dist/index.d.ts +517 -472
- package/dist/index.js +380 -347
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +249 -217
- 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"
|
|
@@ -111,7 +81,7 @@ var consoleFormat = import_logform.format.combine(
|
|
|
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,24 @@ var logger = import_winston.default.createLogger({
|
|
|
119
89
|
import_logform.format.errors({ stack: true }),
|
|
120
90
|
import_logform.format.json()
|
|
121
91
|
),
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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 })]
|
|
93
|
+
});
|
|
94
|
+
import_internal.config.validate("logger", loggerConfigSchema).then((loggerConfig) => {
|
|
95
|
+
if (loggerConfig.level) {
|
|
96
|
+
logger.level = loggerConfig.level;
|
|
97
|
+
}
|
|
98
|
+
}).catch((error) => {
|
|
99
|
+
logger.error("Failed to load logger config:", error);
|
|
133
100
|
});
|
|
134
101
|
|
|
135
102
|
// src/restura/eventManager.ts
|
|
136
103
|
var import_bluebird = __toESM(require("bluebird"));
|
|
137
104
|
var EventManager = class {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
144
|
-
}
|
|
105
|
+
actionHandlers = {
|
|
106
|
+
DATABASE_ROW_DELETE: [],
|
|
107
|
+
DATABASE_ROW_INSERT: [],
|
|
108
|
+
DATABASE_COLUMN_UPDATE: []
|
|
109
|
+
};
|
|
145
110
|
addRowInsertHandler(onInsert, filter) {
|
|
146
111
|
this.actionHandlers.DATABASE_ROW_INSERT.push({
|
|
147
112
|
callback: onInsert,
|
|
@@ -338,6 +303,10 @@ var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
|
|
|
338
303
|
return HtmlStatusCodes2;
|
|
339
304
|
})(HtmlStatusCodes || {});
|
|
340
305
|
var RsError = class _RsError {
|
|
306
|
+
err;
|
|
307
|
+
msg;
|
|
308
|
+
status;
|
|
309
|
+
stack;
|
|
341
310
|
constructor(errCode, message) {
|
|
342
311
|
this.err = errCode;
|
|
343
312
|
this.msg = message || "";
|
|
@@ -490,9 +459,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
490
459
|
var import_path = __toESM(require("path"));
|
|
491
460
|
var import_internal2 = require("@restura/internal");
|
|
492
461
|
var CustomApiFactory = class {
|
|
493
|
-
|
|
494
|
-
this.customApis = {};
|
|
495
|
-
}
|
|
462
|
+
customApis = {};
|
|
496
463
|
async loadApiFiles(baseFolderPath) {
|
|
497
464
|
const apiVersions = ["v1"];
|
|
498
465
|
for (const apiVersion of apiVersions) {
|
|
@@ -506,11 +473,10 @@ var CustomApiFactory = class {
|
|
|
506
473
|
return this.customApis[customApiName];
|
|
507
474
|
}
|
|
508
475
|
async addDirectory(directoryPath, apiVersion) {
|
|
509
|
-
var _a2;
|
|
510
476
|
const entries = await import_fs.default.promises.readdir(directoryPath, {
|
|
511
477
|
withFileTypes: true
|
|
512
478
|
});
|
|
513
|
-
const isTsx2 =
|
|
479
|
+
const isTsx2 = process.argv[1]?.endsWith(".ts");
|
|
514
480
|
const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
|
|
515
481
|
const extension = isTsx2 || isTsNode2 ? "ts" : "js";
|
|
516
482
|
const shouldEndWith = `.api.${apiVersion}.${extension}`;
|
|
@@ -575,6 +541,8 @@ var SqlUtils = class _SqlUtils {
|
|
|
575
541
|
|
|
576
542
|
// src/restura/validators/ResponseValidator.ts
|
|
577
543
|
var ResponseValidator = class _ResponseValidator {
|
|
544
|
+
rootMap;
|
|
545
|
+
database;
|
|
578
546
|
constructor(schema) {
|
|
579
547
|
this.database = schema.database;
|
|
580
548
|
this.rootMap = {};
|
|
@@ -650,7 +618,7 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
650
618
|
if (path5.length === 0 || path5.length > 2 || path5[0] === "") return { validator: "any", isOptional: false };
|
|
651
619
|
const tableName = path5.length == 2 ? path5[0] : name, columnName = path5.length == 2 ? path5[1] : path5[0];
|
|
652
620
|
const table = this.database.find((t) => t.name == tableName);
|
|
653
|
-
const column = table
|
|
621
|
+
const column = table?.columns.find((c) => c.name == columnName);
|
|
654
622
|
if (!table || !column) return { validator: "any", isOptional: false };
|
|
655
623
|
let validator = SqlUtils.convertDatabaseTypeToTypescript(
|
|
656
624
|
column.type,
|
|
@@ -734,10 +702,12 @@ var ResponseValidator = class _ResponseValidator {
|
|
|
734
702
|
var ApiTree = class _ApiTree {
|
|
735
703
|
constructor(namespace, database) {
|
|
736
704
|
this.database = database;
|
|
737
|
-
this.data = [];
|
|
738
705
|
this.namespace = namespace;
|
|
739
706
|
this.children = /* @__PURE__ */ new Map();
|
|
740
707
|
}
|
|
708
|
+
namespace;
|
|
709
|
+
data = [];
|
|
710
|
+
children;
|
|
741
711
|
static createRootNode(database) {
|
|
742
712
|
return new _ApiTree(null, database);
|
|
743
713
|
}
|
|
@@ -881,7 +851,7 @@ var ApiTree = class _ApiTree {
|
|
|
881
851
|
tableName = tableAliasSplit[1];
|
|
882
852
|
table = this.database.find((t) => t.name == tableName);
|
|
883
853
|
}
|
|
884
|
-
const column = table
|
|
854
|
+
const column = table?.columns.find((c) => c.name == columnName);
|
|
885
855
|
if (!table || !column) return { responseType: "any", isNullable: false };
|
|
886
856
|
return {
|
|
887
857
|
responseType: SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value),
|
|
@@ -912,16 +882,17 @@ function apiGenerator(schema) {
|
|
|
912
882
|
${schema.customTypes.join("\n")}
|
|
913
883
|
}`;
|
|
914
884
|
}
|
|
915
|
-
return import_prettier.default.format(apiString,
|
|
916
|
-
parser: "typescript"
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
885
|
+
return import_prettier.default.format(apiString, {
|
|
886
|
+
parser: "typescript",
|
|
887
|
+
...{
|
|
888
|
+
trailingComma: "none",
|
|
889
|
+
tabWidth: 4,
|
|
890
|
+
useTabs: true,
|
|
891
|
+
endOfLine: "lf",
|
|
892
|
+
printWidth: 120,
|
|
893
|
+
singleQuote: true
|
|
894
|
+
}
|
|
895
|
+
});
|
|
925
896
|
}
|
|
926
897
|
|
|
927
898
|
// src/restura/generators/customTypeValidationGenerator.ts
|
|
@@ -932,7 +903,7 @@ var TJS = __toESM(require("typescript-json-schema"));
|
|
|
932
903
|
function customTypeValidationGenerator(currentSchema) {
|
|
933
904
|
const schemaObject = {};
|
|
934
905
|
const customInterfaceNames = currentSchema.customTypes.map((customType) => {
|
|
935
|
-
const matches = customType.match(
|
|
906
|
+
const matches = customType.match(/(?<=interface\s)(\w+)|(?<=type\s)(\w+)/g);
|
|
936
907
|
if (matches && matches.length > 0) return matches[0];
|
|
937
908
|
return "";
|
|
938
909
|
}).filter(Boolean);
|
|
@@ -976,16 +947,17 @@ function modelGenerator(schema) {
|
|
|
976
947
|
modelString += convertTable(table);
|
|
977
948
|
}
|
|
978
949
|
modelString += `}`;
|
|
979
|
-
return import_prettier2.default.format(modelString,
|
|
980
|
-
parser: "typescript"
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
950
|
+
return import_prettier2.default.format(modelString, {
|
|
951
|
+
parser: "typescript",
|
|
952
|
+
...{
|
|
953
|
+
trailingComma: "none",
|
|
954
|
+
tabWidth: 4,
|
|
955
|
+
useTabs: true,
|
|
956
|
+
endOfLine: "lf",
|
|
957
|
+
printWidth: 120,
|
|
958
|
+
singleQuote: true
|
|
959
|
+
}
|
|
960
|
+
});
|
|
989
961
|
}
|
|
990
962
|
function convertTable(table) {
|
|
991
963
|
let modelString = ` export interface ${import_core_utils2.StringUtils.capitalizeFirst(table.name)} {
|
|
@@ -1037,20 +1009,21 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
1037
1009
|
htmlStatusCode = 500;
|
|
1038
1010
|
}
|
|
1039
1011
|
}
|
|
1040
|
-
const errorData =
|
|
1012
|
+
const errorData = {
|
|
1041
1013
|
err: shortError,
|
|
1042
|
-
msg
|
|
1043
|
-
|
|
1014
|
+
msg,
|
|
1015
|
+
...restura.resturaConfig.sendErrorStackTrace && stack ? { stack } : {}
|
|
1016
|
+
};
|
|
1044
1017
|
res.status(htmlStatusCode).send(errorData);
|
|
1045
1018
|
};
|
|
1046
1019
|
next();
|
|
1047
1020
|
}
|
|
1048
1021
|
|
|
1049
|
-
// src/restura/middleware/
|
|
1050
|
-
function
|
|
1022
|
+
// src/restura/middleware/authenticateRequester.ts
|
|
1023
|
+
function authenticateRequester(applicationAuthenticateHandler) {
|
|
1051
1024
|
return (req, res, next) => {
|
|
1052
|
-
applicationAuthenticateHandler(req, res, (
|
|
1053
|
-
req.requesterDetails =
|
|
1025
|
+
applicationAuthenticateHandler(req, res, (authenticatedRequesterDetails) => {
|
|
1026
|
+
req.requesterDetails = { host: req.hostname, ipAddress: req.ip || "", ...authenticatedRequesterDetails };
|
|
1054
1027
|
next();
|
|
1055
1028
|
});
|
|
1056
1029
|
};
|
|
@@ -1082,110 +1055,97 @@ var getMulterUpload = (directory) => {
|
|
|
1082
1055
|
};
|
|
1083
1056
|
|
|
1084
1057
|
// src/restura/schemas/resturaSchema.ts
|
|
1085
|
-
var
|
|
1058
|
+
var import_zod2 = require("zod");
|
|
1086
1059
|
|
|
1087
1060
|
// src/restura/schemas/validatorDataSchema.ts
|
|
1088
|
-
var
|
|
1089
|
-
var validatorDataSchemeValue =
|
|
1090
|
-
var validatorDataSchema =
|
|
1091
|
-
type:
|
|
1061
|
+
var import_zod = require("zod");
|
|
1062
|
+
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())]);
|
|
1063
|
+
var validatorDataSchema = import_zod.z.object({
|
|
1064
|
+
type: import_zod.z.enum(["TYPE_CHECK", "MIN", "MAX", "ONE_OF"]),
|
|
1092
1065
|
value: validatorDataSchemeValue
|
|
1093
1066
|
}).strict();
|
|
1094
1067
|
|
|
1095
1068
|
// src/restura/schemas/resturaSchema.ts
|
|
1096
|
-
var orderBySchema =
|
|
1097
|
-
columnName:
|
|
1098
|
-
order:
|
|
1099
|
-
tableName:
|
|
1069
|
+
var orderBySchema = import_zod2.z.object({
|
|
1070
|
+
columnName: import_zod2.z.string(),
|
|
1071
|
+
order: import_zod2.z.enum(["ASC", "DESC"]),
|
|
1072
|
+
tableName: import_zod2.z.string()
|
|
1100
1073
|
}).strict();
|
|
1101
|
-
var groupBySchema =
|
|
1102
|
-
columnName:
|
|
1103
|
-
tableName:
|
|
1074
|
+
var groupBySchema = import_zod2.z.object({
|
|
1075
|
+
columnName: import_zod2.z.string(),
|
|
1076
|
+
tableName: import_zod2.z.string()
|
|
1104
1077
|
}).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()
|
|
1078
|
+
var whereDataSchema = import_zod2.z.object({
|
|
1079
|
+
tableName: import_zod2.z.string().optional(),
|
|
1080
|
+
columnName: import_zod2.z.string().optional(),
|
|
1081
|
+
operator: import_zod2.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH", "IS", "IS NOT"]).optional(),
|
|
1082
|
+
value: import_zod2.z.string().or(import_zod2.z.number()).optional(),
|
|
1083
|
+
custom: import_zod2.z.string().optional(),
|
|
1084
|
+
conjunction: import_zod2.z.enum(["AND", "OR"]).optional()
|
|
1127
1085
|
}).strict();
|
|
1128
|
-
var assignmentDataSchema =
|
|
1129
|
-
name:
|
|
1130
|
-
value:
|
|
1086
|
+
var assignmentDataSchema = import_zod2.z.object({
|
|
1087
|
+
name: import_zod2.z.string(),
|
|
1088
|
+
value: import_zod2.z.string()
|
|
1131
1089
|
}).strict();
|
|
1132
|
-
var joinDataSchema =
|
|
1133
|
-
table:
|
|
1134
|
-
localColumnName:
|
|
1135
|
-
foreignColumnName:
|
|
1136
|
-
custom:
|
|
1137
|
-
type:
|
|
1138
|
-
alias:
|
|
1090
|
+
var joinDataSchema = import_zod2.z.object({
|
|
1091
|
+
table: import_zod2.z.string(),
|
|
1092
|
+
localColumnName: import_zod2.z.string().optional(),
|
|
1093
|
+
foreignColumnName: import_zod2.z.string().optional(),
|
|
1094
|
+
custom: import_zod2.z.string().optional(),
|
|
1095
|
+
type: import_zod2.z.enum(["LEFT", "INNER"]),
|
|
1096
|
+
alias: import_zod2.z.string().optional()
|
|
1139
1097
|
}).strict();
|
|
1140
|
-
var requestDataSchema =
|
|
1141
|
-
name:
|
|
1142
|
-
required:
|
|
1143
|
-
isNullable:
|
|
1144
|
-
validator:
|
|
1098
|
+
var requestDataSchema = import_zod2.z.object({
|
|
1099
|
+
name: import_zod2.z.string(),
|
|
1100
|
+
required: import_zod2.z.boolean(),
|
|
1101
|
+
isNullable: import_zod2.z.boolean().optional(),
|
|
1102
|
+
validator: import_zod2.z.array(validatorDataSchema)
|
|
1145
1103
|
}).strict();
|
|
1146
|
-
var responseDataSchema =
|
|
1147
|
-
name:
|
|
1148
|
-
selector:
|
|
1149
|
-
subquery:
|
|
1150
|
-
table:
|
|
1151
|
-
joins:
|
|
1152
|
-
where:
|
|
1153
|
-
properties:
|
|
1104
|
+
var responseDataSchema = import_zod2.z.object({
|
|
1105
|
+
name: import_zod2.z.string(),
|
|
1106
|
+
selector: import_zod2.z.string().optional(),
|
|
1107
|
+
subquery: import_zod2.z.object({
|
|
1108
|
+
table: import_zod2.z.string(),
|
|
1109
|
+
joins: import_zod2.z.array(joinDataSchema),
|
|
1110
|
+
where: import_zod2.z.array(whereDataSchema),
|
|
1111
|
+
properties: import_zod2.z.array(import_zod2.z.lazy(() => responseDataSchema)),
|
|
1154
1112
|
// Explicit type for the lazy schema
|
|
1155
1113
|
groupBy: groupBySchema.optional(),
|
|
1156
1114
|
orderBy: orderBySchema.optional()
|
|
1157
1115
|
}).optional(),
|
|
1158
|
-
type:
|
|
1116
|
+
type: import_zod2.z.string().optional()
|
|
1117
|
+
// Type allows you to override the type of the response, used in custom selectors
|
|
1159
1118
|
}).strict();
|
|
1160
|
-
var routeDataBaseSchema =
|
|
1161
|
-
method:
|
|
1162
|
-
name:
|
|
1163
|
-
description:
|
|
1164
|
-
path:
|
|
1165
|
-
roles:
|
|
1119
|
+
var routeDataBaseSchema = import_zod2.z.object({
|
|
1120
|
+
method: import_zod2.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
1121
|
+
name: import_zod2.z.string(),
|
|
1122
|
+
description: import_zod2.z.string(),
|
|
1123
|
+
path: import_zod2.z.string(),
|
|
1124
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1125
|
+
scopes: import_zod2.z.array(import_zod2.z.string())
|
|
1166
1126
|
}).strict();
|
|
1167
1127
|
var standardRouteSchema = routeDataBaseSchema.extend({
|
|
1168
|
-
type:
|
|
1169
|
-
table:
|
|
1170
|
-
joins:
|
|
1171
|
-
assignments:
|
|
1172
|
-
where:
|
|
1173
|
-
request:
|
|
1174
|
-
response:
|
|
1128
|
+
type: import_zod2.z.enum(["ONE", "ARRAY", "PAGED"]),
|
|
1129
|
+
table: import_zod2.z.string(),
|
|
1130
|
+
joins: import_zod2.z.array(joinDataSchema),
|
|
1131
|
+
assignments: import_zod2.z.array(assignmentDataSchema),
|
|
1132
|
+
where: import_zod2.z.array(whereDataSchema),
|
|
1133
|
+
request: import_zod2.z.array(requestDataSchema),
|
|
1134
|
+
response: import_zod2.z.array(responseDataSchema),
|
|
1175
1135
|
groupBy: groupBySchema.optional(),
|
|
1176
1136
|
orderBy: orderBySchema.optional()
|
|
1177
1137
|
}).strict();
|
|
1178
1138
|
var customRouteSchema = routeDataBaseSchema.extend({
|
|
1179
|
-
type:
|
|
1180
|
-
responseType:
|
|
1181
|
-
requestType:
|
|
1182
|
-
request:
|
|
1183
|
-
table:
|
|
1184
|
-
joins:
|
|
1185
|
-
assignments:
|
|
1186
|
-
fileUploadType:
|
|
1139
|
+
type: import_zod2.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
|
|
1140
|
+
responseType: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.enum(["string", "number", "boolean"])]),
|
|
1141
|
+
requestType: import_zod2.z.string().optional(),
|
|
1142
|
+
request: import_zod2.z.array(requestDataSchema).optional(),
|
|
1143
|
+
table: import_zod2.z.undefined(),
|
|
1144
|
+
joins: import_zod2.z.undefined(),
|
|
1145
|
+
assignments: import_zod2.z.undefined(),
|
|
1146
|
+
fileUploadType: import_zod2.z.enum(["SINGLE", "MULTIPLE"]).optional()
|
|
1187
1147
|
}).strict();
|
|
1188
|
-
var postgresColumnNumericTypesSchema =
|
|
1148
|
+
var postgresColumnNumericTypesSchema = import_zod2.z.enum([
|
|
1189
1149
|
"SMALLINT",
|
|
1190
1150
|
// 2 bytes, -32,768 to 32,767
|
|
1191
1151
|
"INTEGER",
|
|
@@ -1205,7 +1165,7 @@ var postgresColumnNumericTypesSchema = import_zod3.z.enum([
|
|
|
1205
1165
|
"BIGSERIAL"
|
|
1206
1166
|
// auto-incrementing big integer
|
|
1207
1167
|
]);
|
|
1208
|
-
var postgresColumnStringTypesSchema =
|
|
1168
|
+
var postgresColumnStringTypesSchema = import_zod2.z.enum([
|
|
1209
1169
|
"CHAR",
|
|
1210
1170
|
// fixed-length, blank-padded
|
|
1211
1171
|
"VARCHAR",
|
|
@@ -1215,7 +1175,7 @@ var postgresColumnStringTypesSchema = import_zod3.z.enum([
|
|
|
1215
1175
|
"BYTEA"
|
|
1216
1176
|
// binary data
|
|
1217
1177
|
]);
|
|
1218
|
-
var postgresColumnDateTypesSchema =
|
|
1178
|
+
var postgresColumnDateTypesSchema = import_zod2.z.enum([
|
|
1219
1179
|
"DATE",
|
|
1220
1180
|
// calendar date (year, month, day)
|
|
1221
1181
|
"TIMESTAMP",
|
|
@@ -1227,13 +1187,13 @@ var postgresColumnDateTypesSchema = import_zod3.z.enum([
|
|
|
1227
1187
|
"INTERVAL"
|
|
1228
1188
|
// time span
|
|
1229
1189
|
]);
|
|
1230
|
-
var postgresColumnJsonTypesSchema =
|
|
1190
|
+
var postgresColumnJsonTypesSchema = import_zod2.z.enum([
|
|
1231
1191
|
"JSON",
|
|
1232
1192
|
// stores JSON data as raw text
|
|
1233
1193
|
"JSONB"
|
|
1234
1194
|
// stores JSON data in a binary format, optimized for query performance
|
|
1235
1195
|
]);
|
|
1236
|
-
var mariaDbColumnNumericTypesSchema =
|
|
1196
|
+
var mariaDbColumnNumericTypesSchema = import_zod2.z.enum([
|
|
1237
1197
|
"BOOLEAN",
|
|
1238
1198
|
// 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
|
|
1239
1199
|
"TINYINT",
|
|
@@ -1253,7 +1213,7 @@ var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
|
|
|
1253
1213
|
"DOUBLE"
|
|
1254
1214
|
// 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
1215
|
]);
|
|
1256
|
-
var mariaDbColumnStringTypesSchema =
|
|
1216
|
+
var mariaDbColumnStringTypesSchema = import_zod2.z.enum([
|
|
1257
1217
|
"CHAR",
|
|
1258
1218
|
// 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
1219
|
"VARCHAR",
|
|
@@ -1279,7 +1239,7 @@ var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
|
|
|
1279
1239
|
"ENUM"
|
|
1280
1240
|
// Enum type
|
|
1281
1241
|
]);
|
|
1282
|
-
var mariaDbColumnDateTypesSchema =
|
|
1242
|
+
var mariaDbColumnDateTypesSchema = import_zod2.z.enum([
|
|
1283
1243
|
"DATE",
|
|
1284
1244
|
// 4-bytes Date has year, month, and day.
|
|
1285
1245
|
"DATETIME",
|
|
@@ -1289,9 +1249,9 @@ var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
|
|
|
1289
1249
|
"TIMESTAMP"
|
|
1290
1250
|
// 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
|
|
1291
1251
|
]);
|
|
1292
|
-
var columnDataSchema =
|
|
1293
|
-
name:
|
|
1294
|
-
type:
|
|
1252
|
+
var columnDataSchema = import_zod2.z.object({
|
|
1253
|
+
name: import_zod2.z.string(),
|
|
1254
|
+
type: import_zod2.z.union([
|
|
1295
1255
|
postgresColumnNumericTypesSchema,
|
|
1296
1256
|
postgresColumnStringTypesSchema,
|
|
1297
1257
|
postgresColumnDateTypesSchema,
|
|
@@ -1300,24 +1260,25 @@ var columnDataSchema = import_zod3.z.object({
|
|
|
1300
1260
|
mariaDbColumnStringTypesSchema,
|
|
1301
1261
|
mariaDbColumnDateTypesSchema
|
|
1302
1262
|
]),
|
|
1303
|
-
isNullable:
|
|
1304
|
-
roles:
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1263
|
+
isNullable: import_zod2.z.boolean(),
|
|
1264
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1265
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1266
|
+
comment: import_zod2.z.string().optional(),
|
|
1267
|
+
default: import_zod2.z.string().optional(),
|
|
1268
|
+
value: import_zod2.z.string().optional(),
|
|
1269
|
+
isPrimary: import_zod2.z.boolean().optional(),
|
|
1270
|
+
isUnique: import_zod2.z.boolean().optional(),
|
|
1271
|
+
hasAutoIncrement: import_zod2.z.boolean().optional(),
|
|
1272
|
+
length: import_zod2.z.number().optional()
|
|
1312
1273
|
}).strict();
|
|
1313
|
-
var indexDataSchema =
|
|
1314
|
-
name:
|
|
1315
|
-
columns:
|
|
1316
|
-
isUnique:
|
|
1317
|
-
isPrimaryKey:
|
|
1318
|
-
order:
|
|
1274
|
+
var indexDataSchema = import_zod2.z.object({
|
|
1275
|
+
name: import_zod2.z.string(),
|
|
1276
|
+
columns: import_zod2.z.array(import_zod2.z.string()),
|
|
1277
|
+
isUnique: import_zod2.z.boolean(),
|
|
1278
|
+
isPrimaryKey: import_zod2.z.boolean(),
|
|
1279
|
+
order: import_zod2.z.enum(["ASC", "DESC"])
|
|
1319
1280
|
}).strict();
|
|
1320
|
-
var foreignKeyActionsSchema =
|
|
1281
|
+
var foreignKeyActionsSchema = import_zod2.z.enum([
|
|
1321
1282
|
"CASCADE",
|
|
1322
1283
|
// CASCADE action for foreign keys
|
|
1323
1284
|
"SET NULL",
|
|
@@ -1329,39 +1290,41 @@ var foreignKeyActionsSchema = import_zod3.z.enum([
|
|
|
1329
1290
|
"SET DEFAULT"
|
|
1330
1291
|
// SET DEFAULT action for foreign keys
|
|
1331
1292
|
]);
|
|
1332
|
-
var foreignKeyDataSchema =
|
|
1333
|
-
name:
|
|
1334
|
-
column:
|
|
1335
|
-
refTable:
|
|
1336
|
-
refColumn:
|
|
1293
|
+
var foreignKeyDataSchema = import_zod2.z.object({
|
|
1294
|
+
name: import_zod2.z.string(),
|
|
1295
|
+
column: import_zod2.z.string(),
|
|
1296
|
+
refTable: import_zod2.z.string(),
|
|
1297
|
+
refColumn: import_zod2.z.string(),
|
|
1337
1298
|
onDelete: foreignKeyActionsSchema,
|
|
1338
1299
|
onUpdate: foreignKeyActionsSchema
|
|
1339
1300
|
}).strict();
|
|
1340
|
-
var checkConstraintDataSchema =
|
|
1341
|
-
name:
|
|
1342
|
-
check:
|
|
1301
|
+
var checkConstraintDataSchema = import_zod2.z.object({
|
|
1302
|
+
name: import_zod2.z.string(),
|
|
1303
|
+
check: import_zod2.z.string()
|
|
1343
1304
|
}).strict();
|
|
1344
|
-
var tableDataSchema =
|
|
1345
|
-
name:
|
|
1346
|
-
columns:
|
|
1347
|
-
indexes:
|
|
1348
|
-
foreignKeys:
|
|
1349
|
-
checkConstraints:
|
|
1350
|
-
roles:
|
|
1351
|
-
|
|
1305
|
+
var tableDataSchema = import_zod2.z.object({
|
|
1306
|
+
name: import_zod2.z.string(),
|
|
1307
|
+
columns: import_zod2.z.array(columnDataSchema),
|
|
1308
|
+
indexes: import_zod2.z.array(indexDataSchema),
|
|
1309
|
+
foreignKeys: import_zod2.z.array(foreignKeyDataSchema),
|
|
1310
|
+
checkConstraints: import_zod2.z.array(checkConstraintDataSchema),
|
|
1311
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1312
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1313
|
+
notify: import_zod2.z.union([import_zod2.z.literal("ALL"), import_zod2.z.array(import_zod2.z.string())]).optional()
|
|
1352
1314
|
}).strict();
|
|
1353
|
-
var endpointDataSchema =
|
|
1354
|
-
name:
|
|
1355
|
-
description:
|
|
1356
|
-
baseUrl:
|
|
1357
|
-
routes:
|
|
1315
|
+
var endpointDataSchema = import_zod2.z.object({
|
|
1316
|
+
name: import_zod2.z.string(),
|
|
1317
|
+
description: import_zod2.z.string(),
|
|
1318
|
+
baseUrl: import_zod2.z.string(),
|
|
1319
|
+
routes: import_zod2.z.array(import_zod2.z.union([standardRouteSchema, customRouteSchema]))
|
|
1358
1320
|
}).strict();
|
|
1359
|
-
var resturaSchema =
|
|
1360
|
-
database:
|
|
1361
|
-
endpoints:
|
|
1362
|
-
globalParams:
|
|
1363
|
-
roles:
|
|
1364
|
-
|
|
1321
|
+
var resturaSchema = import_zod2.z.object({
|
|
1322
|
+
database: import_zod2.z.array(tableDataSchema),
|
|
1323
|
+
endpoints: import_zod2.z.array(endpointDataSchema),
|
|
1324
|
+
globalParams: import_zod2.z.array(import_zod2.z.string()),
|
|
1325
|
+
roles: import_zod2.z.array(import_zod2.z.string()),
|
|
1326
|
+
scopes: import_zod2.z.array(import_zod2.z.string()),
|
|
1327
|
+
customTypes: import_zod2.z.array(import_zod2.z.string())
|
|
1365
1328
|
}).strict();
|
|
1366
1329
|
async function isSchemaValid(schemaToCheck) {
|
|
1367
1330
|
try {
|
|
@@ -1376,7 +1339,7 @@ async function isSchemaValid(schemaToCheck) {
|
|
|
1376
1339
|
// src/restura/validators/requestValidator.ts
|
|
1377
1340
|
var import_core_utils3 = require("@redskytech/core-utils");
|
|
1378
1341
|
var import_jsonschema = __toESM(require("jsonschema"));
|
|
1379
|
-
var
|
|
1342
|
+
var import_zod3 = require("zod");
|
|
1380
1343
|
|
|
1381
1344
|
// src/restura/utils/utils.ts
|
|
1382
1345
|
function addQuotesToStrings(variable) {
|
|
@@ -1458,31 +1421,31 @@ function isValidType(type, requestValue) {
|
|
|
1458
1421
|
try {
|
|
1459
1422
|
expectValidType(type, requestValue);
|
|
1460
1423
|
return true;
|
|
1461
|
-
} catch
|
|
1424
|
+
} catch {
|
|
1462
1425
|
return false;
|
|
1463
1426
|
}
|
|
1464
1427
|
}
|
|
1465
1428
|
function expectValidType(type, requestValue) {
|
|
1466
1429
|
if (type === "number") {
|
|
1467
|
-
return
|
|
1430
|
+
return import_zod3.z.number().parse(requestValue);
|
|
1468
1431
|
}
|
|
1469
1432
|
if (type === "string") {
|
|
1470
|
-
return
|
|
1433
|
+
return import_zod3.z.string().parse(requestValue);
|
|
1471
1434
|
}
|
|
1472
1435
|
if (type === "boolean") {
|
|
1473
|
-
return
|
|
1436
|
+
return import_zod3.z.boolean().parse(requestValue);
|
|
1474
1437
|
}
|
|
1475
1438
|
if (type === "string[]") {
|
|
1476
|
-
return
|
|
1439
|
+
return import_zod3.z.array(import_zod3.z.string()).parse(requestValue);
|
|
1477
1440
|
}
|
|
1478
1441
|
if (type === "number[]") {
|
|
1479
|
-
return
|
|
1442
|
+
return import_zod3.z.array(import_zod3.z.number()).parse(requestValue);
|
|
1480
1443
|
}
|
|
1481
1444
|
if (type === "any[]") {
|
|
1482
|
-
return
|
|
1445
|
+
return import_zod3.z.array(import_zod3.z.any()).parse(requestValue);
|
|
1483
1446
|
}
|
|
1484
1447
|
if (type === "object") {
|
|
1485
|
-
return
|
|
1448
|
+
return import_zod3.z.object({}).strict().parse(requestValue);
|
|
1486
1449
|
}
|
|
1487
1450
|
}
|
|
1488
1451
|
function performTypeCheck(requestValue, validator, requestParamName) {
|
|
@@ -1494,7 +1457,7 @@ function performTypeCheck(requestValue, validator, requestParamName) {
|
|
|
1494
1457
|
}
|
|
1495
1458
|
try {
|
|
1496
1459
|
validatorDataSchemeValue.parse(validator.value);
|
|
1497
|
-
} catch
|
|
1460
|
+
} catch {
|
|
1498
1461
|
throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
|
|
1499
1462
|
}
|
|
1500
1463
|
}
|
|
@@ -1585,18 +1548,18 @@ async function schemaValidation(req, res, next) {
|
|
|
1585
1548
|
}
|
|
1586
1549
|
|
|
1587
1550
|
// src/restura/schemas/resturaConfigSchema.ts
|
|
1588
|
-
var
|
|
1589
|
-
var
|
|
1590
|
-
var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
|
|
1551
|
+
var import_v42 = require("zod/v4");
|
|
1552
|
+
var isTsx = process.argv[1]?.endsWith(".ts");
|
|
1591
1553
|
var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
|
|
1592
1554
|
var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
|
|
1593
|
-
var resturaConfigSchema =
|
|
1594
|
-
authToken:
|
|
1595
|
-
sendErrorStackTrace:
|
|
1596
|
-
schemaFilePath:
|
|
1597
|
-
customApiFolderPath:
|
|
1598
|
-
generatedTypesPath:
|
|
1599
|
-
fileTempCachePath:
|
|
1555
|
+
var resturaConfigSchema = import_v42.z.object({
|
|
1556
|
+
authToken: import_v42.z.string().min(1, "Missing Restura Auth Token"),
|
|
1557
|
+
sendErrorStackTrace: import_v42.z.boolean().default(false),
|
|
1558
|
+
schemaFilePath: import_v42.z.string().default(process.cwd() + "/restura.schema.json"),
|
|
1559
|
+
customApiFolderPath: import_v42.z.string().default(process.cwd() + customApiFolderPath),
|
|
1560
|
+
generatedTypesPath: import_v42.z.string().default(process.cwd() + "/src/@types"),
|
|
1561
|
+
fileTempCachePath: import_v42.z.string().optional(),
|
|
1562
|
+
scratchDatabaseSuffix: import_v42.z.string().optional()
|
|
1600
1563
|
});
|
|
1601
1564
|
|
|
1602
1565
|
// src/restura/sql/PsqlEngine.ts
|
|
@@ -1682,13 +1645,14 @@ function SQL(strings, ...values) {
|
|
|
1682
1645
|
|
|
1683
1646
|
// src/restura/sql/PsqlConnection.ts
|
|
1684
1647
|
var PsqlConnection = class {
|
|
1648
|
+
instanceId;
|
|
1685
1649
|
constructor(instanceId) {
|
|
1686
1650
|
this.instanceId = instanceId || import_crypto.default.randomUUID();
|
|
1687
1651
|
}
|
|
1688
1652
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1689
1653
|
async queryOne(query, options, requesterDetails) {
|
|
1690
1654
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1691
|
-
const meta =
|
|
1655
|
+
const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
|
|
1692
1656
|
this.logSqlStatement(formattedQuery, options, meta);
|
|
1693
1657
|
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1694
1658
|
`;
|
|
@@ -1701,7 +1665,7 @@ var PsqlConnection = class {
|
|
|
1701
1665
|
return response.rows[0];
|
|
1702
1666
|
} catch (error) {
|
|
1703
1667
|
if (RsError.isRsError(error)) throw error;
|
|
1704
|
-
if (
|
|
1668
|
+
if (error?.routine === "_bt_check_unique") {
|
|
1705
1669
|
throw new RsError("DUPLICATE", error.message);
|
|
1706
1670
|
}
|
|
1707
1671
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
@@ -1710,7 +1674,7 @@ var PsqlConnection = class {
|
|
|
1710
1674
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1711
1675
|
async runQuery(query, options, requesterDetails) {
|
|
1712
1676
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1713
|
-
const meta =
|
|
1677
|
+
const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
|
|
1714
1678
|
this.logSqlStatement(formattedQuery, options, meta);
|
|
1715
1679
|
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1716
1680
|
`;
|
|
@@ -1720,7 +1684,7 @@ var PsqlConnection = class {
|
|
|
1720
1684
|
this.logQueryDuration(startTime);
|
|
1721
1685
|
return response.rows;
|
|
1722
1686
|
} catch (error) {
|
|
1723
|
-
if (
|
|
1687
|
+
if (error?.routine === "_bt_check_unique") {
|
|
1724
1688
|
throw new RsError("DUPLICATE", error.message);
|
|
1725
1689
|
}
|
|
1726
1690
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
@@ -1770,13 +1734,20 @@ var PsqlPool = class extends PsqlConnection {
|
|
|
1770
1734
|
super();
|
|
1771
1735
|
this.poolConfig = poolConfig;
|
|
1772
1736
|
this.pool = new Pool(poolConfig);
|
|
1773
|
-
this.queryOne("SELECT NOW();", [], {
|
|
1737
|
+
this.queryOne("SELECT NOW();", [], {
|
|
1738
|
+
isSystemUser: true,
|
|
1739
|
+
role: "",
|
|
1740
|
+
host: "localhost",
|
|
1741
|
+
ipAddress: "",
|
|
1742
|
+
scopes: []
|
|
1743
|
+
}).then(() => {
|
|
1774
1744
|
logger.info("Connected to PostgreSQL database");
|
|
1775
1745
|
}).catch((error) => {
|
|
1776
1746
|
logger.error("Error connecting to database", error);
|
|
1777
1747
|
process.exit(1);
|
|
1778
1748
|
});
|
|
1779
1749
|
}
|
|
1750
|
+
pool;
|
|
1780
1751
|
async query(query, values) {
|
|
1781
1752
|
return this.pool.query(query, values);
|
|
1782
1753
|
}
|
|
@@ -1786,7 +1757,12 @@ var PsqlPool = class extends PsqlConnection {
|
|
|
1786
1757
|
var import_core_utils4 = require("@redskytech/core-utils");
|
|
1787
1758
|
var SqlEngine = class {
|
|
1788
1759
|
async runQueryForRoute(req, routeData, schema) {
|
|
1789
|
-
if (!this.
|
|
1760
|
+
if (!this.canRequesterAccessTable(
|
|
1761
|
+
req.requesterDetails.role,
|
|
1762
|
+
req.requesterDetails.scopes,
|
|
1763
|
+
schema,
|
|
1764
|
+
routeData.table
|
|
1765
|
+
))
|
|
1790
1766
|
throw new RsError("FORBIDDEN", "You do not have permission to access this table");
|
|
1791
1767
|
switch (routeData.method) {
|
|
1792
1768
|
case "POST":
|
|
@@ -1805,7 +1781,7 @@ var SqlEngine = class {
|
|
|
1805
1781
|
if (!tableSchema) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
|
|
1806
1782
|
return tableSchema;
|
|
1807
1783
|
}
|
|
1808
|
-
|
|
1784
|
+
canRequesterAccessColumn(requesterRole, requesterScopes, schema, item, joins) {
|
|
1809
1785
|
if (item.type) return true;
|
|
1810
1786
|
if (item.selector) {
|
|
1811
1787
|
let tableName = item.selector.split(".")[0];
|
|
@@ -1821,26 +1797,36 @@ var SqlEngine = class {
|
|
|
1821
1797
|
const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
|
|
1822
1798
|
if (!columnSchema)
|
|
1823
1799
|
throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1800
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.roles)) {
|
|
1801
|
+
if (!requesterRole) return false;
|
|
1802
|
+
return columnSchema.roles.includes(requesterRole);
|
|
1803
|
+
}
|
|
1804
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.scopes)) {
|
|
1805
|
+
if (!requesterScopes) return false;
|
|
1806
|
+
return columnSchema.scopes.some((scope) => requesterScopes.includes(scope));
|
|
1807
|
+
}
|
|
1808
|
+
return true;
|
|
1828
1809
|
}
|
|
1829
1810
|
if (item.subquery) {
|
|
1830
1811
|
return import_core_utils4.ObjectUtils.isArrayWithData(
|
|
1831
1812
|
item.subquery.properties.filter((nestedItem) => {
|
|
1832
|
-
return this.
|
|
1813
|
+
return this.canRequesterAccessColumn(requesterRole, requesterScopes, schema, nestedItem, joins);
|
|
1833
1814
|
})
|
|
1834
1815
|
);
|
|
1835
1816
|
}
|
|
1836
1817
|
return false;
|
|
1837
1818
|
}
|
|
1838
|
-
|
|
1819
|
+
canRequesterAccessTable(requesterRole, requesterScopes, schema, tableName) {
|
|
1839
1820
|
const tableSchema = this.getTableSchema(schema, tableName);
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1821
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.roles)) {
|
|
1822
|
+
if (!requesterRole) return false;
|
|
1823
|
+
return tableSchema.roles.includes(requesterRole);
|
|
1824
|
+
}
|
|
1825
|
+
if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.scopes)) {
|
|
1826
|
+
if (!requesterScopes) return false;
|
|
1827
|
+
return tableSchema.scopes.some((scope) => requesterScopes.includes(scope));
|
|
1828
|
+
}
|
|
1829
|
+
return true;
|
|
1844
1830
|
}
|
|
1845
1831
|
replaceParamKeywords(value, routeData, req, sqlParams) {
|
|
1846
1832
|
let returnValue = value;
|
|
@@ -1849,11 +1835,10 @@ var SqlEngine = class {
|
|
|
1849
1835
|
return returnValue;
|
|
1850
1836
|
}
|
|
1851
1837
|
replaceLocalParamKeywords(value, routeData, req, sqlParams) {
|
|
1852
|
-
var _a2;
|
|
1853
1838
|
if (!routeData.request) return value;
|
|
1854
1839
|
const data = req.data;
|
|
1855
1840
|
if (typeof value === "string") {
|
|
1856
|
-
|
|
1841
|
+
value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
|
|
1857
1842
|
const requestParam = routeData.request.find((item) => {
|
|
1858
1843
|
return item.name === param.replace("$", "");
|
|
1859
1844
|
});
|
|
@@ -1866,9 +1851,8 @@ var SqlEngine = class {
|
|
|
1866
1851
|
return value;
|
|
1867
1852
|
}
|
|
1868
1853
|
replaceGlobalParamKeywords(value, routeData, req, sqlParams) {
|
|
1869
|
-
var _a2;
|
|
1870
1854
|
if (typeof value === "string") {
|
|
1871
|
-
|
|
1855
|
+
value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
|
|
1872
1856
|
param = param.replace("#", "");
|
|
1873
1857
|
const globalParamValue = req.requesterDetails[param];
|
|
1874
1858
|
if (!globalParamValue)
|
|
@@ -1958,10 +1942,32 @@ negate = "!"
|
|
|
1958
1942
|
operator = "and"i / "or"i
|
|
1959
1943
|
|
|
1960
1944
|
|
|
1961
|
-
column =
|
|
1962
|
-
|
|
1963
|
-
|
|
1945
|
+
column = first:text rest:("." text)* {
|
|
1946
|
+
const partsArray = [first];
|
|
1947
|
+
if (rest && rest.length > 0) {
|
|
1948
|
+
partsArray.push(...rest.map(item => item[1]));
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
if (partsArray.length > 3) {
|
|
1952
|
+
throw new SyntaxError('Column path cannot have more than 3 parts (table.column.jsonField)');
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
if (partsArray.length === 1) {
|
|
1956
|
+
return quoteSqlIdentity(partsArray[0]);
|
|
1957
|
+
}
|
|
1958
|
+
const tableName = quoteSqlIdentity(partsArray[0]);
|
|
1959
|
+
|
|
1960
|
+
// If we only have two parts (table.column), use regular dot notation
|
|
1961
|
+
if (partsArray.length === 2) {
|
|
1962
|
+
return tableName + "." + quoteSqlIdentity(partsArray[1]);
|
|
1963
|
+
}
|
|
1964
1964
|
|
|
1965
|
+
// For JSON paths (more than 2 parts), first part is a column, last part uses ->>
|
|
1966
|
+
const jsonColumn = quoteSqlIdentity(partsArray[1]);
|
|
1967
|
+
const lastPart = partsArray[partsArray.length - 1];
|
|
1968
|
+
const result = tableName + "." + jsonColumn + "->>'" + lastPart + "'";
|
|
1969
|
+
return result;
|
|
1970
|
+
}
|
|
1965
1971
|
|
|
1966
1972
|
text = text:[a-z0-9 \\t\\r\\n\\-_:@']i+ { return text.join(""); }
|
|
1967
1973
|
|
|
@@ -1991,19 +1997,24 @@ var filterPsqlParser_default = filterPsqlParser;
|
|
|
1991
1997
|
var { Client, types } = import_pg2.default;
|
|
1992
1998
|
var systemUser = {
|
|
1993
1999
|
role: "",
|
|
2000
|
+
scopes: [],
|
|
1994
2001
|
host: "",
|
|
1995
2002
|
ipAddress: "",
|
|
1996
2003
|
isSystemUser: true
|
|
1997
2004
|
};
|
|
1998
2005
|
var PsqlEngine = class extends SqlEngine {
|
|
1999
|
-
constructor(psqlConnectionPool, shouldListenForDbTriggers = false) {
|
|
2006
|
+
constructor(psqlConnectionPool, shouldListenForDbTriggers = false, scratchDatabaseSuffix = "") {
|
|
2000
2007
|
super();
|
|
2001
2008
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
2002
2009
|
this.setupPgReturnTypes();
|
|
2003
2010
|
if (shouldListenForDbTriggers) {
|
|
2004
2011
|
this.setupTriggerListeners = this.listenForDbTriggers();
|
|
2005
2012
|
}
|
|
2013
|
+
this.scratchDbName = `${psqlConnectionPool.poolConfig.database}_scratch${scratchDatabaseSuffix ? `_${scratchDatabaseSuffix}` : ""}`;
|
|
2006
2014
|
}
|
|
2015
|
+
setupTriggerListeners;
|
|
2016
|
+
triggerClient;
|
|
2017
|
+
scratchDbName = "";
|
|
2007
2018
|
async close() {
|
|
2008
2019
|
if (this.triggerClient) {
|
|
2009
2020
|
await this.triggerClient.end();
|
|
@@ -2132,27 +2143,22 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2132
2143
|
sqlStatements.push(triggers.join("\n"));
|
|
2133
2144
|
return sqlStatements.join("\n\n");
|
|
2134
2145
|
}
|
|
2135
|
-
async
|
|
2136
|
-
var _a2, _b;
|
|
2146
|
+
async getNewPublicSchemaAndScratchPool() {
|
|
2137
2147
|
const scratchDbExists = await this.psqlConnectionPool.runQuery(
|
|
2138
2148
|
`SELECT *
|
|
2139
2149
|
FROM pg_database
|
|
2140
|
-
WHERE datname = '${this.
|
|
2150
|
+
WHERE datname = '${this.scratchDbName}';`,
|
|
2141
2151
|
[],
|
|
2142
2152
|
systemUser
|
|
2143
2153
|
);
|
|
2144
2154
|
if (scratchDbExists.length === 0) {
|
|
2145
|
-
await this.psqlConnectionPool.runQuery(
|
|
2146
|
-
`CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
|
|
2147
|
-
[],
|
|
2148
|
-
systemUser
|
|
2149
|
-
);
|
|
2155
|
+
await this.psqlConnectionPool.runQuery(`CREATE DATABASE ${this.scratchDbName};`, [], systemUser);
|
|
2150
2156
|
}
|
|
2151
2157
|
const scratchPool = new PsqlPool({
|
|
2152
2158
|
host: this.psqlConnectionPool.poolConfig.host,
|
|
2153
2159
|
port: this.psqlConnectionPool.poolConfig.port,
|
|
2154
2160
|
user: this.psqlConnectionPool.poolConfig.user,
|
|
2155
|
-
database: this.
|
|
2161
|
+
database: this.scratchDbName,
|
|
2156
2162
|
password: this.psqlConnectionPool.poolConfig.password,
|
|
2157
2163
|
max: this.psqlConnectionPool.poolConfig.max,
|
|
2158
2164
|
idleTimeoutMillis: this.psqlConnectionPool.poolConfig.idleTimeoutMillis,
|
|
@@ -2165,16 +2171,17 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2165
2171
|
systemUser
|
|
2166
2172
|
);
|
|
2167
2173
|
const schemaComment = await this.psqlConnectionPool.runQuery(
|
|
2168
|
-
`
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2174
|
+
`
|
|
2175
|
+
SELECT pg_description.description
|
|
2176
|
+
FROM pg_description
|
|
2177
|
+
JOIN pg_namespace ON pg_namespace.oid = pg_description.objoid
|
|
2178
|
+
WHERE pg_namespace.nspname = 'public';`,
|
|
2172
2179
|
[],
|
|
2173
2180
|
systemUser
|
|
2174
2181
|
);
|
|
2175
|
-
if (
|
|
2182
|
+
if (schemaComment[0]?.description) {
|
|
2176
2183
|
await scratchPool.runQuery(
|
|
2177
|
-
`COMMENT ON SCHEMA public IS '${
|
|
2184
|
+
`COMMENT ON SCHEMA public IS '${schemaComment[0]?.description}';`,
|
|
2178
2185
|
[],
|
|
2179
2186
|
systemUser
|
|
2180
2187
|
);
|
|
@@ -2182,7 +2189,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2182
2189
|
return scratchPool;
|
|
2183
2190
|
}
|
|
2184
2191
|
async diffDatabaseToSchema(schema) {
|
|
2185
|
-
const scratchPool = await this.
|
|
2192
|
+
const scratchPool = await this.getNewPublicSchemaAndScratchPool();
|
|
2186
2193
|
await this.createDatabaseFromSchema(schema, scratchPool);
|
|
2187
2194
|
const originalClient = new Client({
|
|
2188
2195
|
database: this.psqlConnectionPool.poolConfig.database,
|
|
@@ -2192,7 +2199,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2192
2199
|
port: this.psqlConnectionPool.poolConfig.port
|
|
2193
2200
|
});
|
|
2194
2201
|
const scratchClient = new Client({
|
|
2195
|
-
database: this.
|
|
2202
|
+
database: this.scratchDbName,
|
|
2196
2203
|
user: this.psqlConnectionPool.poolConfig.user,
|
|
2197
2204
|
password: this.psqlConnectionPool.poolConfig.password,
|
|
2198
2205
|
host: this.psqlConnectionPool.poolConfig.host,
|
|
@@ -2207,24 +2214,30 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2207
2214
|
await Promise.all(endPromises);
|
|
2208
2215
|
return diff.join("\n");
|
|
2209
2216
|
}
|
|
2210
|
-
createNestedSelect(req, schema, item, routeData,
|
|
2217
|
+
createNestedSelect(req, schema, item, routeData, sqlParams) {
|
|
2211
2218
|
if (!item.subquery) return "";
|
|
2212
2219
|
if (!import_core_utils5.ObjectUtils.isArrayWithData(
|
|
2213
2220
|
item.subquery.properties.filter((nestedItem) => {
|
|
2214
|
-
return this.
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2221
|
+
return this.canRequesterAccessColumn(
|
|
2222
|
+
req.requesterDetails.role,
|
|
2223
|
+
req.requesterDetails.scopes,
|
|
2224
|
+
schema,
|
|
2225
|
+
nestedItem,
|
|
2226
|
+
[...routeData.joins, ...item.subquery.joins]
|
|
2227
|
+
);
|
|
2218
2228
|
})
|
|
2219
2229
|
)) {
|
|
2220
2230
|
return "'[]'";
|
|
2221
2231
|
}
|
|
2222
2232
|
return `COALESCE((SELECT JSON_AGG(JSON_BUILD_OBJECT(
|
|
2223
2233
|
${item.subquery.properties.map((nestedItem) => {
|
|
2224
|
-
if (!this.
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2234
|
+
if (!this.canRequesterAccessColumn(
|
|
2235
|
+
req.requesterDetails.role,
|
|
2236
|
+
req.requesterDetails.scopes,
|
|
2237
|
+
schema,
|
|
2238
|
+
nestedItem,
|
|
2239
|
+
[...routeData.joins, ...item.subquery.joins]
|
|
2240
|
+
)) {
|
|
2228
2241
|
return;
|
|
2229
2242
|
}
|
|
2230
2243
|
if (nestedItem.subquery) {
|
|
@@ -2234,7 +2247,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2234
2247
|
schema,
|
|
2235
2248
|
nestedItem,
|
|
2236
2249
|
routeData,
|
|
2237
|
-
userRole,
|
|
2238
2250
|
sqlParams
|
|
2239
2251
|
)}`;
|
|
2240
2252
|
}
|
|
@@ -2243,7 +2255,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2243
2255
|
))
|
|
2244
2256
|
FROM
|
|
2245
2257
|
"${item.subquery.table}"
|
|
2246
|
-
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema,
|
|
2258
|
+
${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, sqlParams)}
|
|
2247
2259
|
${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
|
|
2248
2260
|
), '[]')`;
|
|
2249
2261
|
}
|
|
@@ -2253,7 +2265,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2253
2265
|
(routeData.assignments || []).forEach((assignment) => {
|
|
2254
2266
|
parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
|
|
2255
2267
|
});
|
|
2256
|
-
const query = insertObjectQuery(routeData.table,
|
|
2268
|
+
const query = insertObjectQuery(routeData.table, { ...req.data, ...parameterObj });
|
|
2257
2269
|
const createdItem = await this.psqlConnectionPool.queryOne(
|
|
2258
2270
|
query,
|
|
2259
2271
|
sqlParams,
|
|
@@ -2268,24 +2280,29 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2268
2280
|
};
|
|
2269
2281
|
const whereData = [whereId];
|
|
2270
2282
|
req.data = { id: insertId };
|
|
2271
|
-
return this.executeGetRequest(req,
|
|
2283
|
+
return this.executeGetRequest(req, { ...routeData, where: whereData }, schema);
|
|
2272
2284
|
}
|
|
2273
2285
|
async executeGetRequest(req, routeData, schema) {
|
|
2274
2286
|
const DEFAULT_PAGED_PAGE_NUMBER = 0;
|
|
2275
2287
|
const DEFAULT_PAGED_PER_PAGE_NUMBER = 25;
|
|
2276
2288
|
const sqlParams = [];
|
|
2277
|
-
const userRole = req.requesterDetails.role;
|
|
2278
2289
|
let sqlStatement = "";
|
|
2279
2290
|
const selectColumns = [];
|
|
2280
2291
|
routeData.response.forEach((item) => {
|
|
2281
|
-
if (item.subquery || this.
|
|
2292
|
+
if (item.subquery || this.canRequesterAccessColumn(
|
|
2293
|
+
req.requesterDetails.role,
|
|
2294
|
+
req.requesterDetails.scopes,
|
|
2295
|
+
schema,
|
|
2296
|
+
item,
|
|
2297
|
+
routeData.joins
|
|
2298
|
+
))
|
|
2282
2299
|
selectColumns.push(item);
|
|
2283
2300
|
});
|
|
2284
2301
|
if (!selectColumns.length) throw new RsError("FORBIDDEN", `You do not have permission to access this data.`);
|
|
2285
2302
|
let selectStatement = "SELECT \n";
|
|
2286
2303
|
selectStatement += ` ${selectColumns.map((item) => {
|
|
2287
2304
|
if (item.subquery) {
|
|
2288
|
-
return `${this.createNestedSelect(req, schema, item, routeData,
|
|
2305
|
+
return `${this.createNestedSelect(req, schema, item, routeData, sqlParams)} AS ${escapeColumnName(
|
|
2289
2306
|
item.name
|
|
2290
2307
|
)}`;
|
|
2291
2308
|
}
|
|
@@ -2300,7 +2317,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2300
2317
|
routeData.table,
|
|
2301
2318
|
routeData,
|
|
2302
2319
|
schema,
|
|
2303
|
-
userRole,
|
|
2304
2320
|
sqlParams
|
|
2305
2321
|
);
|
|
2306
2322
|
sqlStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
@@ -2344,7 +2360,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2344
2360
|
}
|
|
2345
2361
|
async executeUpdateRequest(req, routeData, schema) {
|
|
2346
2362
|
const sqlParams = [];
|
|
2347
|
-
const
|
|
2363
|
+
const { id, ...bodyNoId } = req.body;
|
|
2348
2364
|
const table = schema.database.find((item) => {
|
|
2349
2365
|
return item.name === routeData.table;
|
|
2350
2366
|
});
|
|
@@ -2373,7 +2389,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2373
2389
|
routeData.table,
|
|
2374
2390
|
routeData,
|
|
2375
2391
|
schema,
|
|
2376
|
-
req.requesterDetails.role,
|
|
2377
2392
|
sqlParams
|
|
2378
2393
|
);
|
|
2379
2394
|
const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
@@ -2385,10 +2400,15 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
|
2385
2400
|
await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
|
|
2386
2401
|
return true;
|
|
2387
2402
|
}
|
|
2388
|
-
generateJoinStatements(req, joins, baseTable, routeData, schema,
|
|
2403
|
+
generateJoinStatements(req, joins, baseTable, routeData, schema, sqlParams) {
|
|
2389
2404
|
let joinStatements = "";
|
|
2390
2405
|
joins.forEach((item) => {
|
|
2391
|
-
if (!this.
|
|
2406
|
+
if (!this.canRequesterAccessTable(
|
|
2407
|
+
req.requesterDetails.role,
|
|
2408
|
+
req.requesterDetails.scopes,
|
|
2409
|
+
schema,
|
|
2410
|
+
item.table
|
|
2411
|
+
))
|
|
2392
2412
|
throw new RsError("FORBIDDEN", "You do not have permission to access this table");
|
|
2393
2413
|
if (item.custom) {
|
|
2394
2414
|
const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
|
|
@@ -2443,41 +2463,36 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
|
|
|
2443
2463
|
`Invalid where clause in route ${routeData.name}, missing required fields if not custom`
|
|
2444
2464
|
);
|
|
2445
2465
|
let operator = item.operator;
|
|
2446
|
-
let value = item.value;
|
|
2447
2466
|
if (operator === "LIKE") {
|
|
2448
|
-
value = `'%${value}%'`;
|
|
2449
|
-
} else if (operator === "NOT LIKE") {
|
|
2450
|
-
value = `'%${value}%'`;
|
|
2467
|
+
item.value = `'%${item.value}%'`;
|
|
2451
2468
|
} else if (operator === "STARTS WITH") {
|
|
2452
2469
|
operator = "LIKE";
|
|
2453
|
-
value = `'${value}%'`;
|
|
2470
|
+
item.value = `'${item.value}%'`;
|
|
2454
2471
|
} else if (operator === "ENDS WITH") {
|
|
2455
2472
|
operator = "LIKE";
|
|
2456
|
-
value = `'%${value}'`;
|
|
2473
|
+
item.value = `'%${item.value}'`;
|
|
2457
2474
|
}
|
|
2458
|
-
const replacedValue = this.replaceParamKeywords(value, routeData, req, sqlParams);
|
|
2475
|
+
const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
|
|
2459
2476
|
whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator.replace("LIKE", "ILIKE")} ${["IN", "NOT IN"].includes(operator) ? `(${replacedValue})` : replacedValue}
|
|
2460
2477
|
`;
|
|
2461
2478
|
});
|
|
2462
2479
|
const data = req.data;
|
|
2463
|
-
if (routeData.type === "PAGED" && !!
|
|
2480
|
+
if (routeData.type === "PAGED" && !!data?.filter) {
|
|
2464
2481
|
let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
2465
|
-
var _a2;
|
|
2466
2482
|
const requestParam = routeData.request.find((item) => {
|
|
2467
2483
|
return item.name === value.replace("$", "");
|
|
2468
2484
|
});
|
|
2469
2485
|
if (!requestParam)
|
|
2470
2486
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
2471
|
-
return
|
|
2487
|
+
return data[requestParam.name]?.toString() || "";
|
|
2472
2488
|
});
|
|
2473
2489
|
statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
|
|
2474
|
-
var _a2;
|
|
2475
2490
|
const requestParam = routeData.request.find((item) => {
|
|
2476
2491
|
return item.name === value.replace("#", "");
|
|
2477
2492
|
});
|
|
2478
2493
|
if (!requestParam)
|
|
2479
2494
|
throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
|
|
2480
|
-
return
|
|
2495
|
+
return data[requestParam.name]?.toString() || "";
|
|
2481
2496
|
});
|
|
2482
2497
|
statement = filterPsqlParser_default.parse(statement);
|
|
2483
2498
|
if (whereClause.startsWith("WHERE")) {
|
|
@@ -2718,8 +2733,9 @@ var import_internal3 = require("@restura/internal");
|
|
|
2718
2733
|
var import_bluebird3 = __toESM(require("bluebird"));
|
|
2719
2734
|
var os2 = __toESM(require("os"));
|
|
2720
2735
|
var TempCache = class {
|
|
2736
|
+
location;
|
|
2737
|
+
maxDurationDays = 7;
|
|
2721
2738
|
constructor(location) {
|
|
2722
|
-
this.maxDurationDays = 7;
|
|
2723
2739
|
this.location = location || os2.tmpdir();
|
|
2724
2740
|
import_internal3.FileUtils.ensureDir(this.location).catch((e) => {
|
|
2725
2741
|
throw e;
|
|
@@ -2744,15 +2760,24 @@ var TempCache = class {
|
|
|
2744
2760
|
|
|
2745
2761
|
// src/restura/restura.ts
|
|
2746
2762
|
var ResturaEngine = class {
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2763
|
+
// Make public so other modules can access without re-parsing the config
|
|
2764
|
+
resturaConfig;
|
|
2765
|
+
multerCommonUpload;
|
|
2766
|
+
resturaRouter;
|
|
2767
|
+
publicEndpoints = {
|
|
2768
|
+
GET: [],
|
|
2769
|
+
POST: [],
|
|
2770
|
+
PUT: [],
|
|
2771
|
+
PATCH: [],
|
|
2772
|
+
DELETE: []
|
|
2773
|
+
};
|
|
2774
|
+
expressApp;
|
|
2775
|
+
schema;
|
|
2776
|
+
responseValidator;
|
|
2777
|
+
authenticationHandler;
|
|
2778
|
+
customTypeValidation;
|
|
2779
|
+
psqlConnectionPool;
|
|
2780
|
+
psqlEngine;
|
|
2756
2781
|
/**
|
|
2757
2782
|
* Initializes the Restura engine with the provided Express application.
|
|
2758
2783
|
*
|
|
@@ -2760,11 +2785,11 @@ var ResturaEngine = class {
|
|
|
2760
2785
|
* @returns A promise that resolves when the initialization is complete.
|
|
2761
2786
|
*/
|
|
2762
2787
|
async init(app, authenticationHandler, psqlConnectionPool) {
|
|
2763
|
-
this.resturaConfig = import_internal4.config.validate("restura", resturaConfigSchema);
|
|
2788
|
+
this.resturaConfig = await import_internal4.config.validate("restura", resturaConfigSchema);
|
|
2764
2789
|
this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
|
|
2765
2790
|
new TempCache(this.resturaConfig.fileTempCachePath);
|
|
2766
2791
|
this.psqlConnectionPool = psqlConnectionPool;
|
|
2767
|
-
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
|
|
2792
|
+
this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true, this.resturaConfig.scratchDatabaseSuffix);
|
|
2768
2793
|
await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
|
|
2769
2794
|
this.authenticationHandler = authenticationHandler;
|
|
2770
2795
|
app.use((0, import_compression.default)());
|
|
@@ -2773,7 +2798,7 @@ var ResturaEngine = class {
|
|
|
2773
2798
|
app.use((0, import_cookie_parser.default)());
|
|
2774
2799
|
app.disable("x-powered-by");
|
|
2775
2800
|
app.use("/", addApiResponseFunctions);
|
|
2776
|
-
app.use("/api/",
|
|
2801
|
+
app.use("/api/", authenticateRequester(this.authenticationHandler));
|
|
2777
2802
|
app.use("/restura", this.resturaAuthentication);
|
|
2778
2803
|
app.put(
|
|
2779
2804
|
"/restura/v1/schema",
|
|
@@ -2794,7 +2819,7 @@ var ResturaEngine = class {
|
|
|
2794
2819
|
}
|
|
2795
2820
|
/**
|
|
2796
2821
|
* 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.
|
|
2822
|
+
* is determined on whether the endpoint in the schema has no roles or scopes assigned to it.
|
|
2798
2823
|
*
|
|
2799
2824
|
* @param method - The HTTP method (e.g., 'GET', 'POST', 'PUT', 'PATCH', 'DELETE').
|
|
2800
2825
|
* @param fullUrl - The full URL of the endpoint.
|
|
@@ -2883,7 +2908,8 @@ var ResturaEngine = class {
|
|
|
2883
2908
|
route.path = route.path.startsWith("/") ? route.path : `/${route.path}`;
|
|
2884
2909
|
route.path = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
|
|
2885
2910
|
const fullUrl = `${baseUrl}${route.path}`;
|
|
2886
|
-
if (route.roles.length === 0
|
|
2911
|
+
if (route.roles.length === 0 && route.scopes.length === 0)
|
|
2912
|
+
this.publicEndpoints[route.method].push(fullUrl);
|
|
2887
2913
|
this.resturaRouter[route.method.toLowerCase()](
|
|
2888
2914
|
route.path,
|
|
2889
2915
|
// <-- Notice we only use path here since the baseUrl is already added to the router.
|
|
@@ -2933,10 +2959,10 @@ var ResturaEngine = class {
|
|
|
2933
2959
|
);
|
|
2934
2960
|
this.generateResturaGlobalTypes(import_path5.default.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
|
|
2935
2961
|
}
|
|
2936
|
-
async getSchema(
|
|
2962
|
+
async getSchema(_req, res) {
|
|
2937
2963
|
res.send({ data: this.schema });
|
|
2938
2964
|
}
|
|
2939
|
-
async getSchemaAndTypes(
|
|
2965
|
+
async getSchemaAndTypes(_req, res) {
|
|
2940
2966
|
try {
|
|
2941
2967
|
const schema = await this.getLatestFileSystemSchema();
|
|
2942
2968
|
const apiText = await apiGenerator(schema);
|
|
@@ -2947,8 +2973,7 @@ var ResturaEngine = class {
|
|
|
2947
2973
|
}
|
|
2948
2974
|
}
|
|
2949
2975
|
async getMulterFilesIfAny(req, res, routeData) {
|
|
2950
|
-
|
|
2951
|
-
if (!((_a2 = req.header("content-type")) == null ? void 0 : _a2.includes("multipart/form-data"))) return;
|
|
2976
|
+
if (!req.header("content-type")?.includes("multipart/form-data")) return;
|
|
2952
2977
|
if (!this.isCustomRoute(routeData)) return;
|
|
2953
2978
|
if (!routeData.fileUploadType) {
|
|
2954
2979
|
throw new RsError("BAD_REQUEST", "File upload type not defined for route");
|
|
@@ -3011,16 +3036,17 @@ var ResturaEngine = class {
|
|
|
3011
3036
|
await customFunction(req, res, routeData);
|
|
3012
3037
|
}
|
|
3013
3038
|
async storeFileSystemSchema() {
|
|
3014
|
-
const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema),
|
|
3015
|
-
parser: "json"
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3039
|
+
const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), {
|
|
3040
|
+
parser: "json",
|
|
3041
|
+
...{
|
|
3042
|
+
trailingComma: "none",
|
|
3043
|
+
tabWidth: 4,
|
|
3044
|
+
useTabs: true,
|
|
3045
|
+
endOfLine: "lf",
|
|
3046
|
+
printWidth: 120,
|
|
3047
|
+
singleQuote: true
|
|
3048
|
+
}
|
|
3049
|
+
});
|
|
3024
3050
|
import_fs4.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
|
|
3025
3051
|
}
|
|
3026
3052
|
resetPublicEndpoints() {
|
|
@@ -3033,9 +3059,13 @@ var ResturaEngine = class {
|
|
|
3033
3059
|
};
|
|
3034
3060
|
}
|
|
3035
3061
|
validateAuthorization(req, routeData) {
|
|
3036
|
-
const
|
|
3037
|
-
|
|
3038
|
-
if (
|
|
3062
|
+
const requesterRole = req.requesterDetails.role;
|
|
3063
|
+
const requesterScopes = req.requesterDetails.scopes;
|
|
3064
|
+
if (routeData.roles.length === 0 && routeData.scopes.length === 0) return;
|
|
3065
|
+
if (requesterRole && routeData.roles.length > 0 && !routeData.roles.includes(requesterRole))
|
|
3066
|
+
throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
|
|
3067
|
+
if (requesterScopes.length > 0 && routeData.scopes.length > 0 && !routeData.scopes.some((scope) => requesterScopes.includes(scope)))
|
|
3068
|
+
throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
|
|
3039
3069
|
}
|
|
3040
3070
|
getRouteData(method, baseUrl, path5) {
|
|
3041
3071
|
const endpoint = this.schema.endpoints.find((item) => {
|
|
@@ -3089,6 +3119,9 @@ var PsqlTransaction = class extends PsqlConnection {
|
|
|
3089
3119
|
this.connectPromise = this.client.connect();
|
|
3090
3120
|
this.beginTransactionPromise = this.beginTransaction();
|
|
3091
3121
|
}
|
|
3122
|
+
client;
|
|
3123
|
+
beginTransactionPromise;
|
|
3124
|
+
connectPromise;
|
|
3092
3125
|
async close() {
|
|
3093
3126
|
if (this.client) {
|
|
3094
3127
|
await this.client.end();
|