@ruiapp/rapid-core 0.8.21 → 0.9.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/CHANGELOG.md +11 -11
- package/dist/core/request.d.ts +1 -0
- package/dist/core/server.d.ts +2 -1
- package/dist/index.js +740 -740
- package/dist/server.d.ts +2 -1
- package/package.json +1 -1
- package/rollup.config.js +16 -16
- package/src/bootstrapApplicationConfig.ts +782 -782
- package/src/core/actionHandler.ts +23 -23
- package/src/core/eventManager.ts +20 -20
- package/src/core/facility.ts +7 -7
- package/src/core/http/formDataParser.ts +89 -89
- package/src/core/http-types.ts +4 -4
- package/src/core/pluginManager.ts +184 -184
- package/src/core/providers/runtimeProvider.ts +5 -5
- package/src/core/request.ts +96 -95
- package/src/core/response.ts +79 -79
- package/src/core/routeContext.ts +127 -127
- package/src/core/routesBuilder.ts +90 -90
- package/src/core/server.ts +152 -151
- package/src/dataAccess/columnTypeMapper.ts +22 -22
- package/src/dataAccess/dataAccessTypes.ts +165 -165
- package/src/dataAccess/dataAccessor.ts +135 -135
- package/src/dataAccess/entityManager.ts +1932 -1932
- package/src/dataAccess/entityMapper.ts +111 -111
- package/src/dataAccess/entityValidator.ts +33 -33
- package/src/dataAccess/propertyMapper.ts +28 -28
- package/src/deno-std/assert/assert.ts +9 -9
- package/src/deno-std/assert/assertion_error.ts +7 -7
- package/src/deno-std/datetime/to_imf.ts +32 -32
- package/src/deno-std/encoding/base64.ts +141 -141
- package/src/deno-std/http/cookie.ts +372 -372
- package/src/facilities/cache/CacheFacilityTypes.ts +29 -29
- package/src/facilities/cache/CacheFactory.ts +31 -31
- package/src/facilities/cache/MemoryCache.ts +58 -58
- package/src/facilities/cache/MemoryCacheProvider.ts +15 -15
- package/src/facilities/log/LogFacility.ts +35 -35
- package/src/helpers/entityHelpers.ts +76 -76
- package/src/helpers/filterHelper.ts +148 -148
- package/src/helpers/inputHelper.ts +11 -11
- package/src/helpers/licenseHelper.ts +29 -29
- package/src/helpers/metaHelper.ts +111 -111
- package/src/helpers/runCollectionEntityActionHandler.ts +58 -58
- package/src/index.ts +76 -76
- package/src/plugins/auth/AuthPlugin.ts +93 -93
- package/src/plugins/auth/actionHandlers/changePassword.ts +61 -61
- package/src/plugins/auth/actionHandlers/createSession.ts +67 -67
- package/src/plugins/auth/actionHandlers/deleteSession.ts +18 -18
- package/src/plugins/auth/actionHandlers/getMyProfile.ts +35 -35
- package/src/plugins/auth/actionHandlers/index.ts +8 -8
- package/src/plugins/auth/actionHandlers/resetPassword.ts +45 -45
- package/src/plugins/auth/models/AccessToken.ts +56 -56
- package/src/plugins/auth/models/index.ts +3 -3
- package/src/plugins/auth/routes/changePassword.ts +15 -15
- package/src/plugins/auth/routes/getMyProfile.ts +15 -15
- package/src/plugins/auth/routes/index.ts +7 -7
- package/src/plugins/auth/routes/resetPassword.ts +15 -15
- package/src/plugins/auth/routes/signin.ts +15 -15
- package/src/plugins/auth/routes/signout.ts +15 -15
- package/src/plugins/auth/services/AuthService.ts +39 -39
- package/src/plugins/cronJob/CronJobPlugin.ts +104 -104
- package/src/plugins/cronJob/CronJobPluginTypes.ts +44 -44
- package/src/plugins/cronJob/actionHandlers/index.ts +4 -4
- package/src/plugins/cronJob/actionHandlers/runCronJob.ts +32 -32
- package/src/plugins/cronJob/entityWatchers/cronJobEntityWatchers.ts +24 -24
- package/src/plugins/cronJob/entityWatchers/index.ts +4 -4
- package/src/plugins/cronJob/models/CronJob.ts +129 -129
- package/src/plugins/cronJob/models/index.ts +3 -3
- package/src/plugins/cronJob/routes/index.ts +3 -3
- package/src/plugins/cronJob/routes/runCronJob.ts +15 -15
- package/src/plugins/cronJob/services/CronJobService.ts +252 -252
- package/src/plugins/dataManage/DataManagePlugin.ts +163 -163
- package/src/plugins/dataManage/actionHandlers/addEntityRelations.ts +15 -15
- package/src/plugins/dataManage/actionHandlers/countCollectionEntities.ts +17 -17
- package/src/plugins/dataManage/actionHandlers/createCollectionEntitiesBatch.ts +81 -81
- package/src/plugins/dataManage/actionHandlers/createCollectionEntity.ts +20 -20
- package/src/plugins/dataManage/actionHandlers/deleteCollectionEntities.ts +45 -45
- package/src/plugins/dataManage/actionHandlers/deleteCollectionEntityById.ts +20 -20
- package/src/plugins/dataManage/actionHandlers/findCollectionEntities.ts +27 -27
- package/src/plugins/dataManage/actionHandlers/findCollectionEntityById.ts +30 -30
- package/src/plugins/dataManage/actionHandlers/queryDatabase.ts +22 -22
- package/src/plugins/dataManage/actionHandlers/removeEntityRelations.ts +15 -15
- package/src/plugins/dataManage/actionHandlers/updateCollectionEntityById.ts +38 -38
- package/src/plugins/entityAccessControl/EntityAccessControlPlugin.ts +146 -146
- package/src/plugins/fileManage/FileManagePlugin.ts +52 -52
- package/src/plugins/fileManage/actionHandlers/downloadDocument.ts +65 -65
- package/src/plugins/fileManage/actionHandlers/downloadFile.ts +44 -44
- package/src/plugins/fileManage/actionHandlers/uploadFile.ts +33 -33
- package/src/plugins/fileManage/routes/downloadDocument.ts +15 -15
- package/src/plugins/fileManage/routes/downloadFile.ts +15 -15
- package/src/plugins/fileManage/routes/index.ts +5 -5
- package/src/plugins/fileManage/routes/uploadFile.ts +15 -15
- package/src/plugins/license/LicensePlugin.ts +79 -79
- package/src/plugins/license/LicensePluginTypes.ts +95 -95
- package/src/plugins/license/LicenseService.ts +118 -118
- package/src/plugins/license/actionHandlers/getLicense.ts +18 -18
- package/src/plugins/license/actionHandlers/index.ts +4 -4
- package/src/plugins/license/helpers/certHelper.ts +21 -21
- package/src/plugins/license/helpers/cryptoHelper.ts +47 -47
- package/src/plugins/license/models/index.ts +1 -1
- package/src/plugins/license/routes/getLicense.ts +15 -15
- package/src/plugins/license/routes/index.ts +3 -3
- package/src/plugins/mail/MailPlugin.ts +74 -74
- package/src/plugins/mail/MailPluginTypes.ts +27 -27
- package/src/plugins/mail/MailService.ts +38 -38
- package/src/plugins/mail/actionHandlers/index.ts +3 -3
- package/src/plugins/mail/models/index.ts +1 -1
- package/src/plugins/mail/routes/index.ts +1 -1
- package/src/plugins/metaManage/MetaManagePlugin.ts +198 -198
- package/src/plugins/metaManage/actionHandlers/getMetaModelDetail.ts +10 -10
- package/src/plugins/metaManage/actionHandlers/listMetaModels.ts +9 -9
- package/src/plugins/metaManage/actionHandlers/listMetaRoutes.ts +9 -9
- package/src/plugins/metaManage/services/MetaService.ts +376 -376
- package/src/plugins/notification/NotificationPlugin.ts +68 -68
- package/src/plugins/notification/NotificationPluginTypes.ts +13 -13
- package/src/plugins/notification/NotificationService.ts +25 -25
- package/src/plugins/notification/actionHandlers/index.ts +3 -3
- package/src/plugins/notification/models/Notification.ts +60 -60
- package/src/plugins/notification/models/index.ts +3 -3
- package/src/plugins/notification/routes/index.ts +1 -1
- package/src/plugins/routeManage/RouteManagePlugin.ts +62 -62
- package/src/plugins/routeManage/actionHandlers/httpProxy.ts +13 -13
- package/src/plugins/sequence/SequencePlugin.ts +146 -146
- package/src/plugins/sequence/SequencePluginTypes.ts +69 -69
- package/src/plugins/sequence/SequenceService.ts +92 -92
- package/src/plugins/sequence/actionHandlers/generateSn.ts +32 -32
- package/src/plugins/sequence/actionHandlers/index.ts +4 -4
- package/src/plugins/sequence/models/SequenceAutoIncrementRecord.ts +49 -49
- package/src/plugins/sequence/models/SequenceRule.ts +42 -42
- package/src/plugins/sequence/models/index.ts +4 -4
- package/src/plugins/sequence/routes/generateSn.ts +15 -15
- package/src/plugins/sequence/routes/index.ts +3 -3
- package/src/plugins/sequence/segment-utility.ts +11 -11
- package/src/plugins/sequence/segments/autoIncrement.ts +90 -90
- package/src/plugins/sequence/segments/dayOfMonth.ts +19 -19
- package/src/plugins/sequence/segments/index.ts +9 -9
- package/src/plugins/sequence/segments/literal.ts +16 -16
- package/src/plugins/sequence/segments/month.ts +19 -19
- package/src/plugins/sequence/segments/parameter.ts +20 -20
- package/src/plugins/sequence/segments/year.ts +19 -19
- package/src/plugins/serverOperation/ServerOperationPlugin.ts +91 -91
- package/src/plugins/serverOperation/ServerOperationPluginTypes.ts +15 -15
- package/src/plugins/serverOperation/actionHandlers/index.ts +4 -4
- package/src/plugins/serverOperation/actionHandlers/runServerOperation.ts +15 -15
- package/src/plugins/setting/SettingPlugin.ts +68 -68
- package/src/plugins/setting/SettingPluginTypes.ts +37 -37
- package/src/plugins/setting/SettingService.ts +213 -213
- package/src/plugins/setting/actionHandlers/getSystemSettingValues.ts +30 -30
- package/src/plugins/setting/actionHandlers/getUserSettingValues.ts +38 -38
- package/src/plugins/setting/actionHandlers/index.ts +6 -6
- package/src/plugins/setting/actionHandlers/setSystemSettingValues.ts +30 -30
- package/src/plugins/setting/models/SystemSettingGroupSetting.ts +57 -57
- package/src/plugins/setting/models/SystemSettingItem.ts +48 -48
- package/src/plugins/setting/models/SystemSettingItemSetting.ts +73 -73
- package/src/plugins/setting/models/UserSettingGroupSetting.ts +57 -57
- package/src/plugins/setting/models/UserSettingItem.ts +55 -55
- package/src/plugins/setting/models/UserSettingItemSetting.ts +73 -73
- package/src/plugins/setting/models/index.ts +8 -8
- package/src/plugins/setting/routes/getSystemSettingValues.ts +15 -15
- package/src/plugins/setting/routes/getUserSettingValues.ts +15 -15
- package/src/plugins/setting/routes/index.ts +5 -5
- package/src/plugins/setting/routes/setSystemSettingValues.ts +15 -15
- package/src/plugins/stateMachine/StateMachinePlugin.ts +196 -196
- package/src/plugins/stateMachine/StateMachinePluginTypes.ts +48 -48
- package/src/plugins/stateMachine/actionHandlers/index.ts +4 -4
- package/src/plugins/stateMachine/actionHandlers/sendStateMachineEvent.ts +54 -54
- package/src/plugins/stateMachine/models/StateMachine.ts +42 -42
- package/src/plugins/stateMachine/models/index.ts +3 -3
- package/src/plugins/stateMachine/routes/index.ts +3 -3
- package/src/plugins/stateMachine/routes/sendStateMachineEvent.ts +15 -15
- package/src/plugins/stateMachine/stateMachineHelper.ts +36 -36
- package/src/plugins/webhooks/WebhooksPlugin.ts +148 -148
- package/src/plugins/webhooks/pluginConfig.ts +75 -75
- package/src/polyfill.ts +5 -5
- package/src/proxy/mod.ts +38 -38
- package/src/proxy/types.ts +21 -21
- package/src/queryBuilder/index.ts +1 -1
- package/src/queryBuilder/queryBuilder.ts +755 -755
- package/src/server.ts +523 -524
- package/src/types/cron-job-types.ts +66 -66
- package/src/types.ts +832 -832
- package/src/utilities/accessControlUtility.ts +33 -33
- package/src/utilities/entityUtility.ts +18 -18
- package/src/utilities/errorUtility.ts +15 -15
- package/src/utilities/fsUtility.ts +61 -61
- package/src/utilities/httpUtility.ts +19 -19
- package/src/utilities/jwtUtility.ts +26 -26
- package/src/utilities/pathUtility.ts +14 -14
- package/src/utilities/timeUtility.ts +17 -17
- package/src/utilities/typeUtility.ts +15 -15
- package/tsconfig.json +19 -19
package/dist/index.js
CHANGED
|
@@ -1203,485 +1203,6 @@ class RouteContext {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
}
|
|
1205
1205
|
|
|
1206
|
-
const parseFormDataBody = async (request, options = { all: false }) => {
|
|
1207
|
-
const contentType = request.headers.get("Content-Type");
|
|
1208
|
-
if (isFormDataContent(contentType)) {
|
|
1209
|
-
return parseFormData(request, options);
|
|
1210
|
-
}
|
|
1211
|
-
return {};
|
|
1212
|
-
};
|
|
1213
|
-
function isFormDataContent(contentType) {
|
|
1214
|
-
if (contentType === null) {
|
|
1215
|
-
return false;
|
|
1216
|
-
}
|
|
1217
|
-
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
1218
|
-
}
|
|
1219
|
-
async function parseFormData(request, options) {
|
|
1220
|
-
const formData = await request.formData();
|
|
1221
|
-
if (formData) {
|
|
1222
|
-
return convertFormDataToBodyData(formData, options);
|
|
1223
|
-
}
|
|
1224
|
-
return {};
|
|
1225
|
-
}
|
|
1226
|
-
function convertFormDataToBodyData(formData, options) {
|
|
1227
|
-
const form = {};
|
|
1228
|
-
formData.forEach((value, key) => {
|
|
1229
|
-
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
1230
|
-
if (!shouldParseAllValues) {
|
|
1231
|
-
form[key] = value;
|
|
1232
|
-
}
|
|
1233
|
-
else {
|
|
1234
|
-
handleParsingAllValues(form, key, value);
|
|
1235
|
-
}
|
|
1236
|
-
});
|
|
1237
|
-
return form;
|
|
1238
|
-
}
|
|
1239
|
-
const handleParsingAllValues = (form, key, value) => {
|
|
1240
|
-
if (form[key] && isArrayField(form[key])) {
|
|
1241
|
-
appendToExistingArray(form[key], value);
|
|
1242
|
-
}
|
|
1243
|
-
else if (form[key]) {
|
|
1244
|
-
convertToNewArray(form, key, value);
|
|
1245
|
-
}
|
|
1246
|
-
else {
|
|
1247
|
-
form[key] = value;
|
|
1248
|
-
}
|
|
1249
|
-
};
|
|
1250
|
-
function isArrayField(field) {
|
|
1251
|
-
return Array.isArray(field);
|
|
1252
|
-
}
|
|
1253
|
-
const appendToExistingArray = (arr, value) => {
|
|
1254
|
-
arr.push(value);
|
|
1255
|
-
};
|
|
1256
|
-
const convertToNewArray = (form, key, value) => {
|
|
1257
|
-
form[key] = [form[key], value];
|
|
1258
|
-
};
|
|
1259
|
-
|
|
1260
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
1261
|
-
class AssertionError extends Error {
|
|
1262
|
-
name = "AssertionError";
|
|
1263
|
-
constructor(message) {
|
|
1264
|
-
super(message);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
1269
|
-
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
|
|
1270
|
-
function assert(expr, msg = "") {
|
|
1271
|
-
if (!expr) {
|
|
1272
|
-
throw new AssertionError(msg);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
1277
|
-
// This module is browser compatible.
|
|
1278
|
-
/**
|
|
1279
|
-
* Formats the given date to IMF date time format. (Reference:
|
|
1280
|
-
* https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
|
|
1281
|
-
* IMF is the time format to use when generating times in HTTP
|
|
1282
|
-
* headers. The time being formatted must be in UTC for Format to
|
|
1283
|
-
* generate the correct format.
|
|
1284
|
-
*
|
|
1285
|
-
* @example
|
|
1286
|
-
* ```ts
|
|
1287
|
-
* import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
|
|
1288
|
-
*
|
|
1289
|
-
* toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
|
|
1290
|
-
* ```
|
|
1291
|
-
* @param date Date to parse
|
|
1292
|
-
* @return IMF date formatted string
|
|
1293
|
-
*/
|
|
1294
|
-
function toIMF(date) {
|
|
1295
|
-
function dtPad(v, lPad = 2) {
|
|
1296
|
-
return v.padStart(lPad, "0");
|
|
1297
|
-
}
|
|
1298
|
-
const d = dtPad(date.getUTCDate().toString());
|
|
1299
|
-
const h = dtPad(date.getUTCHours().toString());
|
|
1300
|
-
const min = dtPad(date.getUTCMinutes().toString());
|
|
1301
|
-
const s = dtPad(date.getUTCSeconds().toString());
|
|
1302
|
-
const y = date.getUTCFullYear();
|
|
1303
|
-
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
1304
|
-
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
1305
|
-
return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
1309
|
-
const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
|
|
1310
|
-
function toString(cookie) {
|
|
1311
|
-
if (!cookie.name) {
|
|
1312
|
-
return "";
|
|
1313
|
-
}
|
|
1314
|
-
const out = [];
|
|
1315
|
-
validateName(cookie.name);
|
|
1316
|
-
validateValue(cookie.name, cookie.value);
|
|
1317
|
-
out.push(`${cookie.name}=${cookie.value}`);
|
|
1318
|
-
// Fallback for invalid Set-Cookie
|
|
1319
|
-
// ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
|
|
1320
|
-
if (cookie.name.startsWith("__Secure")) {
|
|
1321
|
-
cookie.secure = true;
|
|
1322
|
-
}
|
|
1323
|
-
if (cookie.name.startsWith("__Host")) {
|
|
1324
|
-
cookie.path = "/";
|
|
1325
|
-
cookie.secure = true;
|
|
1326
|
-
delete cookie.domain;
|
|
1327
|
-
}
|
|
1328
|
-
if (cookie.secure) {
|
|
1329
|
-
out.push("Secure");
|
|
1330
|
-
}
|
|
1331
|
-
if (cookie.httpOnly) {
|
|
1332
|
-
out.push("HttpOnly");
|
|
1333
|
-
}
|
|
1334
|
-
if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
|
|
1335
|
-
assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
|
|
1336
|
-
out.push(`Max-Age=${cookie.maxAge}`);
|
|
1337
|
-
}
|
|
1338
|
-
if (cookie.domain) {
|
|
1339
|
-
validateDomain(cookie.domain);
|
|
1340
|
-
out.push(`Domain=${cookie.domain}`);
|
|
1341
|
-
}
|
|
1342
|
-
if (cookie.sameSite) {
|
|
1343
|
-
out.push(`SameSite=${cookie.sameSite}`);
|
|
1344
|
-
}
|
|
1345
|
-
if (cookie.path) {
|
|
1346
|
-
validatePath(cookie.path);
|
|
1347
|
-
out.push(`Path=${cookie.path}`);
|
|
1348
|
-
}
|
|
1349
|
-
if (cookie.expires) {
|
|
1350
|
-
const { expires } = cookie;
|
|
1351
|
-
const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
|
|
1352
|
-
out.push(`Expires=${dateString}`);
|
|
1353
|
-
}
|
|
1354
|
-
if (cookie.unparsed) {
|
|
1355
|
-
out.push(cookie.unparsed.join("; "));
|
|
1356
|
-
}
|
|
1357
|
-
return out.join("; ");
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Validate Cookie Name.
|
|
1361
|
-
* @param name Cookie name.
|
|
1362
|
-
*/
|
|
1363
|
-
function validateName(name) {
|
|
1364
|
-
if (name && !FIELD_CONTENT_REGEXP.test(name)) {
|
|
1365
|
-
throw new TypeError(`Invalid cookie name: "${name}".`);
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
/**
|
|
1369
|
-
* Validate Path Value.
|
|
1370
|
-
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
|
|
1371
|
-
* @param path Path value.
|
|
1372
|
-
*/
|
|
1373
|
-
function validatePath(path) {
|
|
1374
|
-
if (path == null) {
|
|
1375
|
-
return;
|
|
1376
|
-
}
|
|
1377
|
-
for (let i = 0; i < path.length; i++) {
|
|
1378
|
-
const c = path.charAt(i);
|
|
1379
|
-
if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7e) || c == ";") {
|
|
1380
|
-
throw new Error(path + ": Invalid cookie path char '" + c + "'");
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* Validate Cookie Value.
|
|
1386
|
-
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
|
|
1387
|
-
* @param value Cookie value.
|
|
1388
|
-
*/
|
|
1389
|
-
function validateValue(name, value) {
|
|
1390
|
-
if (value == null || name == null)
|
|
1391
|
-
return;
|
|
1392
|
-
for (let i = 0; i < value.length; i++) {
|
|
1393
|
-
const c = value.charAt(i);
|
|
1394
|
-
if (c < String.fromCharCode(0x21) ||
|
|
1395
|
-
c == String.fromCharCode(0x22) ||
|
|
1396
|
-
c == String.fromCharCode(0x2c) ||
|
|
1397
|
-
c == String.fromCharCode(0x3b) ||
|
|
1398
|
-
c == String.fromCharCode(0x5c) ||
|
|
1399
|
-
c == String.fromCharCode(0x7f)) {
|
|
1400
|
-
throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
|
|
1401
|
-
}
|
|
1402
|
-
if (c > String.fromCharCode(0x80)) {
|
|
1403
|
-
throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + c.charCodeAt(0).toString(16));
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
/**
|
|
1408
|
-
* Validate Cookie Domain.
|
|
1409
|
-
* See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
|
|
1410
|
-
* @param domain Cookie domain.
|
|
1411
|
-
*/
|
|
1412
|
-
function validateDomain(domain) {
|
|
1413
|
-
if (domain == null) {
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
const char1 = domain.charAt(0);
|
|
1417
|
-
const charN = domain.charAt(domain.length - 1);
|
|
1418
|
-
if (char1 == "-" || charN == "." || charN == "-") {
|
|
1419
|
-
throw new Error("Invalid first/last char in cookie domain: " + domain);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
/**
|
|
1423
|
-
* Parse cookies of a header
|
|
1424
|
-
*
|
|
1425
|
-
* @example
|
|
1426
|
-
* ```ts
|
|
1427
|
-
* import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
1428
|
-
*
|
|
1429
|
-
* const headers = new Headers();
|
|
1430
|
-
* headers.set("Cookie", "full=of; tasty=chocolate");
|
|
1431
|
-
*
|
|
1432
|
-
* const cookies = getCookies(headers);
|
|
1433
|
-
* console.log(cookies); // { full: "of", tasty: "chocolate" }
|
|
1434
|
-
* ```
|
|
1435
|
-
*
|
|
1436
|
-
* @param headers The headers instance to get cookies from
|
|
1437
|
-
* @return Object with cookie names as keys
|
|
1438
|
-
*/
|
|
1439
|
-
function getCookies(headers) {
|
|
1440
|
-
const cookie = headers.get("Cookie");
|
|
1441
|
-
if (cookie != null) {
|
|
1442
|
-
const out = {};
|
|
1443
|
-
const c = cookie.split(";");
|
|
1444
|
-
for (const kv of c) {
|
|
1445
|
-
const [cookieKey, ...cookieVal] = kv.split("=");
|
|
1446
|
-
assert(cookieKey != null);
|
|
1447
|
-
const key = cookieKey.trim();
|
|
1448
|
-
out[key] = cookieVal.join("=");
|
|
1449
|
-
}
|
|
1450
|
-
return out;
|
|
1451
|
-
}
|
|
1452
|
-
return {};
|
|
1453
|
-
}
|
|
1454
|
-
/**
|
|
1455
|
-
* Set the cookie header properly in the headers
|
|
1456
|
-
*
|
|
1457
|
-
* @example
|
|
1458
|
-
* ```ts
|
|
1459
|
-
* import {
|
|
1460
|
-
* Cookie,
|
|
1461
|
-
* setCookie,
|
|
1462
|
-
* } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
1463
|
-
*
|
|
1464
|
-
* const headers = new Headers();
|
|
1465
|
-
* const cookie: Cookie = { name: "Space", value: "Cat" };
|
|
1466
|
-
* setCookie(headers, cookie);
|
|
1467
|
-
*
|
|
1468
|
-
* const cookieHeader = headers.get("set-cookie");
|
|
1469
|
-
* console.log(cookieHeader); // Space=Cat
|
|
1470
|
-
* ```
|
|
1471
|
-
*
|
|
1472
|
-
* @param headers The headers instance to set the cookie to
|
|
1473
|
-
* @param cookie Cookie to set
|
|
1474
|
-
*/
|
|
1475
|
-
function setCookie(headers, cookie) {
|
|
1476
|
-
// Parsing cookie headers to make consistent set-cookie header
|
|
1477
|
-
// ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
|
|
1478
|
-
const v = toString(cookie);
|
|
1479
|
-
if (v) {
|
|
1480
|
-
headers.append("Set-Cookie", v);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Set the cookie header with empty value in the headers to delete it
|
|
1485
|
-
*
|
|
1486
|
-
* > Note: Deleting a `Cookie` will set its expiration date before now. Forcing
|
|
1487
|
-
* > the browser to delete it.
|
|
1488
|
-
*
|
|
1489
|
-
* @example
|
|
1490
|
-
* ```ts
|
|
1491
|
-
* import { deleteCookie } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
1492
|
-
*
|
|
1493
|
-
* const headers = new Headers();
|
|
1494
|
-
* deleteCookie(headers, "deno");
|
|
1495
|
-
*
|
|
1496
|
-
* const cookieHeader = headers.get("set-cookie");
|
|
1497
|
-
* console.log(cookieHeader); // deno=; Expires=Thus, 01 Jan 1970 00:00:00 GMT
|
|
1498
|
-
* ```
|
|
1499
|
-
*
|
|
1500
|
-
* @param headers The headers instance to delete the cookie from
|
|
1501
|
-
* @param name Name of cookie
|
|
1502
|
-
* @param attributes Additional cookie attributes
|
|
1503
|
-
*/
|
|
1504
|
-
function deleteCookie(headers, name, attributes) {
|
|
1505
|
-
setCookie(headers, {
|
|
1506
|
-
name: name,
|
|
1507
|
-
value: "",
|
|
1508
|
-
expires: new Date(0),
|
|
1509
|
-
...attributes,
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
function parseSetCookie(value) {
|
|
1513
|
-
const attrs = value.split(";").map((attr) => {
|
|
1514
|
-
const [key, ...values] = attr.trim().split("=");
|
|
1515
|
-
return [key, values.join("=")];
|
|
1516
|
-
});
|
|
1517
|
-
const cookie = {
|
|
1518
|
-
name: attrs[0][0],
|
|
1519
|
-
value: attrs[0][1],
|
|
1520
|
-
};
|
|
1521
|
-
for (const [key, value] of attrs.slice(1)) {
|
|
1522
|
-
switch (key.toLocaleLowerCase()) {
|
|
1523
|
-
case "expires":
|
|
1524
|
-
cookie.expires = new Date(value);
|
|
1525
|
-
break;
|
|
1526
|
-
case "max-age":
|
|
1527
|
-
cookie.maxAge = Number(value);
|
|
1528
|
-
if (cookie.maxAge < 0) {
|
|
1529
|
-
console.warn("Max-Age must be an integer superior or equal to 0. Cookie ignored.");
|
|
1530
|
-
return null;
|
|
1531
|
-
}
|
|
1532
|
-
break;
|
|
1533
|
-
case "domain":
|
|
1534
|
-
cookie.domain = value;
|
|
1535
|
-
break;
|
|
1536
|
-
case "path":
|
|
1537
|
-
cookie.path = value;
|
|
1538
|
-
break;
|
|
1539
|
-
case "secure":
|
|
1540
|
-
cookie.secure = true;
|
|
1541
|
-
break;
|
|
1542
|
-
case "httponly":
|
|
1543
|
-
cookie.httpOnly = true;
|
|
1544
|
-
break;
|
|
1545
|
-
case "samesite":
|
|
1546
|
-
cookie.sameSite = value;
|
|
1547
|
-
break;
|
|
1548
|
-
default:
|
|
1549
|
-
if (!Array.isArray(cookie.unparsed)) {
|
|
1550
|
-
cookie.unparsed = [];
|
|
1551
|
-
}
|
|
1552
|
-
cookie.unparsed.push([key, value].join("="));
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
if (cookie.name.startsWith("__Secure-")) {
|
|
1556
|
-
/** This requirement is mentioned in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie but not the RFC. */
|
|
1557
|
-
if (!cookie.secure) {
|
|
1558
|
-
console.warn("Cookies with names starting with `__Secure-` must be set with the secure flag. Cookie ignored.");
|
|
1559
|
-
return null;
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
if (cookie.name.startsWith("__Host-")) {
|
|
1563
|
-
if (!cookie.secure) {
|
|
1564
|
-
console.warn("Cookies with names starting with `__Host-` must be set with the secure flag. Cookie ignored.");
|
|
1565
|
-
return null;
|
|
1566
|
-
}
|
|
1567
|
-
if (cookie.domain !== undefined) {
|
|
1568
|
-
console.warn("Cookies with names starting with `__Host-` must not have a domain specified. Cookie ignored.");
|
|
1569
|
-
return null;
|
|
1570
|
-
}
|
|
1571
|
-
if (cookie.path !== "/") {
|
|
1572
|
-
console.warn("Cookies with names starting with `__Host-` must have path be `/`. Cookie has been ignored.");
|
|
1573
|
-
return null;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
return cookie;
|
|
1577
|
-
}
|
|
1578
|
-
/**
|
|
1579
|
-
* Parse set-cookies of a header
|
|
1580
|
-
*
|
|
1581
|
-
* @example
|
|
1582
|
-
* ```ts
|
|
1583
|
-
* import { getSetCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
1584
|
-
*
|
|
1585
|
-
* const headers = new Headers([
|
|
1586
|
-
* ["Set-Cookie", "lulu=meow; Secure; Max-Age=3600"],
|
|
1587
|
-
* ["Set-Cookie", "booya=kasha; HttpOnly; Path=/"],
|
|
1588
|
-
* ]);
|
|
1589
|
-
*
|
|
1590
|
-
* const cookies = getSetCookies(headers);
|
|
1591
|
-
* console.log(cookies); // [{ name: "lulu", value: "meow", secure: true, maxAge: 3600 }, { name: "booya", value: "kahsa", httpOnly: true, path: "/ }]
|
|
1592
|
-
* ```
|
|
1593
|
-
*
|
|
1594
|
-
* @param headers The headers instance to get set-cookies from
|
|
1595
|
-
* @return List of cookies
|
|
1596
|
-
*/
|
|
1597
|
-
function getSetCookies(headers) {
|
|
1598
|
-
// TODO(lino-levan): remove this ts-ignore when Typescript 5.2 lands in Deno
|
|
1599
|
-
// @ts-ignore Typescript's TS Dom types will be out of date until 5.2
|
|
1600
|
-
return headers
|
|
1601
|
-
.getSetCookie()
|
|
1602
|
-
/** Parse each `set-cookie` header separately */
|
|
1603
|
-
.map(parseSetCookie)
|
|
1604
|
-
/** Skip empty cookies */
|
|
1605
|
-
.filter(Boolean);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
const GlobalRequest = global.Request;
|
|
1609
|
-
class RapidRequest {
|
|
1610
|
-
#logger;
|
|
1611
|
-
#raw;
|
|
1612
|
-
#bodyParsed;
|
|
1613
|
-
#body;
|
|
1614
|
-
#headers;
|
|
1615
|
-
#parsedCookies;
|
|
1616
|
-
method;
|
|
1617
|
-
url;
|
|
1618
|
-
constructor(server, req) {
|
|
1619
|
-
this.#logger = server.getLogger();
|
|
1620
|
-
this.#raw = req;
|
|
1621
|
-
this.method = req.method;
|
|
1622
|
-
this.url = new URL(req.url);
|
|
1623
|
-
this.#headers = req.headers;
|
|
1624
|
-
}
|
|
1625
|
-
async parseBody() {
|
|
1626
|
-
if (this.#bodyParsed) {
|
|
1627
|
-
this.#logger.warn("Request body has been parsed. 'parseBody()' method should not be called more than once.");
|
|
1628
|
-
return;
|
|
1629
|
-
}
|
|
1630
|
-
const requestMethod = this.method;
|
|
1631
|
-
if (requestMethod !== "POST" && requestMethod !== "PUT" && requestMethod !== "PATCH") {
|
|
1632
|
-
this.#body = null;
|
|
1633
|
-
this.#bodyParsed = true;
|
|
1634
|
-
return;
|
|
1635
|
-
}
|
|
1636
|
-
const contentLength = parseInt(this.#headers.get("Content-Length") || "0", 10);
|
|
1637
|
-
if (!contentLength) {
|
|
1638
|
-
this.#body = null;
|
|
1639
|
-
this.#bodyParsed = true;
|
|
1640
|
-
return;
|
|
1641
|
-
}
|
|
1642
|
-
const req = this.#raw;
|
|
1643
|
-
const contentType = this.#headers.get("Content-Type") || "application/json";
|
|
1644
|
-
if (contentType.includes("json")) {
|
|
1645
|
-
this.#body = {
|
|
1646
|
-
type: "json",
|
|
1647
|
-
value: await req.json(),
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
else if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
1651
|
-
const bodyText = await req.text();
|
|
1652
|
-
this.#body = {
|
|
1653
|
-
type: "form",
|
|
1654
|
-
value: qs__default["default"].parse(bodyText),
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
else if (contentType.startsWith("multipart/form-data")) {
|
|
1658
|
-
this.#body = {
|
|
1659
|
-
type: "form-data",
|
|
1660
|
-
value: await parseFormDataBody(req),
|
|
1661
|
-
};
|
|
1662
|
-
}
|
|
1663
|
-
this.#bodyParsed = true;
|
|
1664
|
-
}
|
|
1665
|
-
get rawRequest() {
|
|
1666
|
-
return this.#raw;
|
|
1667
|
-
}
|
|
1668
|
-
get headers() {
|
|
1669
|
-
return this.#headers;
|
|
1670
|
-
}
|
|
1671
|
-
get cookies() {
|
|
1672
|
-
if (!this.#parsedCookies) {
|
|
1673
|
-
this.#parsedCookies = getCookies(this.#headers);
|
|
1674
|
-
}
|
|
1675
|
-
return this.#parsedCookies;
|
|
1676
|
-
}
|
|
1677
|
-
get body() {
|
|
1678
|
-
if (!this.#bodyParsed) {
|
|
1679
|
-
throw new Error("Request body not parsed, you should call 'parseBody()' method before getting the body.");
|
|
1680
|
-
}
|
|
1681
|
-
return this.#body;
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
1206
|
var bootstrapApplicationConfig = {
|
|
1686
1207
|
code: "default",
|
|
1687
1208
|
name: "default",
|
|
@@ -3294,8 +2815,8 @@ async function findManyRelationLinksViaLinkTable(options) {
|
|
|
3294
2815
|
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
3295
2816
|
schema: relationProperty.linkSchema,
|
|
3296
2817
|
tableName: relationProperty.linkTableName,
|
|
3297
|
-
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = ANY($1::int[])
|
|
3298
|
-
ORDER BY id
|
|
2818
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = ANY($1::int[])
|
|
2819
|
+
ORDER BY id
|
|
3299
2820
|
`;
|
|
3300
2821
|
const params = [mainEntityIds];
|
|
3301
2822
|
const relationLinks = await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
@@ -3926,7 +3447,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3926
3447
|
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
3927
3448
|
schema: property.linkSchema,
|
|
3928
3449
|
tableName: property.linkTableName,
|
|
3929
|
-
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1
|
|
3450
|
+
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1
|
|
3930
3451
|
AND ${server.queryBuilder.quoteObject(property.targetIdColumnName)} <> ALL($2::int[])`, [id, targetIdsToKeep], routeContext?.getDbTransactionClient());
|
|
3931
3452
|
}
|
|
3932
3453
|
else {
|
|
@@ -3955,8 +3476,8 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3955
3476
|
await server.queryDatabaseObject(`UPDATE ${server.queryBuilder.quoteTable({
|
|
3956
3477
|
schema: relationModel.schema,
|
|
3957
3478
|
tableName: relationModel.tableName,
|
|
3958
|
-
})}
|
|
3959
|
-
SET ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = null
|
|
3479
|
+
})}
|
|
3480
|
+
SET ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = null
|
|
3960
3481
|
WHERE id = ANY($1::int[])`, [targetIdsToRemove], routeContext?.getDbTransactionClient());
|
|
3961
3482
|
}
|
|
3962
3483
|
else {
|
|
@@ -4241,7 +3762,7 @@ async function deleteEntityById(server, dataAccessor, options, plugin) {
|
|
|
4241
3762
|
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
4242
3763
|
schema: relationProperty.linkSchema,
|
|
4243
3764
|
tableName: relationProperty.linkTableName,
|
|
4244
|
-
})}
|
|
3765
|
+
})}
|
|
4245
3766
|
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
4246
3767
|
}
|
|
4247
3768
|
else {
|
|
@@ -4251,8 +3772,8 @@ async function deleteEntityById(server, dataAccessor, options, plugin) {
|
|
|
4251
3772
|
await server.queryDatabaseObject(`UPDATE ${server.queryBuilder.quoteTable({
|
|
4252
3773
|
schema: relationModel.schema,
|
|
4253
3774
|
tableName: relationModel.tableName,
|
|
4254
|
-
})}
|
|
4255
|
-
SET ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = null
|
|
3775
|
+
})}
|
|
3776
|
+
SET ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = null
|
|
4256
3777
|
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
4257
3778
|
}
|
|
4258
3779
|
}
|
|
@@ -4349,11 +3870,11 @@ class EntityManager {
|
|
|
4349
3870
|
const command = `INSERT INTO ${queryBuilder.quoteTable({
|
|
4350
3871
|
schema: relationProperty.linkSchema,
|
|
4351
3872
|
tableName: relationProperty.linkTableName,
|
|
4352
|
-
})} (${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)})
|
|
4353
|
-
SELECT $1, $2 WHERE NOT EXISTS (
|
|
4354
|
-
SELECT ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}
|
|
4355
|
-
FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
4356
|
-
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}=$2
|
|
3873
|
+
})} (${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)})
|
|
3874
|
+
SELECT $1, $2 WHERE NOT EXISTS (
|
|
3875
|
+
SELECT ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}
|
|
3876
|
+
FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
3877
|
+
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}=$2
|
|
4357
3878
|
)`;
|
|
4358
3879
|
const params = [id, relation.id];
|
|
4359
3880
|
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
@@ -4393,7 +3914,7 @@ class EntityManager {
|
|
|
4393
3914
|
const { queryBuilder } = server;
|
|
4394
3915
|
if (relationProperty.linkTableName) {
|
|
4395
3916
|
for (const relation of relations) {
|
|
4396
|
-
const command = `DELETE FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
3917
|
+
const command = `DELETE FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
4397
3918
|
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}=$2;`;
|
|
4398
3919
|
const params = [id, relation.id];
|
|
4399
3920
|
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
@@ -4540,295 +4061,774 @@ class RapidServer {
|
|
|
4540
4061
|
}
|
|
4541
4062
|
}
|
|
4542
4063
|
}
|
|
4543
|
-
if (routes) {
|
|
4544
|
-
for (const route of routes) {
|
|
4545
|
-
const originalRoute = lodash.find(this.#applicationConfig.routes, (item) => item.code == route.code);
|
|
4546
|
-
if (originalRoute) {
|
|
4547
|
-
originalRoute.name = route.name;
|
|
4548
|
-
originalRoute.actions = route.actions;
|
|
4549
|
-
}
|
|
4550
|
-
else {
|
|
4551
|
-
this.#applicationConfig.routes.push(route);
|
|
4552
|
-
}
|
|
4064
|
+
if (routes) {
|
|
4065
|
+
for (const route of routes) {
|
|
4066
|
+
const originalRoute = lodash.find(this.#applicationConfig.routes, (item) => item.code == route.code);
|
|
4067
|
+
if (originalRoute) {
|
|
4068
|
+
originalRoute.name = route.name;
|
|
4069
|
+
originalRoute.actions = route.actions;
|
|
4070
|
+
}
|
|
4071
|
+
else {
|
|
4072
|
+
this.#applicationConfig.routes.push(route);
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
appendModelProperties(modelSingularCode, properties) {
|
|
4078
|
+
const originalModel = lodash.find(this.#applicationConfig.models, (item) => item.singularCode == modelSingularCode);
|
|
4079
|
+
if (!originalModel) {
|
|
4080
|
+
throw new Error(`Cannot append model properties. Model '${modelSingularCode}' was not found.`);
|
|
4081
|
+
}
|
|
4082
|
+
const originalProperties = originalModel.properties;
|
|
4083
|
+
for (const property of properties) {
|
|
4084
|
+
const originalProperty = lodash.find(originalProperties, (item) => item.code == property.code);
|
|
4085
|
+
if (originalProperty) {
|
|
4086
|
+
originalProperty.name = property.name;
|
|
4087
|
+
}
|
|
4088
|
+
else {
|
|
4089
|
+
originalProperties.push(property);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
registerActionHandler(plugin, options) {
|
|
4094
|
+
const handler = lodash.bind(options.handler, null, plugin);
|
|
4095
|
+
this.#actionHandlersMapByCode.set(options.code, handler);
|
|
4096
|
+
}
|
|
4097
|
+
getActionHandlerByCode(code) {
|
|
4098
|
+
return this.#actionHandlersMapByCode.get(code);
|
|
4099
|
+
}
|
|
4100
|
+
registerMiddleware(middleware) {
|
|
4101
|
+
this.#middlewares.push(middleware);
|
|
4102
|
+
}
|
|
4103
|
+
getDataAccessor(options) {
|
|
4104
|
+
const { namespace, singularCode } = options;
|
|
4105
|
+
let dataAccessor = this.#cachedDataAccessors.get(singularCode);
|
|
4106
|
+
if (dataAccessor) {
|
|
4107
|
+
return dataAccessor;
|
|
4108
|
+
}
|
|
4109
|
+
const model = this.getModel(options);
|
|
4110
|
+
if (!model) {
|
|
4111
|
+
throw new Error(`Data model ${namespace}.${singularCode} not found.`);
|
|
4112
|
+
}
|
|
4113
|
+
dataAccessor = new DataAccessor(this, this.#databaseAccessor, {
|
|
4114
|
+
model,
|
|
4115
|
+
queryBuilder: this.queryBuilder,
|
|
4116
|
+
});
|
|
4117
|
+
this.#cachedDataAccessors.set(singularCode, dataAccessor);
|
|
4118
|
+
return dataAccessor;
|
|
4119
|
+
}
|
|
4120
|
+
getModel(options) {
|
|
4121
|
+
if (options.namespace) {
|
|
4122
|
+
return this.#applicationConfig?.models.find((e) => e.namespace === options.namespace && e.singularCode === options.singularCode);
|
|
4123
|
+
}
|
|
4124
|
+
return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
|
|
4125
|
+
}
|
|
4126
|
+
getEntityManager(singularCode) {
|
|
4127
|
+
let entityManager = this.#cachedEntityManager.get(singularCode);
|
|
4128
|
+
if (entityManager) {
|
|
4129
|
+
return entityManager;
|
|
4130
|
+
}
|
|
4131
|
+
const dataAccessor = this.getDataAccessor({ singularCode });
|
|
4132
|
+
entityManager = new EntityManager(this, dataAccessor);
|
|
4133
|
+
this.#cachedEntityManager.set(singularCode, entityManager);
|
|
4134
|
+
return entityManager;
|
|
4135
|
+
}
|
|
4136
|
+
registerEventHandler(eventName, listener) {
|
|
4137
|
+
this.#eventManager.on(eventName, listener);
|
|
4138
|
+
}
|
|
4139
|
+
registerEntityWatcher(entityWatcher) {
|
|
4140
|
+
this.#entityWatchers.push(entityWatcher);
|
|
4141
|
+
}
|
|
4142
|
+
async emitEvent(event) {
|
|
4143
|
+
const { eventName, payload, sender, routeContext: routerContext } = event;
|
|
4144
|
+
this.#logger.debug(`Emitting '${eventName}' event.`, { eventName });
|
|
4145
|
+
this.#logger.verbose(`Event payload: `, { payload });
|
|
4146
|
+
await this.#eventManager.emit(eventName, sender, payload, routerContext);
|
|
4147
|
+
// TODO: should move this logic into metaManager
|
|
4148
|
+
// if (
|
|
4149
|
+
// (eventName === "entity.create" || eventName === "entity.update" ||
|
|
4150
|
+
// eventName === "entity.delete") &&
|
|
4151
|
+
// payload.namespace === "meta"
|
|
4152
|
+
// ) {
|
|
4153
|
+
// await this.configureApplication();
|
|
4154
|
+
// }
|
|
4155
|
+
}
|
|
4156
|
+
registerService(name, service) {
|
|
4157
|
+
this.#services.set(name, service);
|
|
4158
|
+
}
|
|
4159
|
+
getService(name) {
|
|
4160
|
+
return this.#services.get(name);
|
|
4161
|
+
}
|
|
4162
|
+
registerCronJob(job) {
|
|
4163
|
+
const jobDuplicate = lodash.find(this.#cronJobs, (item) => item.code === job.code);
|
|
4164
|
+
if (jobDuplicate) {
|
|
4165
|
+
this.#logger.warn(`Duplicated cron job with code "${job.code}"`);
|
|
4166
|
+
}
|
|
4167
|
+
this.#cronJobs.push(job);
|
|
4168
|
+
}
|
|
4169
|
+
listCronJobs() {
|
|
4170
|
+
return [...this.#cronJobs, ...this.#appCronJobs];
|
|
4171
|
+
}
|
|
4172
|
+
async start() {
|
|
4173
|
+
this.#logger.info("Starting rapid server...");
|
|
4174
|
+
const pluginManager = this.#pluginManager;
|
|
4175
|
+
await pluginManager.loadPlugins(this.#plugins);
|
|
4176
|
+
await pluginManager.initPlugins();
|
|
4177
|
+
await pluginManager.registerMiddlewares();
|
|
4178
|
+
await pluginManager.registerActionHandlers();
|
|
4179
|
+
await pluginManager.registerEventHandlers();
|
|
4180
|
+
await pluginManager.registerMessageHandlers();
|
|
4181
|
+
await pluginManager.registerTaskProcessors();
|
|
4182
|
+
this.#entityWatchers = this.#entityWatchers.concat(this.#appEntityWatchers);
|
|
4183
|
+
for (const entityWatcher of this.#entityWatchers) {
|
|
4184
|
+
if (entityWatcher.eventName === "entity.beforeCreate") {
|
|
4185
|
+
this.#entityBeforeCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4186
|
+
}
|
|
4187
|
+
else if (entityWatcher.eventName === "entity.create") {
|
|
4188
|
+
this.#entityCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4189
|
+
}
|
|
4190
|
+
else if (entityWatcher.eventName === "entity.beforeUpdate") {
|
|
4191
|
+
this.#entityBeforeUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4192
|
+
}
|
|
4193
|
+
else if (entityWatcher.eventName === "entity.update") {
|
|
4194
|
+
this.#entityUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4195
|
+
}
|
|
4196
|
+
else if (entityWatcher.eventName === "entity.beforeDelete") {
|
|
4197
|
+
this.#entityBeforeDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4198
|
+
}
|
|
4199
|
+
else if (entityWatcher.eventName === "entity.delete") {
|
|
4200
|
+
this.#entityDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4201
|
+
}
|
|
4202
|
+
else if (entityWatcher.eventName === "entity.addRelations") {
|
|
4203
|
+
this.#entityAddRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4204
|
+
}
|
|
4205
|
+
else if (entityWatcher.eventName === "entity.removeRelations") {
|
|
4206
|
+
this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4207
|
+
}
|
|
4208
|
+
else if (entityWatcher.eventName === "entity.beforeResponse") {
|
|
4209
|
+
this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
await this.configureApplication();
|
|
4213
|
+
if (!this.#disableCronJobs) {
|
|
4214
|
+
await pluginManager.registerCronJobs();
|
|
4215
|
+
}
|
|
4216
|
+
this.#logger.info(`Rapid server ready.`);
|
|
4217
|
+
await pluginManager.onApplicationReady(this.#applicationConfig);
|
|
4218
|
+
}
|
|
4219
|
+
async configureApplication() {
|
|
4220
|
+
this.#applicationConfig = lodash.cloneDeep(this.#bootstrapApplicationConfig);
|
|
4221
|
+
const pluginManager = this.#pluginManager;
|
|
4222
|
+
await pluginManager.onLoadingApplication(this.#applicationConfig);
|
|
4223
|
+
await pluginManager.configureModels(this.#applicationConfig);
|
|
4224
|
+
await pluginManager.configureModelProperties(this.#applicationConfig);
|
|
4225
|
+
await pluginManager.configureServices(this.#applicationConfig);
|
|
4226
|
+
await pluginManager.configureRoutes(this.#applicationConfig);
|
|
4227
|
+
// TODO: check application configuration.
|
|
4228
|
+
await pluginManager.onApplicationLoaded(this.#applicationConfig);
|
|
4229
|
+
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
4230
|
+
}
|
|
4231
|
+
registerFacilityFactory(factory) {
|
|
4232
|
+
this.#facilityFactories.set(factory.name, factory);
|
|
4233
|
+
}
|
|
4234
|
+
async getFacility(name, options, nullIfUnknownFacility) {
|
|
4235
|
+
const factory = this.#facilityFactories.get(name);
|
|
4236
|
+
if (!factory) {
|
|
4237
|
+
if (nullIfUnknownFacility) {
|
|
4238
|
+
return null;
|
|
4239
|
+
}
|
|
4240
|
+
else {
|
|
4241
|
+
throw new Error(`Failed to get facility. Unknown facility name: ${name}`);
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
return await factory.createFacility(this, options);
|
|
4245
|
+
}
|
|
4246
|
+
async queryDatabaseObject(sql, params, client, dropErrorLog) {
|
|
4247
|
+
try {
|
|
4248
|
+
return await this.#databaseAccessor.queryDatabaseObject(sql, params, client);
|
|
4249
|
+
}
|
|
4250
|
+
catch (err) {
|
|
4251
|
+
if (!dropErrorLog) {
|
|
4252
|
+
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
4253
|
+
}
|
|
4254
|
+
throw err;
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
async tryQueryDatabaseObject(sql, params, client, dropErrorLog) {
|
|
4258
|
+
try {
|
|
4259
|
+
return await this.queryDatabaseObject(sql, params, client);
|
|
4260
|
+
}
|
|
4261
|
+
catch (err) {
|
|
4262
|
+
if (!dropErrorLog) {
|
|
4263
|
+
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
4553
4264
|
}
|
|
4554
4265
|
}
|
|
4266
|
+
return [];
|
|
4555
4267
|
}
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4268
|
+
get middlewares() {
|
|
4269
|
+
return this.#middlewares;
|
|
4270
|
+
}
|
|
4271
|
+
async handleRequest(rapidRequest, next) {
|
|
4272
|
+
await rapidRequest.parseBody();
|
|
4273
|
+
const routeContext = new RouteContext(this, rapidRequest);
|
|
4274
|
+
const { response } = routeContext;
|
|
4275
|
+
try {
|
|
4276
|
+
await this.#pluginManager.onPrepareRouteContext(routeContext);
|
|
4277
|
+
await this.#buildedRoutes(routeContext, next);
|
|
4560
4278
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4279
|
+
catch (ex) {
|
|
4280
|
+
let error;
|
|
4281
|
+
if (lodash.isString(ex)) {
|
|
4282
|
+
error = {
|
|
4283
|
+
message: ex,
|
|
4284
|
+
};
|
|
4566
4285
|
}
|
|
4567
4286
|
else {
|
|
4568
|
-
|
|
4287
|
+
error = { name: ex.name, message: ex.message, stack: ex.stack };
|
|
4569
4288
|
}
|
|
4289
|
+
this.#logger.error("handle request error.", { error });
|
|
4290
|
+
response.json({ error }, 500);
|
|
4291
|
+
}
|
|
4292
|
+
if (!response.status && !response.body) {
|
|
4293
|
+
response.json({
|
|
4294
|
+
error: {
|
|
4295
|
+
message: "No route handler was found to handle this request.",
|
|
4296
|
+
},
|
|
4297
|
+
}, 404);
|
|
4570
4298
|
}
|
|
4299
|
+
return response.getResponse();
|
|
4571
4300
|
}
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
this.#actionHandlersMapByCode.set(options.code, handler);
|
|
4301
|
+
async beforeRunRouteActions(handlerContext) {
|
|
4302
|
+
await this.#pluginManager.beforeRunRouteActions(handlerContext);
|
|
4575
4303
|
}
|
|
4576
|
-
|
|
4577
|
-
|
|
4304
|
+
async beforeCreateEntity(model, options) {
|
|
4305
|
+
await this.#pluginManager.beforeCreateEntity(model, options);
|
|
4578
4306
|
}
|
|
4579
|
-
|
|
4580
|
-
this.#
|
|
4307
|
+
async beforeUpdateEntity(model, options, currentEntity) {
|
|
4308
|
+
await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
|
|
4581
4309
|
}
|
|
4582
|
-
|
|
4583
|
-
const {
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
return dataAccessor;
|
|
4310
|
+
async #handleEntityEvent(eventName, sender, payload, routerContext) {
|
|
4311
|
+
const { modelSingularCode, baseModelSingularCode } = payload;
|
|
4312
|
+
if (!routerContext) {
|
|
4313
|
+
routerContext = new RouteContext(this);
|
|
4587
4314
|
}
|
|
4588
|
-
const
|
|
4589
|
-
|
|
4590
|
-
|
|
4315
|
+
const entityWatchHandlerContext = {
|
|
4316
|
+
server: this,
|
|
4317
|
+
payload,
|
|
4318
|
+
routerContext,
|
|
4319
|
+
};
|
|
4320
|
+
let emitter;
|
|
4321
|
+
if (eventName === "entity.beforeCreate") {
|
|
4322
|
+
emitter = this.#entityBeforeCreateEventEmitters;
|
|
4591
4323
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4324
|
+
else if (eventName === "entity.create") {
|
|
4325
|
+
emitter = this.#entityCreateEventEmitters;
|
|
4326
|
+
}
|
|
4327
|
+
else if (eventName === "entity.beforeUpdate") {
|
|
4328
|
+
emitter = this.#entityBeforeUpdateEventEmitters;
|
|
4329
|
+
}
|
|
4330
|
+
else if (eventName === "entity.update") {
|
|
4331
|
+
emitter = this.#entityUpdateEventEmitters;
|
|
4332
|
+
}
|
|
4333
|
+
else if (eventName === "entity.beforeDelete") {
|
|
4334
|
+
emitter = this.#entityBeforeDeleteEventEmitters;
|
|
4335
|
+
}
|
|
4336
|
+
else if (eventName === "entity.delete") {
|
|
4337
|
+
emitter = this.#entityDeleteEventEmitters;
|
|
4338
|
+
}
|
|
4339
|
+
else if (eventName === "entity.addRelations") {
|
|
4340
|
+
emitter = this.#entityAddRelationsEventEmitters;
|
|
4341
|
+
}
|
|
4342
|
+
else if (eventName === "entity.removeRelations") {
|
|
4343
|
+
emitter = this.#entityRemoveRelationsEventEmitters;
|
|
4344
|
+
}
|
|
4345
|
+
else if (eventName === "entity.beforeResponse") {
|
|
4346
|
+
emitter = this.#entityBeforeResponseEventEmitters;
|
|
4347
|
+
}
|
|
4348
|
+
await emitter.emit(modelSingularCode, entityWatchHandlerContext);
|
|
4349
|
+
if (baseModelSingularCode) {
|
|
4350
|
+
await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
const parseFormDataBody = async (request, options = { all: false }) => {
|
|
4356
|
+
const contentType = request.headers.get("Content-Type");
|
|
4357
|
+
if (isFormDataContent(contentType)) {
|
|
4358
|
+
return parseFormData(request, options);
|
|
4359
|
+
}
|
|
4360
|
+
return {};
|
|
4361
|
+
};
|
|
4362
|
+
function isFormDataContent(contentType) {
|
|
4363
|
+
if (contentType === null) {
|
|
4364
|
+
return false;
|
|
4365
|
+
}
|
|
4366
|
+
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
4367
|
+
}
|
|
4368
|
+
async function parseFormData(request, options) {
|
|
4369
|
+
const formData = await request.formData();
|
|
4370
|
+
if (formData) {
|
|
4371
|
+
return convertFormDataToBodyData(formData, options);
|
|
4372
|
+
}
|
|
4373
|
+
return {};
|
|
4374
|
+
}
|
|
4375
|
+
function convertFormDataToBodyData(formData, options) {
|
|
4376
|
+
const form = {};
|
|
4377
|
+
formData.forEach((value, key) => {
|
|
4378
|
+
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
4379
|
+
if (!shouldParseAllValues) {
|
|
4380
|
+
form[key] = value;
|
|
4381
|
+
}
|
|
4382
|
+
else {
|
|
4383
|
+
handleParsingAllValues(form, key, value);
|
|
4384
|
+
}
|
|
4385
|
+
});
|
|
4386
|
+
return form;
|
|
4387
|
+
}
|
|
4388
|
+
const handleParsingAllValues = (form, key, value) => {
|
|
4389
|
+
if (form[key] && isArrayField(form[key])) {
|
|
4390
|
+
appendToExistingArray(form[key], value);
|
|
4391
|
+
}
|
|
4392
|
+
else if (form[key]) {
|
|
4393
|
+
convertToNewArray(form, key, value);
|
|
4394
|
+
}
|
|
4395
|
+
else {
|
|
4396
|
+
form[key] = value;
|
|
4397
|
+
}
|
|
4398
|
+
};
|
|
4399
|
+
function isArrayField(field) {
|
|
4400
|
+
return Array.isArray(field);
|
|
4401
|
+
}
|
|
4402
|
+
const appendToExistingArray = (arr, value) => {
|
|
4403
|
+
arr.push(value);
|
|
4404
|
+
};
|
|
4405
|
+
const convertToNewArray = (form, key, value) => {
|
|
4406
|
+
form[key] = [form[key], value];
|
|
4407
|
+
};
|
|
4408
|
+
|
|
4409
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
4410
|
+
class AssertionError extends Error {
|
|
4411
|
+
name = "AssertionError";
|
|
4412
|
+
constructor(message) {
|
|
4413
|
+
super(message);
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
4418
|
+
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
|
|
4419
|
+
function assert(expr, msg = "") {
|
|
4420
|
+
if (!expr) {
|
|
4421
|
+
throw new AssertionError(msg);
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
4426
|
+
// This module is browser compatible.
|
|
4427
|
+
/**
|
|
4428
|
+
* Formats the given date to IMF date time format. (Reference:
|
|
4429
|
+
* https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
|
|
4430
|
+
* IMF is the time format to use when generating times in HTTP
|
|
4431
|
+
* headers. The time being formatted must be in UTC for Format to
|
|
4432
|
+
* generate the correct format.
|
|
4433
|
+
*
|
|
4434
|
+
* @example
|
|
4435
|
+
* ```ts
|
|
4436
|
+
* import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
|
|
4437
|
+
*
|
|
4438
|
+
* toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
|
|
4439
|
+
* ```
|
|
4440
|
+
* @param date Date to parse
|
|
4441
|
+
* @return IMF date formatted string
|
|
4442
|
+
*/
|
|
4443
|
+
function toIMF(date) {
|
|
4444
|
+
function dtPad(v, lPad = 2) {
|
|
4445
|
+
return v.padStart(lPad, "0");
|
|
4446
|
+
}
|
|
4447
|
+
const d = dtPad(date.getUTCDate().toString());
|
|
4448
|
+
const h = dtPad(date.getUTCHours().toString());
|
|
4449
|
+
const min = dtPad(date.getUTCMinutes().toString());
|
|
4450
|
+
const s = dtPad(date.getUTCSeconds().toString());
|
|
4451
|
+
const y = date.getUTCFullYear();
|
|
4452
|
+
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
4453
|
+
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
4454
|
+
return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
4458
|
+
const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
|
|
4459
|
+
function toString(cookie) {
|
|
4460
|
+
if (!cookie.name) {
|
|
4461
|
+
return "";
|
|
4462
|
+
}
|
|
4463
|
+
const out = [];
|
|
4464
|
+
validateName(cookie.name);
|
|
4465
|
+
validateValue(cookie.name, cookie.value);
|
|
4466
|
+
out.push(`${cookie.name}=${cookie.value}`);
|
|
4467
|
+
// Fallback for invalid Set-Cookie
|
|
4468
|
+
// ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
|
|
4469
|
+
if (cookie.name.startsWith("__Secure")) {
|
|
4470
|
+
cookie.secure = true;
|
|
4471
|
+
}
|
|
4472
|
+
if (cookie.name.startsWith("__Host")) {
|
|
4473
|
+
cookie.path = "/";
|
|
4474
|
+
cookie.secure = true;
|
|
4475
|
+
delete cookie.domain;
|
|
4476
|
+
}
|
|
4477
|
+
if (cookie.secure) {
|
|
4478
|
+
out.push("Secure");
|
|
4479
|
+
}
|
|
4480
|
+
if (cookie.httpOnly) {
|
|
4481
|
+
out.push("HttpOnly");
|
|
4482
|
+
}
|
|
4483
|
+
if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
|
|
4484
|
+
assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
|
|
4485
|
+
out.push(`Max-Age=${cookie.maxAge}`);
|
|
4598
4486
|
}
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
}
|
|
4603
|
-
return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
|
|
4487
|
+
if (cookie.domain) {
|
|
4488
|
+
validateDomain(cookie.domain);
|
|
4489
|
+
out.push(`Domain=${cookie.domain}`);
|
|
4604
4490
|
}
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
if (entityManager) {
|
|
4608
|
-
return entityManager;
|
|
4609
|
-
}
|
|
4610
|
-
const dataAccessor = this.getDataAccessor({ singularCode });
|
|
4611
|
-
entityManager = new EntityManager(this, dataAccessor);
|
|
4612
|
-
this.#cachedEntityManager.set(singularCode, entityManager);
|
|
4613
|
-
return entityManager;
|
|
4491
|
+
if (cookie.sameSite) {
|
|
4492
|
+
out.push(`SameSite=${cookie.sameSite}`);
|
|
4614
4493
|
}
|
|
4615
|
-
|
|
4616
|
-
|
|
4494
|
+
if (cookie.path) {
|
|
4495
|
+
validatePath(cookie.path);
|
|
4496
|
+
out.push(`Path=${cookie.path}`);
|
|
4617
4497
|
}
|
|
4618
|
-
|
|
4619
|
-
|
|
4498
|
+
if (cookie.expires) {
|
|
4499
|
+
const { expires } = cookie;
|
|
4500
|
+
const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
|
|
4501
|
+
out.push(`Expires=${dateString}`);
|
|
4620
4502
|
}
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
this.#logger.debug(`Emitting '${eventName}' event.`, { eventName });
|
|
4624
|
-
this.#logger.verbose(`Event payload: `, { payload });
|
|
4625
|
-
await this.#eventManager.emit(eventName, sender, payload, routerContext);
|
|
4626
|
-
// TODO: should move this logic into metaManager
|
|
4627
|
-
// if (
|
|
4628
|
-
// (eventName === "entity.create" || eventName === "entity.update" ||
|
|
4629
|
-
// eventName === "entity.delete") &&
|
|
4630
|
-
// payload.namespace === "meta"
|
|
4631
|
-
// ) {
|
|
4632
|
-
// await this.configureApplication();
|
|
4633
|
-
// }
|
|
4503
|
+
if (cookie.unparsed) {
|
|
4504
|
+
out.push(cookie.unparsed.join("; "));
|
|
4634
4505
|
}
|
|
4635
|
-
|
|
4636
|
-
|
|
4506
|
+
return out.join("; ");
|
|
4507
|
+
}
|
|
4508
|
+
/**
|
|
4509
|
+
* Validate Cookie Name.
|
|
4510
|
+
* @param name Cookie name.
|
|
4511
|
+
*/
|
|
4512
|
+
function validateName(name) {
|
|
4513
|
+
if (name && !FIELD_CONTENT_REGEXP.test(name)) {
|
|
4514
|
+
throw new TypeError(`Invalid cookie name: "${name}".`);
|
|
4637
4515
|
}
|
|
4638
|
-
|
|
4639
|
-
|
|
4516
|
+
}
|
|
4517
|
+
/**
|
|
4518
|
+
* Validate Path Value.
|
|
4519
|
+
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
|
|
4520
|
+
* @param path Path value.
|
|
4521
|
+
*/
|
|
4522
|
+
function validatePath(path) {
|
|
4523
|
+
if (path == null) {
|
|
4524
|
+
return;
|
|
4640
4525
|
}
|
|
4641
|
-
|
|
4642
|
-
const
|
|
4643
|
-
if (
|
|
4644
|
-
|
|
4526
|
+
for (let i = 0; i < path.length; i++) {
|
|
4527
|
+
const c = path.charAt(i);
|
|
4528
|
+
if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7e) || c == ";") {
|
|
4529
|
+
throw new Error(path + ": Invalid cookie path char '" + c + "'");
|
|
4645
4530
|
}
|
|
4646
|
-
this.#cronJobs.push(job);
|
|
4647
|
-
}
|
|
4648
|
-
listCronJobs() {
|
|
4649
|
-
return [...this.#cronJobs, ...this.#appCronJobs];
|
|
4650
4531
|
}
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
else if (entityWatcher.eventName === "entity.beforeUpdate") {
|
|
4670
|
-
this.#entityBeforeUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4671
|
-
}
|
|
4672
|
-
else if (entityWatcher.eventName === "entity.update") {
|
|
4673
|
-
this.#entityUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4674
|
-
}
|
|
4675
|
-
else if (entityWatcher.eventName === "entity.beforeDelete") {
|
|
4676
|
-
this.#entityBeforeDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4677
|
-
}
|
|
4678
|
-
else if (entityWatcher.eventName === "entity.delete") {
|
|
4679
|
-
this.#entityDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4680
|
-
}
|
|
4681
|
-
else if (entityWatcher.eventName === "entity.addRelations") {
|
|
4682
|
-
this.#entityAddRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4683
|
-
}
|
|
4684
|
-
else if (entityWatcher.eventName === "entity.removeRelations") {
|
|
4685
|
-
this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4686
|
-
}
|
|
4687
|
-
else if (entityWatcher.eventName === "entity.beforeResponse") {
|
|
4688
|
-
this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4689
|
-
}
|
|
4532
|
+
}
|
|
4533
|
+
/**
|
|
4534
|
+
* Validate Cookie Value.
|
|
4535
|
+
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
|
|
4536
|
+
* @param value Cookie value.
|
|
4537
|
+
*/
|
|
4538
|
+
function validateValue(name, value) {
|
|
4539
|
+
if (value == null || name == null)
|
|
4540
|
+
return;
|
|
4541
|
+
for (let i = 0; i < value.length; i++) {
|
|
4542
|
+
const c = value.charAt(i);
|
|
4543
|
+
if (c < String.fromCharCode(0x21) ||
|
|
4544
|
+
c == String.fromCharCode(0x22) ||
|
|
4545
|
+
c == String.fromCharCode(0x2c) ||
|
|
4546
|
+
c == String.fromCharCode(0x3b) ||
|
|
4547
|
+
c == String.fromCharCode(0x5c) ||
|
|
4548
|
+
c == String.fromCharCode(0x7f)) {
|
|
4549
|
+
throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
|
|
4690
4550
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
await pluginManager.registerCronJobs();
|
|
4551
|
+
if (c > String.fromCharCode(0x80)) {
|
|
4552
|
+
throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + c.charCodeAt(0).toString(16));
|
|
4694
4553
|
}
|
|
4695
|
-
this.#logger.info(`Rapid server ready.`);
|
|
4696
|
-
await pluginManager.onApplicationReady(this.#applicationConfig);
|
|
4697
4554
|
}
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
await pluginManager.onApplicationLoaded(this.#applicationConfig);
|
|
4708
|
-
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
4555
|
+
}
|
|
4556
|
+
/**
|
|
4557
|
+
* Validate Cookie Domain.
|
|
4558
|
+
* See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
|
|
4559
|
+
* @param domain Cookie domain.
|
|
4560
|
+
*/
|
|
4561
|
+
function validateDomain(domain) {
|
|
4562
|
+
if (domain == null) {
|
|
4563
|
+
return;
|
|
4709
4564
|
}
|
|
4710
|
-
|
|
4711
|
-
|
|
4565
|
+
const char1 = domain.charAt(0);
|
|
4566
|
+
const charN = domain.charAt(domain.length - 1);
|
|
4567
|
+
if (char1 == "-" || charN == "." || charN == "-") {
|
|
4568
|
+
throw new Error("Invalid first/last char in cookie domain: " + domain);
|
|
4712
4569
|
}
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4570
|
+
}
|
|
4571
|
+
/**
|
|
4572
|
+
* Parse cookies of a header
|
|
4573
|
+
*
|
|
4574
|
+
* @example
|
|
4575
|
+
* ```ts
|
|
4576
|
+
* import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
4577
|
+
*
|
|
4578
|
+
* const headers = new Headers();
|
|
4579
|
+
* headers.set("Cookie", "full=of; tasty=chocolate");
|
|
4580
|
+
*
|
|
4581
|
+
* const cookies = getCookies(headers);
|
|
4582
|
+
* console.log(cookies); // { full: "of", tasty: "chocolate" }
|
|
4583
|
+
* ```
|
|
4584
|
+
*
|
|
4585
|
+
* @param headers The headers instance to get cookies from
|
|
4586
|
+
* @return Object with cookie names as keys
|
|
4587
|
+
*/
|
|
4588
|
+
function getCookies(headers) {
|
|
4589
|
+
const cookie = headers.get("Cookie");
|
|
4590
|
+
if (cookie != null) {
|
|
4591
|
+
const out = {};
|
|
4592
|
+
const c = cookie.split(";");
|
|
4593
|
+
for (const kv of c) {
|
|
4594
|
+
const [cookieKey, ...cookieVal] = kv.split("=");
|
|
4595
|
+
assert(cookieKey != null);
|
|
4596
|
+
const key = cookieKey.trim();
|
|
4597
|
+
out[key] = cookieVal.join("=");
|
|
4722
4598
|
}
|
|
4723
|
-
return
|
|
4599
|
+
return out;
|
|
4724
4600
|
}
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4601
|
+
return {};
|
|
4602
|
+
}
|
|
4603
|
+
/**
|
|
4604
|
+
* Set the cookie header properly in the headers
|
|
4605
|
+
*
|
|
4606
|
+
* @example
|
|
4607
|
+
* ```ts
|
|
4608
|
+
* import {
|
|
4609
|
+
* Cookie,
|
|
4610
|
+
* setCookie,
|
|
4611
|
+
* } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
4612
|
+
*
|
|
4613
|
+
* const headers = new Headers();
|
|
4614
|
+
* const cookie: Cookie = { name: "Space", value: "Cat" };
|
|
4615
|
+
* setCookie(headers, cookie);
|
|
4616
|
+
*
|
|
4617
|
+
* const cookieHeader = headers.get("set-cookie");
|
|
4618
|
+
* console.log(cookieHeader); // Space=Cat
|
|
4619
|
+
* ```
|
|
4620
|
+
*
|
|
4621
|
+
* @param headers The headers instance to set the cookie to
|
|
4622
|
+
* @param cookie Cookie to set
|
|
4623
|
+
*/
|
|
4624
|
+
function setCookie(headers, cookie) {
|
|
4625
|
+
// Parsing cookie headers to make consistent set-cookie header
|
|
4626
|
+
// ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
|
|
4627
|
+
const v = toString(cookie);
|
|
4628
|
+
if (v) {
|
|
4629
|
+
headers.append("Set-Cookie", v);
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
/**
|
|
4633
|
+
* Set the cookie header with empty value in the headers to delete it
|
|
4634
|
+
*
|
|
4635
|
+
* > Note: Deleting a `Cookie` will set its expiration date before now. Forcing
|
|
4636
|
+
* > the browser to delete it.
|
|
4637
|
+
*
|
|
4638
|
+
* @example
|
|
4639
|
+
* ```ts
|
|
4640
|
+
* import { deleteCookie } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
4641
|
+
*
|
|
4642
|
+
* const headers = new Headers();
|
|
4643
|
+
* deleteCookie(headers, "deno");
|
|
4644
|
+
*
|
|
4645
|
+
* const cookieHeader = headers.get("set-cookie");
|
|
4646
|
+
* console.log(cookieHeader); // deno=; Expires=Thus, 01 Jan 1970 00:00:00 GMT
|
|
4647
|
+
* ```
|
|
4648
|
+
*
|
|
4649
|
+
* @param headers The headers instance to delete the cookie from
|
|
4650
|
+
* @param name Name of cookie
|
|
4651
|
+
* @param attributes Additional cookie attributes
|
|
4652
|
+
*/
|
|
4653
|
+
function deleteCookie(headers, name, attributes) {
|
|
4654
|
+
setCookie(headers, {
|
|
4655
|
+
name: name,
|
|
4656
|
+
value: "",
|
|
4657
|
+
expires: new Date(0),
|
|
4658
|
+
...attributes,
|
|
4659
|
+
});
|
|
4660
|
+
}
|
|
4661
|
+
function parseSetCookie(value) {
|
|
4662
|
+
const attrs = value.split(";").map((attr) => {
|
|
4663
|
+
const [key, ...values] = attr.trim().split("=");
|
|
4664
|
+
return [key, values.join("=")];
|
|
4665
|
+
});
|
|
4666
|
+
const cookie = {
|
|
4667
|
+
name: attrs[0][0],
|
|
4668
|
+
value: attrs[0][1],
|
|
4669
|
+
};
|
|
4670
|
+
for (const [key, value] of attrs.slice(1)) {
|
|
4671
|
+
switch (key.toLocaleLowerCase()) {
|
|
4672
|
+
case "expires":
|
|
4673
|
+
cookie.expires = new Date(value);
|
|
4674
|
+
break;
|
|
4675
|
+
case "max-age":
|
|
4676
|
+
cookie.maxAge = Number(value);
|
|
4677
|
+
if (cookie.maxAge < 0) {
|
|
4678
|
+
console.warn("Max-Age must be an integer superior or equal to 0. Cookie ignored.");
|
|
4679
|
+
return null;
|
|
4680
|
+
}
|
|
4681
|
+
break;
|
|
4682
|
+
case "domain":
|
|
4683
|
+
cookie.domain = value;
|
|
4684
|
+
break;
|
|
4685
|
+
case "path":
|
|
4686
|
+
cookie.path = value;
|
|
4687
|
+
break;
|
|
4688
|
+
case "secure":
|
|
4689
|
+
cookie.secure = true;
|
|
4690
|
+
break;
|
|
4691
|
+
case "httponly":
|
|
4692
|
+
cookie.httpOnly = true;
|
|
4693
|
+
break;
|
|
4694
|
+
case "samesite":
|
|
4695
|
+
cookie.sameSite = value;
|
|
4696
|
+
break;
|
|
4697
|
+
default:
|
|
4698
|
+
if (!Array.isArray(cookie.unparsed)) {
|
|
4699
|
+
cookie.unparsed = [];
|
|
4700
|
+
}
|
|
4701
|
+
cookie.unparsed.push([key, value].join("="));
|
|
4734
4702
|
}
|
|
4735
4703
|
}
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
if (!dropErrorLog) {
|
|
4742
|
-
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
4743
|
-
}
|
|
4704
|
+
if (cookie.name.startsWith("__Secure-")) {
|
|
4705
|
+
/** This requirement is mentioned in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie but not the RFC. */
|
|
4706
|
+
if (!cookie.secure) {
|
|
4707
|
+
console.warn("Cookies with names starting with `__Secure-` must be set with the secure flag. Cookie ignored.");
|
|
4708
|
+
return null;
|
|
4744
4709
|
}
|
|
4745
|
-
return [];
|
|
4746
|
-
}
|
|
4747
|
-
get middlewares() {
|
|
4748
|
-
return this.#middlewares;
|
|
4749
4710
|
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
const { response } = routeContext;
|
|
4755
|
-
try {
|
|
4756
|
-
await this.#pluginManager.onPrepareRouteContext(routeContext);
|
|
4757
|
-
await this.#buildedRoutes(routeContext, next);
|
|
4711
|
+
if (cookie.name.startsWith("__Host-")) {
|
|
4712
|
+
if (!cookie.secure) {
|
|
4713
|
+
console.warn("Cookies with names starting with `__Host-` must be set with the secure flag. Cookie ignored.");
|
|
4714
|
+
return null;
|
|
4758
4715
|
}
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
error = {
|
|
4763
|
-
message: ex,
|
|
4764
|
-
};
|
|
4765
|
-
}
|
|
4766
|
-
else {
|
|
4767
|
-
error = { name: ex.name, message: ex.message, stack: ex.stack };
|
|
4768
|
-
}
|
|
4769
|
-
this.#logger.error("handle request error.", { error });
|
|
4770
|
-
response.json({ error }, 500);
|
|
4716
|
+
if (cookie.domain !== undefined) {
|
|
4717
|
+
console.warn("Cookies with names starting with `__Host-` must not have a domain specified. Cookie ignored.");
|
|
4718
|
+
return null;
|
|
4771
4719
|
}
|
|
4772
|
-
if (
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
message: "No route handler was found to handle this request.",
|
|
4776
|
-
},
|
|
4777
|
-
}, 404);
|
|
4720
|
+
if (cookie.path !== "/") {
|
|
4721
|
+
console.warn("Cookies with names starting with `__Host-` must have path be `/`. Cookie has been ignored.");
|
|
4722
|
+
return null;
|
|
4778
4723
|
}
|
|
4779
|
-
return response.getResponse();
|
|
4780
|
-
}
|
|
4781
|
-
async beforeRunRouteActions(handlerContext) {
|
|
4782
|
-
await this.#pluginManager.beforeRunRouteActions(handlerContext);
|
|
4783
4724
|
}
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4725
|
+
return cookie;
|
|
4726
|
+
}
|
|
4727
|
+
/**
|
|
4728
|
+
* Parse set-cookies of a header
|
|
4729
|
+
*
|
|
4730
|
+
* @example
|
|
4731
|
+
* ```ts
|
|
4732
|
+
* import { getSetCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
4733
|
+
*
|
|
4734
|
+
* const headers = new Headers([
|
|
4735
|
+
* ["Set-Cookie", "lulu=meow; Secure; Max-Age=3600"],
|
|
4736
|
+
* ["Set-Cookie", "booya=kasha; HttpOnly; Path=/"],
|
|
4737
|
+
* ]);
|
|
4738
|
+
*
|
|
4739
|
+
* const cookies = getSetCookies(headers);
|
|
4740
|
+
* console.log(cookies); // [{ name: "lulu", value: "meow", secure: true, maxAge: 3600 }, { name: "booya", value: "kahsa", httpOnly: true, path: "/ }]
|
|
4741
|
+
* ```
|
|
4742
|
+
*
|
|
4743
|
+
* @param headers The headers instance to get set-cookies from
|
|
4744
|
+
* @return List of cookies
|
|
4745
|
+
*/
|
|
4746
|
+
function getSetCookies(headers) {
|
|
4747
|
+
// TODO(lino-levan): remove this ts-ignore when Typescript 5.2 lands in Deno
|
|
4748
|
+
// @ts-ignore Typescript's TS Dom types will be out of date until 5.2
|
|
4749
|
+
return headers
|
|
4750
|
+
.getSetCookie()
|
|
4751
|
+
/** Parse each `set-cookie` header separately */
|
|
4752
|
+
.map(parseSetCookie)
|
|
4753
|
+
/** Skip empty cookies */
|
|
4754
|
+
.filter(Boolean);
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
const GlobalRequest = global.Request;
|
|
4758
|
+
class RapidRequest {
|
|
4759
|
+
#logger;
|
|
4760
|
+
#raw;
|
|
4761
|
+
#bodyParsed;
|
|
4762
|
+
#body;
|
|
4763
|
+
#headers;
|
|
4764
|
+
#parsedCookies;
|
|
4765
|
+
method;
|
|
4766
|
+
url;
|
|
4767
|
+
ip;
|
|
4768
|
+
constructor(server, req) {
|
|
4769
|
+
this.#logger = server.getLogger();
|
|
4770
|
+
this.#raw = req;
|
|
4771
|
+
this.method = req.method;
|
|
4772
|
+
this.url = new URL(req.url);
|
|
4773
|
+
this.#headers = req.headers;
|
|
4789
4774
|
}
|
|
4790
|
-
async
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
}
|
|
4795
|
-
const entityWatchHandlerContext = {
|
|
4796
|
-
server: this,
|
|
4797
|
-
payload,
|
|
4798
|
-
routerContext,
|
|
4799
|
-
};
|
|
4800
|
-
let emitter;
|
|
4801
|
-
if (eventName === "entity.beforeCreate") {
|
|
4802
|
-
emitter = this.#entityBeforeCreateEventEmitters;
|
|
4803
|
-
}
|
|
4804
|
-
else if (eventName === "entity.create") {
|
|
4805
|
-
emitter = this.#entityCreateEventEmitters;
|
|
4806
|
-
}
|
|
4807
|
-
else if (eventName === "entity.beforeUpdate") {
|
|
4808
|
-
emitter = this.#entityBeforeUpdateEventEmitters;
|
|
4775
|
+
async parseBody() {
|
|
4776
|
+
if (this.#bodyParsed) {
|
|
4777
|
+
this.#logger.warn("Request body has been parsed. 'parseBody()' method should not be called more than once.");
|
|
4778
|
+
return;
|
|
4809
4779
|
}
|
|
4810
|
-
|
|
4811
|
-
|
|
4780
|
+
const requestMethod = this.method;
|
|
4781
|
+
if (requestMethod !== "POST" && requestMethod !== "PUT" && requestMethod !== "PATCH") {
|
|
4782
|
+
this.#body = null;
|
|
4783
|
+
this.#bodyParsed = true;
|
|
4784
|
+
return;
|
|
4812
4785
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4786
|
+
const contentLength = parseInt(this.#headers.get("Content-Length") || "0", 10);
|
|
4787
|
+
if (!contentLength) {
|
|
4788
|
+
this.#body = null;
|
|
4789
|
+
this.#bodyParsed = true;
|
|
4790
|
+
return;
|
|
4815
4791
|
}
|
|
4816
|
-
|
|
4817
|
-
|
|
4792
|
+
const req = this.#raw;
|
|
4793
|
+
const contentType = this.#headers.get("Content-Type") || "application/json";
|
|
4794
|
+
if (contentType.includes("json")) {
|
|
4795
|
+
this.#body = {
|
|
4796
|
+
type: "json",
|
|
4797
|
+
value: await req.json(),
|
|
4798
|
+
};
|
|
4818
4799
|
}
|
|
4819
|
-
else if (
|
|
4820
|
-
|
|
4800
|
+
else if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
4801
|
+
const bodyText = await req.text();
|
|
4802
|
+
this.#body = {
|
|
4803
|
+
type: "form",
|
|
4804
|
+
value: qs__default["default"].parse(bodyText),
|
|
4805
|
+
};
|
|
4821
4806
|
}
|
|
4822
|
-
else if (
|
|
4823
|
-
|
|
4807
|
+
else if (contentType.startsWith("multipart/form-data")) {
|
|
4808
|
+
this.#body = {
|
|
4809
|
+
type: "form-data",
|
|
4810
|
+
value: await parseFormDataBody(req),
|
|
4811
|
+
};
|
|
4824
4812
|
}
|
|
4825
|
-
|
|
4826
|
-
|
|
4813
|
+
this.#bodyParsed = true;
|
|
4814
|
+
}
|
|
4815
|
+
get rawRequest() {
|
|
4816
|
+
return this.#raw;
|
|
4817
|
+
}
|
|
4818
|
+
get headers() {
|
|
4819
|
+
return this.#headers;
|
|
4820
|
+
}
|
|
4821
|
+
get cookies() {
|
|
4822
|
+
if (!this.#parsedCookies) {
|
|
4823
|
+
this.#parsedCookies = getCookies(this.#headers);
|
|
4827
4824
|
}
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4825
|
+
return this.#parsedCookies;
|
|
4826
|
+
}
|
|
4827
|
+
get body() {
|
|
4828
|
+
if (!this.#bodyParsed) {
|
|
4829
|
+
throw new Error("Request body not parsed, you should call 'parseBody()' method before getting the body.");
|
|
4831
4830
|
}
|
|
4831
|
+
return this.#body;
|
|
4832
4832
|
}
|
|
4833
4833
|
}
|
|
4834
4834
|
|
|
@@ -5372,9 +5372,9 @@ class MetaService {
|
|
|
5372
5372
|
await server.tryQueryDatabaseObject(`COMMENT ON TABLE ${queryBuilder.quoteTable(model)} IS ${queryBuilder.formatValueToSqlLiteral(model.name)};`, []);
|
|
5373
5373
|
}
|
|
5374
5374
|
}
|
|
5375
|
-
const sqlQueryColumnInformations = `SELECT c.table_schema, c.table_name, c.column_name, c.ordinal_position, d.description, c.data_type, c.udt_name, c.is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale
|
|
5376
|
-
FROM information_schema.columns c
|
|
5377
|
-
INNER JOIN pg_catalog.pg_statio_all_tables st ON (st.schemaname = c.table_schema and st.relname = c.table_name)
|
|
5375
|
+
const sqlQueryColumnInformations = `SELECT c.table_schema, c.table_name, c.column_name, c.ordinal_position, d.description, c.data_type, c.udt_name, c.is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale
|
|
5376
|
+
FROM information_schema.columns c
|
|
5377
|
+
INNER JOIN pg_catalog.pg_statio_all_tables st ON (st.schemaname = c.table_schema and st.relname = c.table_name)
|
|
5378
5378
|
LEFT JOIN pg_catalog.pg_description d ON (d.objoid = st.relid and d.objsubid = c.ordinal_position);`;
|
|
5379
5379
|
const columnsInDb = await server.queryDatabaseObject(sqlQueryColumnInformations, []);
|
|
5380
5380
|
for (const model of applicationConfig.models) {
|
|
@@ -9481,9 +9481,9 @@ class EntityAccessControlPlugin {
|
|
|
9481
9481
|
if (!userId) {
|
|
9482
9482
|
return;
|
|
9483
9483
|
}
|
|
9484
|
-
const actions = await server.queryDatabaseObject(`select distinct a.* from sys_actions a
|
|
9485
|
-
inner join oc_role_sys_action_links ra on a.id = ra.action_id
|
|
9486
|
-
inner join oc_role_user_links ru on ru.role_id = ra.role_id
|
|
9484
|
+
const actions = await server.queryDatabaseObject(`select distinct a.* from sys_actions a
|
|
9485
|
+
inner join oc_role_sys_action_links ra on a.id = ra.action_id
|
|
9486
|
+
inner join oc_role_user_links ru on ru.role_id = ra.role_id
|
|
9487
9487
|
where ru.user_id = $1;`, [userId]);
|
|
9488
9488
|
routeContext.state.allowedActions = actions.map((item) => item.code);
|
|
9489
9489
|
}
|