@ruiapp/rapid-core 0.1.19 → 0.1.21

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.
Files changed (54) hide show
  1. package/dist/core/actionHandler.d.ts +2 -0
  2. package/dist/core/pluginManager.d.ts +3 -0
  3. package/dist/core/request.d.ts +2 -1
  4. package/dist/core/routeContext.d.ts +4 -1
  5. package/dist/core/server.d.ts +8 -2
  6. package/dist/dataAccess/dataAccessor.d.ts +2 -1
  7. package/dist/facilities/log/LogFacility.d.ts +33 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +288 -264
  10. package/dist/plugins/auth/AuthPlugin.d.ts +0 -9
  11. package/dist/plugins/dataManage/DataManagePlugin.d.ts +0 -10
  12. package/dist/plugins/entityAccessControl/EntityAccessControlPlugin.d.ts +17 -0
  13. package/dist/plugins/fileManage/FileManagePlugin.d.ts +0 -10
  14. package/dist/plugins/metaManage/MetaManagePlugin.d.ts +0 -8
  15. package/dist/plugins/routeManage/RouteManagePlugin.d.ts +0 -9
  16. package/dist/plugins/webhooks/WebhooksPlugin.d.ts +0 -6
  17. package/dist/server.d.ts +8 -3
  18. package/dist/types.d.ts +11 -0
  19. package/dist/utilities/accessControlUtility.d.ts +5 -0
  20. package/package.json +5 -2
  21. package/rollup.config.js +1 -18
  22. package/src/core/actionHandler.ts +2 -0
  23. package/src/core/pluginManager.ts +12 -0
  24. package/src/core/request.ts +6 -2
  25. package/src/core/routeContext.ts +6 -1
  26. package/src/core/routesBuilder.ts +16 -6
  27. package/src/core/server.ts +8 -2
  28. package/src/dataAccess/dataAccessor.ts +13 -9
  29. package/src/dataAccess/entityManager.ts +24 -24
  30. package/src/facilities/log/LogFacility.ts +36 -0
  31. package/src/helpers/inputHelper.ts +3 -3
  32. package/src/helpers/runCollectionEntityActionHandler.ts +4 -9
  33. package/src/index.ts +2 -1
  34. package/src/plugins/auth/AuthPlugin.ts +3 -31
  35. package/src/plugins/dataManage/DataManagePlugin.ts +0 -32
  36. package/src/plugins/dataManage/actionHandlers/addEntityRelations.ts +3 -7
  37. package/src/plugins/dataManage/actionHandlers/createCollectionEntitiesBatch.ts +3 -7
  38. package/src/plugins/dataManage/actionHandlers/createCollectionEntity.ts +3 -7
  39. package/src/plugins/dataManage/actionHandlers/deleteCollectionEntityById.ts +2 -2
  40. package/src/plugins/dataManage/actionHandlers/findCollectionEntities.ts +0 -1
  41. package/src/plugins/dataManage/actionHandlers/findCollectionEntityById.ts +2 -2
  42. package/src/plugins/dataManage/actionHandlers/queryDatabase.ts +5 -9
  43. package/src/plugins/dataManage/actionHandlers/removeEntityRelations.ts +2 -6
  44. package/src/plugins/dataManage/actionHandlers/updateCollectionEntityById.ts +3 -8
  45. package/src/plugins/entityAccessControl/EntityAccessControlPlugin.ts +107 -0
  46. package/src/plugins/fileManage/FileManagePlugin.ts +0 -31
  47. package/src/plugins/metaManage/MetaManagePlugin.ts +16 -39
  48. package/src/plugins/routeManage/RouteManagePlugin.ts +4 -30
  49. package/src/plugins/routeManage/actionHandlers/httpProxy.ts +2 -1
  50. package/src/plugins/webhooks/WebhooksPlugin.ts +26 -36
  51. package/src/queryBuilder/queryBuilder.ts +3 -3
  52. package/src/server.ts +53 -17
  53. package/src/types.ts +13 -0
  54. package/src/utilities/accessControlUtility.ts +33 -0
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var _ = require('lodash');
5
+ var lodash = require('lodash');
6
6
  var events = require('events');
7
7
  var Router = require('koa-tree-router');
8
8
  var qs = require('qs');
@@ -14,25 +14,6 @@ var uuid = require('uuid');
14
14
 
15
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
16
16
 
17
- function _interopNamespace(e) {
18
- if (e && e.__esModule) return e;
19
- var n = Object.create(null);
20
- if (e) {
21
- Object.keys(e).forEach(function (k) {
22
- if (k !== 'default') {
23
- var d = Object.getOwnPropertyDescriptor(e, k);
24
- Object.defineProperty(n, k, d.get ? d : {
25
- enumerable: true,
26
- get: function () { return e[k]; }
27
- });
28
- }
29
- });
30
- }
31
- n["default"] = e;
32
- return Object.freeze(n);
33
- }
34
-
35
- var ___namespace = /*#__PURE__*/_interopNamespace(_);
36
17
  var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
37
18
  var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
38
19
  var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
@@ -46,10 +27,12 @@ function fixBigIntJSONSerialize() {
46
27
  }
47
28
 
48
29
  class DataAccessor {
30
+ #logger;
49
31
  #model;
50
32
  #queryBuilder;
51
33
  #databaseAccessor;
52
- constructor(databaseAccessor, options) {
34
+ constructor(server, databaseAccessor, options) {
35
+ this.#logger = server.getLogger();
53
36
  this.#databaseAccessor = databaseAccessor;
54
37
  this.#queryBuilder = options.queryBuilder;
55
38
  this.#model = options.model;
@@ -63,7 +46,7 @@ class DataAccessor {
63
46
  };
64
47
  const query = this.#queryBuilder.insert(this.#model, options);
65
48
  const result = await this.#databaseAccessor.queryDatabaseObject(query.command, query.params);
66
- return ___namespace.first(result);
49
+ return lodash.first(result);
67
50
  }
68
51
  async updateById(id, entity) {
69
52
  const options = {
@@ -78,17 +61,17 @@ class DataAccessor {
78
61
  };
79
62
  const query = this.#queryBuilder.update(this.#model, options);
80
63
  const result = await this.#databaseAccessor.queryDatabaseObject(query.command, query.params);
81
- return ___namespace.first(result);
64
+ return lodash.first(result);
82
65
  }
83
66
  async find(options) {
84
- console.debug(`DataAccessor '${this.#model.singularCode}' find with options:`, options);
67
+ this.#logger.debug(`Finding '${this.#model.singularCode}' entity.`, { options });
85
68
  const query = this.#queryBuilder.select(this.#model, options);
86
69
  return await this.#databaseAccessor.queryDatabaseObject(query.command, query.params);
87
70
  }
88
71
  async findOne(options) {
89
- ___namespace.set(options, "pagination.limit", 1);
72
+ lodash.set(options, "pagination.limit", 1);
90
73
  const list = await this.find(options);
91
- return ___namespace.first(list);
74
+ return lodash.first(list);
92
75
  }
93
76
  async findById(id) {
94
77
  const options = {
@@ -102,12 +85,12 @@ class DataAccessor {
102
85
  };
103
86
  const query = this.#queryBuilder.select(this.#model, options);
104
87
  const result = await this.#databaseAccessor.queryDatabaseObject(query.command, query.params);
105
- return ___namespace.first(result);
88
+ return lodash.first(result);
106
89
  }
107
90
  async count(options) {
108
91
  const query = this.#queryBuilder.count(this.#model, options);
109
92
  const result = await this.#databaseAccessor.queryDatabaseObject(query.command, query.params);
110
- const row = ___namespace.first(result);
93
+ const row = lodash.first(result);
111
94
  if (row) {
112
95
  return row;
113
96
  }
@@ -233,7 +216,7 @@ class QueryBuilder {
233
216
  }
234
217
  let property = null;
235
218
  if (model) {
236
- property = ___namespace.find(model.properties, (e) => e.code === propertyName);
219
+ property = lodash.find(model.properties, (e) => e.code === propertyName);
237
220
  }
238
221
  if (property && property.type === "json") {
239
222
  params.push(JSON.stringify(entity[propertyName]));
@@ -268,7 +251,7 @@ class QueryBuilder {
268
251
  }
269
252
  let property = null;
270
253
  if (model) {
271
- property = ___namespace.find(model.properties, (e) => (e.columnName || e.code) === propertyName);
254
+ property = lodash.find(model.properties, (e) => (e.columnName || e.code) === propertyName);
272
255
  }
273
256
  if (property && property.type === "json") {
274
257
  params.push(JSON.stringify(entity[propertyName]));
@@ -554,6 +537,14 @@ class PluginManager {
554
537
  }
555
538
  }
556
539
  }
540
+ /** 在接收到HTTP请求,执行 actions 前调用。 */
541
+ async beforeRunRouteActions(handlerContext) {
542
+ for (const plugin of this.#plugins) {
543
+ if (plugin.beforeRunRouteActions) {
544
+ await plugin.beforeRunRouteActions(this.#server, handlerContext);
545
+ }
546
+ }
547
+ }
557
548
  }
558
549
 
559
550
  class EventManager {
@@ -580,6 +571,7 @@ function isNullOrUndefined(val) {
580
571
  }
581
572
 
582
573
  async function buildRoutes(server, applicationConfig) {
574
+ const logger = server.getLogger();
583
575
  const router = new Router__default["default"]();
584
576
  let baseUrl = server.config.baseUrl;
585
577
  if (baseUrl) {
@@ -596,6 +588,7 @@ async function buildRoutes(server, applicationConfig) {
596
588
  }
597
589
  const routePath = baseUrl + routeConfig.endpoint;
598
590
  router[routeConfig.method.toLowerCase()](routePath, async (routerContext, next) => {
591
+ routerContext.routeConfig = lodash.cloneDeep(routeConfig);
599
592
  const { request, params } = routerContext;
600
593
  let search = request.url.search;
601
594
  if (search && search.startsWith("?")) {
@@ -612,21 +605,27 @@ async function buildRoutes(server, applicationConfig) {
612
605
  }
613
606
  }
614
607
  // Normalize input value
615
- console.debug(`${requestMethod} ${request.url.toString()}`);
616
- console.debug(`input: ${JSON.stringify(input)}`);
608
+ logger.debug("Processing rapid request.", {
609
+ method: requestMethod,
610
+ url: request.url.toString(),
611
+ input
612
+ });
617
613
  let handlerContext = {
614
+ logger,
618
615
  routerContext,
619
616
  next,
620
617
  server,
621
618
  applicationConfig,
622
619
  input,
623
620
  };
624
- for (const handlerConfig of routeConfig.actions) {
625
- const handler = server.getActionHandlerByCode(handlerConfig.code);
621
+ await server.beforeRunRouteActions(handlerContext);
622
+ for (const actionConfig of routeConfig.actions) {
623
+ const actionCode = actionConfig.code;
624
+ const handler = server.getActionHandlerByCode(actionCode);
626
625
  if (!handler) {
627
- throw new Error("Unknown handler: " + handlerConfig.code);
626
+ throw new Error("Unknown handler: " + actionCode);
628
627
  }
629
- const result = handler(handlerContext, handlerConfig.config);
628
+ const result = handler(handlerContext, actionConfig.config);
630
629
  if (result instanceof Promise) {
631
630
  await result;
632
631
  }
@@ -645,12 +644,12 @@ function mergeHeaders(target, source) {
645
644
  target.set(keyValuePair[0], keyValuePair[1]);
646
645
  }
647
646
  }
648
- else if (_.isArray(source)) {
647
+ else if (lodash.isArray(source)) {
649
648
  for (const keyValuePair of source) {
650
649
  target.set(keyValuePair[0], keyValuePair[1]);
651
650
  }
652
651
  }
653
- else if (_.isObject(source)) {
652
+ else if (lodash.isObject(source)) {
654
653
  Object.entries(source).forEach(([key, value]) => target.set(key, value));
655
654
  }
656
655
  return target;
@@ -698,13 +697,16 @@ class RapidResponse {
698
697
  }
699
698
 
700
699
  class RouteContext {
700
+ #logger;
701
701
  request;
702
702
  response;
703
703
  state;
704
704
  method;
705
705
  path;
706
706
  params;
707
- constructor(request) {
707
+ routeConfig;
708
+ constructor(server, request) {
709
+ this.#logger = server.getLogger();
708
710
  this.request = request;
709
711
  this.state = {};
710
712
  this.response = new RapidResponse();
@@ -1016,6 +1018,7 @@ function setCookie(headers, cookie) {
1016
1018
 
1017
1019
  const GlobalRequest = global.Request;
1018
1020
  class RapidRequest {
1021
+ #logger;
1019
1022
  #raw;
1020
1023
  #bodyParsed;
1021
1024
  #body;
@@ -1023,7 +1026,8 @@ class RapidRequest {
1023
1026
  #parsedCookies;
1024
1027
  method;
1025
1028
  url;
1026
- constructor(req) {
1029
+ constructor(server, req) {
1030
+ this.#logger = server.getLogger();
1027
1031
  this.#raw = req;
1028
1032
  this.method = req.method;
1029
1033
  this.url = new URL(req.url);
@@ -1031,7 +1035,7 @@ class RapidRequest {
1031
1035
  }
1032
1036
  async parseBody() {
1033
1037
  if (this.#bodyParsed) {
1034
- console.warn("Request body has been parsed. 'parseBody()' method should not be called more than once.");
1038
+ this.#logger.warn("Request body has been parsed. 'parseBody()' method should not be called more than once.");
1035
1039
  return;
1036
1040
  }
1037
1041
  const requestMethod = this.method;
@@ -1739,7 +1743,7 @@ async function findEntities(server, dataAccessor, options) {
1739
1743
  const fieldsToSelect = [];
1740
1744
  const relationPropertiesToSelect = [];
1741
1745
  if (options.properties) {
1742
- ___namespace.forEach(options.properties, (propertyCode) => {
1746
+ lodash.forEach(options.properties, (propertyCode) => {
1743
1747
  const property = model.properties.find((e) => e.code === propertyCode);
1744
1748
  if (!property) {
1745
1749
  throw new Error(`Collection '${model.namespace}.${model.singularCode}' does not have a property '${propertyCode}'.`);
@@ -1780,8 +1784,8 @@ async function findEntities(server, dataAccessor, options) {
1780
1784
  }
1781
1785
  if (isManyRelation) {
1782
1786
  const relationLinks = await findManyRelationLinksViaLinkTable(server, targetModel, relationProperty, entityIds);
1783
- ___namespace.forEach(entities, (entity) => {
1784
- entity[relationProperty.code] = ___namespace.filter(relationLinks, (link) => {
1787
+ lodash.forEach(entities, (entity) => {
1788
+ entity[relationProperty.code] = lodash.filter(relationLinks, (link) => {
1785
1789
  return link[relationProperty.selfIdColumnName] == entity["id"];
1786
1790
  }).map(link => mapDbRowToEntity(targetModel, link.targetEntity, false));
1787
1791
  });
@@ -1793,7 +1797,7 @@ async function findEntities(server, dataAccessor, options) {
1793
1797
  relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode(server, model, relationProperty, entityIds);
1794
1798
  }
1795
1799
  else {
1796
- const targetEntityIds = ___namespace.uniq(___namespace.reject(___namespace.map(entities, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
1800
+ const targetEntityIds = lodash.uniq(lodash.reject(lodash.map(entities, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
1797
1801
  relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode(server, model, relationProperty, targetEntityIds);
1798
1802
  }
1799
1803
  const targetModel = server.getModel({
@@ -1801,12 +1805,12 @@ async function findEntities(server, dataAccessor, options) {
1801
1805
  });
1802
1806
  entities.forEach((entity) => {
1803
1807
  if (isManyRelation) {
1804
- entity[relationProperty.code] = ___namespace.filter(relatedEntities, (relatedEntity) => {
1808
+ entity[relationProperty.code] = lodash.filter(relatedEntities, (relatedEntity) => {
1805
1809
  return relatedEntity[relationProperty.selfIdColumnName] == entity.id;
1806
1810
  }).map(item => mapDbRowToEntity(targetModel, item, false));
1807
1811
  }
1808
1812
  else {
1809
- entity[relationProperty.code] = mapDbRowToEntity(targetModel, ___namespace.find(relatedEntities, (relatedEntity) => {
1813
+ entity[relationProperty.code] = mapDbRowToEntity(targetModel, lodash.find(relatedEntities, (relatedEntity) => {
1810
1814
  // TODO: id property code should be configurable.
1811
1815
  return relatedEntity["id"] == entity[relationProperty.targetIdColumnName];
1812
1816
  }), false);
@@ -1819,7 +1823,7 @@ async function findEntities(server, dataAccessor, options) {
1819
1823
  }
1820
1824
  async function findEntity(server, dataAccessor, options) {
1821
1825
  const entities = await findEntities(server, dataAccessor, options);
1822
- return ___namespace.first(entities);
1826
+ return lodash.first(entities);
1823
1827
  }
1824
1828
  async function findById(server, dataAccessor, id, keepNonPropertyFields = false) {
1825
1829
  return await findEntity(server, dataAccessor, {
@@ -1845,7 +1849,7 @@ async function replaceFiltersWithFiltersOperator(server, model, filters) {
1845
1849
  replacedFilters.push(filter);
1846
1850
  }
1847
1851
  else if (operator === "exists" || operator === "notExists") {
1848
- const relationProperty = ___namespace.find(model.properties, (property) => property.code === filter.field);
1852
+ const relationProperty = lodash.find(model.properties, (property) => property.code === filter.field);
1849
1853
  if (!relationProperty) {
1850
1854
  throw new Error(`Invalid filters. Property '${filter.field}' was not found in model '${model.namespace}.${model.singularCode}'`);
1851
1855
  }
@@ -1890,7 +1894,7 @@ async function replaceFiltersWithFiltersOperator(server, model, filters) {
1890
1894
  filters: filter.filters,
1891
1895
  properties: ["id"],
1892
1896
  });
1893
- const entityIds = ___namespace.map(entities, (entity) => entity["id"]);
1897
+ const entityIds = lodash.map(entities, (entity) => entity["id"]);
1894
1898
  replacedFilters.push({
1895
1899
  field: relationProperty.targetIdColumnName,
1896
1900
  operator: operator === "exists" ? "in" : "notIn",
@@ -1909,7 +1913,7 @@ async function replaceFiltersWithFiltersOperator(server, model, filters) {
1909
1913
  filters: filter.filters,
1910
1914
  properties: [relationProperty.selfIdColumnName],
1911
1915
  });
1912
- const selfEntityIds = ___namespace.map(targetEntities, (entity) => entity[relationProperty.selfIdColumnName]);
1916
+ const selfEntityIds = lodash.map(targetEntities, (entity) => entity[relationProperty.selfIdColumnName]);
1913
1917
  replacedFilters.push({
1914
1918
  field: "id",
1915
1919
  operator: operator === "exists" ? "in" : "notIn",
@@ -1934,7 +1938,7 @@ async function replaceFiltersWithFiltersOperator(server, model, filters) {
1934
1938
  filters: filter.filters,
1935
1939
  properties: ['id'],
1936
1940
  });
1937
- const targetEntityIds = ___namespace.map(targetEntities, (entity) => entity['id']);
1941
+ const targetEntityIds = lodash.map(targetEntities, (entity) => entity['id']);
1938
1942
  const command = `SELECT * FROM ${server.queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })} WHERE ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName)} = ANY($1::int[])`;
1939
1943
  const params = [targetEntityIds];
1940
1944
  const links = await server.queryDatabaseObject(command, params);
@@ -1971,8 +1975,8 @@ async function findManyRelationLinksViaLinkTable(server, targetModel, relationPr
1971
1975
  singularCode: targetModel.singularCode,
1972
1976
  });
1973
1977
  const targetEntities = await dataAccessor.find(findEntityOptions);
1974
- ___namespace.forEach(links, (link) => {
1975
- link.targetEntity = ___namespace.find(targetEntities, (e) => e["id"] == link[relationProperty.targetIdColumnName]);
1978
+ lodash.forEach(links, (link) => {
1979
+ link.targetEntity = lodash.find(targetEntities, (e) => e["id"] == link[relationProperty.targetIdColumnName]);
1976
1980
  });
1977
1981
  return links;
1978
1982
  }
@@ -2011,7 +2015,7 @@ async function createEntity(server, dataAccessor, options) {
2011
2015
  const { entity } = options;
2012
2016
  const oneRelationPropertiesToCreate = [];
2013
2017
  const manyRelationPropertiesToCreate = [];
2014
- ___namespace.keys(entity).forEach((propertyCode) => {
2018
+ lodash.keys(entity).forEach((propertyCode) => {
2015
2019
  const property = model.properties.find((e) => e.code === propertyCode);
2016
2020
  if (!property) {
2017
2021
  // Unknown property
@@ -2030,7 +2034,7 @@ async function createEntity(server, dataAccessor, options) {
2030
2034
  // save one-relation properties
2031
2035
  for (const property of oneRelationPropertiesToCreate) {
2032
2036
  const fieldValue = entity[property.code];
2033
- if (___namespace.isObject(fieldValue)) {
2037
+ if (lodash.isObject(fieldValue)) {
2034
2038
  if (!fieldValue["id"]) {
2035
2039
  const targetDataAccessor = server.getDataAccessor({
2036
2040
  singularCode: property.targetSingularCode,
@@ -2059,12 +2063,12 @@ async function createEntity(server, dataAccessor, options) {
2059
2063
  singularCode: property.targetSingularCode,
2060
2064
  });
2061
2065
  const relatedEntitiesToBeSaved = entity[property.code];
2062
- if (!___namespace.isArray(relatedEntitiesToBeSaved)) {
2066
+ if (!lodash.isArray(relatedEntitiesToBeSaved)) {
2063
2067
  throw new Error(`Value of field '${property.code}' should be an array.`);
2064
2068
  }
2065
2069
  for (const relatedEntityToBeSaved of relatedEntitiesToBeSaved) {
2066
2070
  let relatedEntityId;
2067
- if (___namespace.isObject(relatedEntityToBeSaved)) {
2071
+ if (lodash.isObject(relatedEntityToBeSaved)) {
2068
2072
  relatedEntityId = relatedEntityToBeSaved["id"];
2069
2073
  if (!relatedEntityId) {
2070
2074
  // related entity is to be created
@@ -2138,7 +2142,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2138
2142
  }
2139
2143
  const oneRelationPropertiesToUpdate = [];
2140
2144
  const manyRelationPropertiesToUpdate = [];
2141
- ___namespace.keys(changes).forEach((propertyCode) => {
2145
+ lodash.keys(changes).forEach((propertyCode) => {
2142
2146
  const property = model.properties.find((e) => e.code === propertyCode);
2143
2147
  if (!property) {
2144
2148
  // Unknown property
@@ -2156,7 +2160,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2156
2160
  const row = mapEntityToDbRow(model, changes);
2157
2161
  oneRelationPropertiesToUpdate.forEach(property => {
2158
2162
  const fieldValue = changes[property.code];
2159
- if (___namespace.isObject(fieldValue)) {
2163
+ if (lodash.isObject(fieldValue)) {
2160
2164
  row[property.targetIdColumnName] = fieldValue["id"];
2161
2165
  }
2162
2166
  else {
@@ -2175,7 +2179,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2175
2179
  singularCode: property.targetSingularCode,
2176
2180
  });
2177
2181
  const relatedEntitiesToBeSaved = changes[property.code];
2178
- if (!___namespace.isArray(relatedEntitiesToBeSaved)) {
2182
+ if (!lodash.isArray(relatedEntitiesToBeSaved)) {
2179
2183
  throw new Error(`Value of field '${property.code}' should be an array.`);
2180
2184
  }
2181
2185
  if (property.linkTableName) {
@@ -2184,7 +2188,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2184
2188
  }
2185
2189
  for (const relatedEntityToBeSaved of relatedEntitiesToBeSaved) {
2186
2190
  let relatedEntityId;
2187
- if (___namespace.isObject(relatedEntityToBeSaved)) {
2191
+ if (lodash.isObject(relatedEntityToBeSaved)) {
2188
2192
  relatedEntityId = relatedEntityToBeSaved["id"];
2189
2193
  if (!relatedEntityId) {
2190
2194
  // related entity is to be created
@@ -2369,6 +2373,7 @@ class EntityManager {
2369
2373
  }
2370
2374
 
2371
2375
  class RapidServer {
2376
+ #logger;
2372
2377
  #pluginManager;
2373
2378
  #plugins;
2374
2379
  #eventManager;
@@ -2384,6 +2389,7 @@ class RapidServer {
2384
2389
  databaseConfig;
2385
2390
  #buildedRoutes;
2386
2391
  constructor(options) {
2392
+ this.#logger = options.logger;
2387
2393
  this.#pluginManager = new PluginManager(this);
2388
2394
  this.#eventManager = new EventManager();
2389
2395
  this.#middlewares = [];
@@ -2400,6 +2406,9 @@ class RapidServer {
2400
2406
  this.config = options.serverConfig;
2401
2407
  this.#plugins = options.plugins || [];
2402
2408
  }
2409
+ getLogger() {
2410
+ return this.#logger;
2411
+ }
2403
2412
  getApplicationConfig() {
2404
2413
  return this.#applicationConfig;
2405
2414
  }
@@ -2407,12 +2416,13 @@ class RapidServer {
2407
2416
  const { models, routes } = config;
2408
2417
  if (models) {
2409
2418
  for (const model of models) {
2410
- const originalModel = ___namespace.find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
2419
+ const originalModel = lodash.find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
2411
2420
  if (originalModel) {
2421
+ lodash.merge(originalModel, lodash.omit(model, ["id", "maintainedBy", "namespace", "singularCode", "pluralCode", "schema", "tableName", "properties", "extensions"]));
2412
2422
  originalModel.name = model.name;
2413
2423
  const originalProperties = originalModel.properties;
2414
2424
  for (const property of model.properties) {
2415
- const originalProperty = ___namespace.find(originalProperties, (item) => item.code == property.code);
2425
+ const originalProperty = lodash.find(originalProperties, (item) => item.code == property.code);
2416
2426
  if (originalProperty) {
2417
2427
  originalProperty.name = property.name;
2418
2428
  }
@@ -2428,7 +2438,7 @@ class RapidServer {
2428
2438
  }
2429
2439
  if (routes) {
2430
2440
  for (const route of routes) {
2431
- const originalRoute = ___namespace.find(this.#applicationConfig.routes, (item) => item.code == route.code);
2441
+ const originalRoute = lodash.find(this.#applicationConfig.routes, (item) => item.code == route.code);
2432
2442
  if (originalRoute) {
2433
2443
  originalRoute.name = route.name;
2434
2444
  originalRoute.actions = route.actions;
@@ -2439,8 +2449,24 @@ class RapidServer {
2439
2449
  }
2440
2450
  }
2441
2451
  }
2452
+ appendModelProperties(modelSingularCode, properties) {
2453
+ const originalModel = lodash.find(this.#applicationConfig.models, (item) => item.singularCode == modelSingularCode);
2454
+ if (!originalModel) {
2455
+ throw new Error(`Cannot append model properties. Model '${modelSingularCode}' was not found.`);
2456
+ }
2457
+ const originalProperties = originalModel.properties;
2458
+ for (const property of properties) {
2459
+ const originalProperty = lodash.find(originalProperties, (item) => item.code == property.code);
2460
+ if (originalProperty) {
2461
+ originalProperty.name = property.name;
2462
+ }
2463
+ else {
2464
+ originalProperties.push(property);
2465
+ }
2466
+ }
2467
+ }
2442
2468
  registerActionHandler(plugin, options) {
2443
- const handler = ___namespace.bind(options.handler, null, plugin);
2469
+ const handler = lodash.bind(options.handler, null, plugin);
2444
2470
  this.#actionHandlersMapByCode.set(options.code, handler);
2445
2471
  }
2446
2472
  getActionHandlerByCode(code) {
@@ -2459,7 +2485,7 @@ class RapidServer {
2459
2485
  if (!model) {
2460
2486
  throw new Error(`Data model ${namespace}.${singularCode} not found.`);
2461
2487
  }
2462
- dataAccessor = new DataAccessor(this.#databaseAccessor, {
2488
+ dataAccessor = new DataAccessor(this, this.#databaseAccessor, {
2463
2489
  model,
2464
2490
  queryBuilder: this.queryBuilder,
2465
2491
  });
@@ -2487,7 +2513,7 @@ class RapidServer {
2487
2513
  return this;
2488
2514
  }
2489
2515
  async emitEvent(eventName, sender, payload) {
2490
- console.log(`Emit event "${eventName}"`, payload);
2516
+ this.#logger.debug(`Emitting '${eventName}' event.`, { eventName, payload });
2491
2517
  await this.#eventManager.emit(eventName, sender, payload);
2492
2518
  // TODO: should move this logic into metaManager
2493
2519
  // if (
@@ -2499,7 +2525,7 @@ class RapidServer {
2499
2525
  // }
2500
2526
  }
2501
2527
  async start() {
2502
- console.log("Starting rapid server...");
2528
+ this.#logger.info("Starting rapid server...");
2503
2529
  const pluginManager = this.#pluginManager;
2504
2530
  await pluginManager.loadPlugins(this.#plugins);
2505
2531
  await pluginManager.initPlugins();
@@ -2509,11 +2535,11 @@ class RapidServer {
2509
2535
  await pluginManager.registerMessageHandlers();
2510
2536
  await pluginManager.registerTaskProcessors();
2511
2537
  await this.configureApplication();
2512
- console.log(`Application ready.`);
2538
+ this.#logger.info(`Rapid server ready.`);
2513
2539
  await pluginManager.onApplicationReady(this.#applicationConfig);
2514
2540
  }
2515
2541
  async configureApplication() {
2516
- this.#applicationConfig = ___namespace.merge({}, this.#bootstrapApplicationConfig);
2542
+ this.#applicationConfig = lodash.cloneDeep(this.#bootstrapApplicationConfig);
2517
2543
  const pluginManager = this.#pluginManager;
2518
2544
  await pluginManager.onLoadingApplication(this.#applicationConfig);
2519
2545
  await pluginManager.configureModels(this.#applicationConfig);
@@ -2524,14 +2550,20 @@ class RapidServer {
2524
2550
  this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
2525
2551
  }
2526
2552
  async queryDatabaseObject(sql, params) {
2527
- return await this.#databaseAccessor.queryDatabaseObject(sql, params);
2553
+ try {
2554
+ return await this.#databaseAccessor.queryDatabaseObject(sql, params);
2555
+ }
2556
+ catch (err) {
2557
+ this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
2558
+ throw err;
2559
+ }
2528
2560
  }
2529
2561
  async tryQueryDatabaseObject(sql, params) {
2530
2562
  try {
2531
2563
  return await this.queryDatabaseObject(sql, params);
2532
2564
  }
2533
2565
  catch (err) {
2534
- console.error(err.message);
2566
+ this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
2535
2567
  }
2536
2568
  return [];
2537
2569
  }
@@ -2539,13 +2571,16 @@ class RapidServer {
2539
2571
  return this.#middlewares;
2540
2572
  }
2541
2573
  async handleRequest(request, next) {
2542
- const rapidRequest = new RapidRequest(request);
2574
+ const rapidRequest = new RapidRequest(this, request);
2543
2575
  await rapidRequest.parseBody();
2544
- const routeContext = new RouteContext(rapidRequest);
2545
- this.#pluginManager.onPrepareRouteContext(routeContext);
2576
+ const routeContext = new RouteContext(this, rapidRequest);
2577
+ await this.#pluginManager.onPrepareRouteContext(routeContext);
2546
2578
  await this.#buildedRoutes(routeContext, next);
2547
2579
  return routeContext.response.getResponse();
2548
2580
  }
2581
+ async beforeRunRouteActions(handlerContext) {
2582
+ await this.#pluginManager.beforeRunRouteActions(handlerContext);
2583
+ }
2549
2584
  }
2550
2585
 
2551
2586
  // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
@@ -2751,10 +2786,6 @@ class MetaManager {
2751
2786
  get configurations() {
2752
2787
  return [];
2753
2788
  }
2754
- async initPlugin(server) {
2755
- }
2756
- async registerMiddlewares(server) {
2757
- }
2758
2789
  async registerActionHandlers(server) {
2759
2790
  server.registerActionHandler(this, listMetaModels);
2760
2791
  server.registerActionHandler(this, listMetaRoutes);
@@ -2765,31 +2796,20 @@ class MetaManager {
2765
2796
  server.registerEventHandler("entity.update", handleEntityUpdateEvent.bind(this, server));
2766
2797
  server.registerEventHandler("entity.delete", handleEntityDeleteEvent.bind(this, server));
2767
2798
  }
2768
- async registerMessageHandlers(server) {
2769
- }
2770
- async registerTaskProcessors(server) {
2771
- }
2772
- async onLoadingApplication(server, applicationConfig) {
2773
- }
2774
2799
  async configureModels(server, applicationConfig) {
2800
+ const logger = server.getLogger();
2775
2801
  try {
2802
+ logger.info("Loading meta of models...");
2776
2803
  const models = await listCollections(server, applicationConfig);
2777
2804
  server.appendApplicationConfig({ models });
2778
2805
  }
2779
- catch (ex) {
2780
- console.warn("Failed to loading existing meta of models.", ex);
2806
+ catch (error) {
2807
+ logger.crit("Failed to load meta of models.", { error });
2781
2808
  }
2782
2809
  }
2783
- async configureModelProperties(server, applicationConfig) {
2784
- }
2785
- async configureRoutes(server, applicationConfig) {
2786
- }
2787
2810
  async onApplicationLoaded(server, applicationConfig) {
2788
- console.log("metaManager.onApplicationLoaded");
2789
2811
  await syncDatabaseSchema(server, applicationConfig);
2790
2812
  }
2791
- async onApplicationReady(server, applicationConfig) {
2792
- }
2793
2813
  }
2794
2814
  async function handleEntityCreateEvent(server, sender, payload) {
2795
2815
  if (sender === this) {
@@ -2849,15 +2869,16 @@ function listCollections(server, applicationConfig) {
2849
2869
  });
2850
2870
  }
2851
2871
  async function syncDatabaseSchema(server, applicationConfig) {
2852
- console.log("Synchronizing database schema...");
2872
+ const logger = server.getLogger();
2873
+ logger.info("Synchronizing database schema...");
2853
2874
  const sqlQueryTableInformations = `SELECT table_schema, table_name FROM information_schema.tables`;
2854
2875
  const tablesInDb = await server.queryDatabaseObject(sqlQueryTableInformations);
2855
2876
  const { queryBuilder } = server;
2856
2877
  for (const model of applicationConfig.models) {
2857
- console.debug(`Checking data table for '${model.namespace}.${model.singularCode}'...`);
2878
+ logger.debug(`Checking data table for '${model.namespace}.${model.singularCode}'...`);
2858
2879
  const expectedTableSchema = model.schema || server.databaseConfig.dbDefaultSchema;
2859
2880
  const expectedTableName = model.tableName;
2860
- const tableInDb = ___namespace.find(tablesInDb, { table_schema: expectedTableSchema, table_name: expectedTableName });
2881
+ const tableInDb = lodash.find(tablesInDb, { table_schema: expectedTableSchema, table_name: expectedTableName });
2861
2882
  if (!tableInDb) {
2862
2883
  await server.queryDatabaseObject(`CREATE TABLE IF NOT EXISTS ${queryBuilder.quoteTable(model)} ()`, []);
2863
2884
  }
@@ -2866,16 +2887,16 @@ async function syncDatabaseSchema(server, applicationConfig) {
2866
2887
  FROM information_schema.columns;`;
2867
2888
  const columnsInDb = await server.queryDatabaseObject(sqlQueryColumnInformations, []);
2868
2889
  for (const model of applicationConfig.models) {
2869
- console.debug(`Checking data columns for '${model.namespace}.${model.singularCode}'...`);
2890
+ logger.debug(`Checking data columns for '${model.namespace}.${model.singularCode}'...`);
2870
2891
  for (const property of model.properties) {
2871
2892
  let columnDDL;
2872
2893
  if (isRelationProperty(property)) {
2873
2894
  if (property.relation === "one") {
2874
2895
  const targetModel = applicationConfig.models.find(item => item.singularCode === property.targetSingularCode);
2875
2896
  if (!targetModel) {
2876
- console.warn(`Cannot find target model with singular code "${property.targetSingularCode}".`);
2897
+ logger.warn(`Cannot find target model with singular code "${property.targetSingularCode}".`);
2877
2898
  }
2878
- const columnInDb = ___namespace.find(columnsInDb, {
2899
+ const columnInDb = lodash.find(columnsInDb, {
2879
2900
  table_schema: model.schema || "public",
2880
2901
  table_name: model.tableName,
2881
2902
  column_name: property.targetIdColumnName,
@@ -2893,7 +2914,7 @@ async function syncDatabaseSchema(server, applicationConfig) {
2893
2914
  }
2894
2915
  else if (property.relation === "many") {
2895
2916
  if (property.linkTableName) {
2896
- const tableInDb = ___namespace.find(tablesInDb, { table_schema: property.linkSchema || server.databaseConfig.dbDefaultSchema, table_name: property.linkTableName });
2917
+ const tableInDb = lodash.find(tablesInDb, { table_schema: property.linkSchema || server.databaseConfig.dbDefaultSchema, table_name: property.linkTableName });
2897
2918
  if (!tableInDb) {
2898
2919
  columnDDL = generateLinkTableDDL(queryBuilder, {
2899
2920
  linkSchema: property.linkSchema,
@@ -2906,10 +2927,10 @@ async function syncDatabaseSchema(server, applicationConfig) {
2906
2927
  else {
2907
2928
  const targetModel = applicationConfig.models.find(item => item.singularCode === property.targetSingularCode);
2908
2929
  if (!targetModel) {
2909
- console.warn(`Cannot find target model with singular code "${property.targetSingularCode}".`);
2930
+ logger.warn(`Cannot find target model with singular code "${property.targetSingularCode}".`);
2910
2931
  continue;
2911
2932
  }
2912
- const columnInDb = ___namespace.find(columnsInDb, {
2933
+ const columnInDb = lodash.find(columnsInDb, {
2913
2934
  table_schema: targetModel.schema || "public",
2914
2935
  table_name: targetModel.tableName,
2915
2936
  column_name: property.selfIdColumnName,
@@ -2935,7 +2956,7 @@ async function syncDatabaseSchema(server, applicationConfig) {
2935
2956
  }
2936
2957
  else {
2937
2958
  const columnName = property.columnName || property.code;
2938
- const columnInDb = ___namespace.find(columnsInDb, {
2959
+ const columnInDb = lodash.find(columnsInDb, {
2939
2960
  table_schema: model.schema || "public",
2940
2961
  table_name: model.tableName,
2941
2962
  column_name: columnName,
@@ -2997,7 +3018,6 @@ function generateCreateColumnDDL(queryBuilder, options) {
2997
3018
  else {
2998
3019
  const columnType = pgPropertyTypeColumnMap[options.type];
2999
3020
  if (!columnType) {
3000
- console.log('options', options);
3001
3021
  throw new Error(`Property type "${options.type}" is not supported.`);
3002
3022
  }
3003
3023
  columnDDL += ` ${columnType}`;
@@ -3035,22 +3055,19 @@ const pgPropertyTypeColumnMap = {
3035
3055
  };
3036
3056
 
3037
3057
  function mergeInput(defaultInput, input, fixedInput) {
3038
- return ___namespace.mergeWith({}, defaultInput, input, fixedInput, doNotMergeArray);
3058
+ return lodash.mergeWith({}, defaultInput, input, fixedInput, doNotMergeArray);
3039
3059
  }
3040
3060
  function doNotMergeArray(objValue, srcValue) {
3041
- if (___namespace.isArray(srcValue)) {
3061
+ if (lodash.isArray(srcValue)) {
3042
3062
  return srcValue;
3043
3063
  }
3044
3064
  }
3045
3065
 
3046
3066
  async function runCollectionEntityActionHandler(ctx, options, code, handleEntityAction) {
3047
- const { server, input } = ctx;
3067
+ const { logger, server, input } = ctx;
3048
3068
  const { defaultInput, fixedInput } = options;
3049
- console.debug(`Running ${code} handler...`);
3050
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3051
3069
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3052
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3053
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3070
+ logger.debug(`Running ${code} handler...`, { defaultInput, fixedInput, mergedInput });
3054
3071
  const entityManager = server.getEntityManager(options.singularCode);
3055
3072
  const result = handleEntityAction(entityManager, mergedInput);
3056
3073
  if (result instanceof Promise) {
@@ -3122,8 +3139,8 @@ var findCollectionEntities = /*#__PURE__*/Object.freeze({
3122
3139
 
3123
3140
  const code$g = "findCollectionEntityById";
3124
3141
  async function handler$g(plugin, ctx, options) {
3125
- console.debug(`Running ${code$g} handler...`);
3126
- const { server, input } = ctx;
3142
+ const { logger, server, input } = ctx;
3143
+ logger.debug(`Running ${code$g} handler...`, { input });
3127
3144
  const { id } = input;
3128
3145
  const entityManager = server.getEntityManager(options.singularCode);
3129
3146
  const entity = await entityManager.findById(id);
@@ -3155,13 +3172,10 @@ var countCollectionEntities = /*#__PURE__*/Object.freeze({
3155
3172
 
3156
3173
  const code$e = "createCollectionEntity";
3157
3174
  async function handler$e(plugin, ctx, options) {
3158
- const { server, input } = ctx;
3175
+ const { logger, server, input } = ctx;
3159
3176
  const { defaultInput, fixedInput } = options;
3160
- console.debug(`Running ${code$e} handler...`);
3161
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3162
3177
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3163
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3164
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3178
+ logger.debug(`Running ${code$e} handler...`, { defaultInput, fixedInput, mergedInput });
3165
3179
  const userId = ctx.routerContext.state?.userId;
3166
3180
  if (userId) {
3167
3181
  input.createdBy = userId;
@@ -3181,19 +3195,16 @@ var createCollectionEntity = /*#__PURE__*/Object.freeze({
3181
3195
 
3182
3196
  const code$d = "createCollectionEntitiesBatch";
3183
3197
  async function handler$d(plugin, ctx, options) {
3184
- const { server, input } = ctx;
3198
+ const { logger, server, input } = ctx;
3185
3199
  const { defaultInput, fixedInput } = options;
3186
- console.debug(`Running ${code$d} handler...`);
3200
+ logger.debug(`Running ${code$d} handler...`, { defaultInput, fixedInput, input });
3187
3201
  const { entities } = input;
3188
- if (!_.isArray(entities)) {
3202
+ if (!lodash.isArray(entities)) {
3189
3203
  throw new Error("input.entities should be an array.");
3190
3204
  }
3191
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3192
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3193
3205
  const output = [];
3194
3206
  for (const entity of entities) {
3195
3207
  const mergedEntity = mergeInput(defaultInput?.entity || {}, entity, fixedInput?.entity);
3196
- console.debug(`mergedEntity: ${JSON.stringify(mergedEntity)}`);
3197
3208
  const userId = ctx.routerContext.state?.userId;
3198
3209
  if (userId) {
3199
3210
  mergedEntity.createdBy = userId;
@@ -3215,13 +3226,10 @@ var createCollectionEntitiesBatch = /*#__PURE__*/Object.freeze({
3215
3226
 
3216
3227
  const code$c = "updateCollectionEntityById";
3217
3228
  async function handler$c(plugin, ctx, options) {
3218
- const { server, input } = ctx;
3229
+ const { logger, server, input } = ctx;
3219
3230
  const { defaultInput, fixedInput } = options;
3220
- console.debug(`Running ${code$c} handler...`);
3221
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3222
3231
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3223
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3224
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3232
+ logger.debug(`Running ${code$c} handler...`, { defaultInput, fixedInput, mergedInput });
3225
3233
  const entityManager = server.getEntityManager(options.singularCode);
3226
3234
  const output = await entityManager.updateEntityById({ id: mergedInput.id, entityToSave: mergedInput }, plugin);
3227
3235
  ctx.output = output;
@@ -3235,8 +3243,8 @@ var updateCollectionEntityById = /*#__PURE__*/Object.freeze({
3235
3243
 
3236
3244
  const code$b = "deleteCollectionEntityById";
3237
3245
  async function handler$b(plugin, ctx, options) {
3238
- console.debug(`Running ${code$b} handler...`);
3239
- const { server, input } = ctx;
3246
+ const { logger, server, input } = ctx;
3247
+ logger.debug(`Running ${code$b} handler...`);
3240
3248
  const entityManager = server.getEntityManager(options.singularCode);
3241
3249
  await entityManager.deleteById(input.id, plugin);
3242
3250
  ctx.status = 200;
@@ -3251,13 +3259,10 @@ var deleteCollectionEntityById = /*#__PURE__*/Object.freeze({
3251
3259
 
3252
3260
  const code$a = "addEntityRelations";
3253
3261
  async function handler$a(plugin, ctx, options) {
3254
- const { server, input } = ctx;
3262
+ const { logger, server, input } = ctx;
3255
3263
  const { defaultInput, fixedInput } = options;
3256
- console.debug(`Running ${code$a} handler...`);
3257
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3258
3264
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3259
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3260
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3265
+ logger.debug(`Running ${code$a} handler...`, { defaultInput, fixedInput, mergedInput });
3261
3266
  const entityManager = server.getEntityManager(options.singularCode);
3262
3267
  await entityManager.addRelations(mergedInput, plugin);
3263
3268
  ctx.output = {};
@@ -3271,13 +3276,10 @@ var addEntityRelations = /*#__PURE__*/Object.freeze({
3271
3276
 
3272
3277
  const code$9 = "removeEntityRelations";
3273
3278
  async function handler$9(plugin, ctx, options) {
3274
- const { server, input } = ctx;
3279
+ const { logger, server, input } = ctx;
3275
3280
  const { defaultInput, fixedInput } = options;
3276
- console.debug(`Running ${code$9} handler...`);
3277
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3278
3281
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3279
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3280
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3282
+ logger.debug(`Running ${code$9} handler...`, { defaultInput, fixedInput, mergedInput });
3281
3283
  const entityManager = server.getEntityManager(options.singularCode);
3282
3284
  await entityManager.removeRelations(mergedInput, plugin);
3283
3285
  ctx.output = {};
@@ -3291,16 +3293,13 @@ var removeEntityRelations = /*#__PURE__*/Object.freeze({
3291
3293
 
3292
3294
  const code$8 = "queryDatabase";
3293
3295
  async function handler$8(plugin, ctx, options) {
3294
- const { server, input } = ctx;
3296
+ const { logger, server, input } = ctx;
3295
3297
  const { sql, querySingle, defaultInput, fixedInput } = options;
3296
- console.debug(`Running ${code$8} handler...`);
3297
- console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
3298
3298
  const mergedInput = mergeInput(defaultInput, input, fixedInput);
3299
- console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
3300
- console.debug(`mergedInput: ${JSON.stringify(mergedInput)}`);
3299
+ logger.debug(`Running ${code$8} handler...`, { defaultInput, fixedInput, mergedInput });
3301
3300
  const result = await server.queryDatabaseObject(sql, mergedInput);
3302
3301
  if (querySingle) {
3303
- ctx.output = ___namespace.first(result);
3302
+ ctx.output = lodash.first(result);
3304
3303
  }
3305
3304
  else {
3306
3305
  ctx.output = result;
@@ -3390,10 +3389,6 @@ class DataManager {
3390
3389
  get configurations() {
3391
3390
  return [];
3392
3391
  }
3393
- async initPlugin(server) {
3394
- }
3395
- async registerMiddlewares(server) {
3396
- }
3397
3392
  async registerActionHandlers(server) {
3398
3393
  server.registerActionHandler(this, findCollectionEntities);
3399
3394
  server.registerActionHandler(this, findCollectionEntityById);
@@ -3406,18 +3401,6 @@ class DataManager {
3406
3401
  server.registerActionHandler(this, deleteCollectionEntityById);
3407
3402
  server.registerActionHandler(this, queryDatabase);
3408
3403
  }
3409
- async registerEventHandlers(server) {
3410
- }
3411
- async registerMessageHandlers(server) {
3412
- }
3413
- async registerTaskProcessors(server) {
3414
- }
3415
- async onLoadingApplication(server, applicationConfig) {
3416
- }
3417
- async configureModels(server, applicationConfig) {
3418
- }
3419
- async configureModelProperties(server, applicationConfig) {
3420
- }
3421
3404
  async configureRoutes(server, applicationConfig) {
3422
3405
  const { models } = applicationConfig;
3423
3406
  const routes = [];
@@ -3445,11 +3428,6 @@ class DataManager {
3445
3428
  });
3446
3429
  server.appendApplicationConfig({ routes });
3447
3430
  }
3448
- async onApplicationLoaded(server, applicationConfig) {
3449
- console.log("[dataManager.onApplicationLoaded]");
3450
- }
3451
- async onApplicationReady(server, applicationConfig) {
3452
- }
3453
3431
  }
3454
3432
 
3455
3433
  async function fetchWithTimeout(url, reqInit, timeout) {
@@ -3504,7 +3482,8 @@ async function sendSourceResponse(proxyCtx, targetRes) {
3504
3482
 
3505
3483
  const code$7 = "httpProxy";
3506
3484
  async function handler$7(plugin, ctx, options) {
3507
- console.debug(`Running ${code$7} handler...`);
3485
+ const { logger } = ctx;
3486
+ logger.debug(`Running ${code$7} handler...`);
3508
3487
  await doProxy(ctx.routerContext, options);
3509
3488
  }
3510
3489
 
@@ -3533,10 +3512,6 @@ class RouteManager {
3533
3512
  get configurations() {
3534
3513
  return [];
3535
3514
  }
3536
- async initPlugin(server) {
3537
- }
3538
- async registerMiddlewares(server) {
3539
- }
3540
3515
  async registerActionHandlers(server) {
3541
3516
  server.registerActionHandler(this, httpProxy);
3542
3517
  }
@@ -3546,18 +3521,10 @@ class RouteManager {
3546
3521
  // server.registerEventHandler("entity.update", handleEntityEvent.bind(null, server))
3547
3522
  // server.registerEventHandler("entity.delete", handleEntityEvent.bind(null, server))
3548
3523
  }
3549
- async registerMessageHandlers(server) {
3550
- }
3551
- async registerTaskProcessors(server) {
3552
- }
3553
- async onLoadingApplication(server, applicationConfig) {
3554
- }
3555
- async configureModels(server, applicationConfig) {
3556
- }
3557
- async configureModelProperties(server, applicationConfig) {
3558
- }
3559
3524
  async configureRoutes(server, applicationConfig) {
3525
+ const logger = server.getLogger();
3560
3526
  try {
3527
+ logger.info("Loading meta of routes...");
3561
3528
  const entityManager = server.getEntityManager("route");
3562
3529
  const routes = await entityManager.findEntities({
3563
3530
  orderBy: [
@@ -3566,15 +3533,10 @@ class RouteManager {
3566
3533
  });
3567
3534
  applicationConfig.routes.push(...routes);
3568
3535
  }
3569
- catch (ex) {
3570
- console.warn("Failed to loading existing meta of routes.", ex.message);
3536
+ catch (error) {
3537
+ logger.crit("Failed to load meta of routes.", { error });
3571
3538
  }
3572
3539
  }
3573
- async onApplicationLoaded(server, applicationConfig) {
3574
- console.log("[routeManager.onApplicationLoaded] onApplicationLoaded");
3575
- }
3576
- async onApplicationReady(server, applicationConfig) {
3577
- }
3578
3540
  }
3579
3541
 
3580
3542
  var pluginConfig = {
@@ -3654,16 +3616,23 @@ var pluginConfig = {
3654
3616
  * Webhooks plugin
3655
3617
  */
3656
3618
  function listWebhooks(server) {
3657
- const entityManager = server.getEntityManager("webhook");
3658
- return entityManager.findEntities({
3659
- filters: [
3660
- {
3661
- field: "enabled",
3662
- operator: "eq",
3663
- value: true,
3664
- },
3665
- ],
3666
- });
3619
+ const logger = server.getLogger();
3620
+ logger.info("Loading meta of webhooks...");
3621
+ try {
3622
+ const entityManager = server.getEntityManager("webhook");
3623
+ return entityManager.findEntities({
3624
+ filters: [
3625
+ {
3626
+ field: "enabled",
3627
+ operator: "eq",
3628
+ value: true,
3629
+ },
3630
+ ],
3631
+ });
3632
+ }
3633
+ catch (error) {
3634
+ logger.crit("Failed to load meta of webhooks.", { error });
3635
+ }
3667
3636
  }
3668
3637
  class WebhooksPlugin {
3669
3638
  #webhooks;
@@ -3685,12 +3654,6 @@ class WebhooksPlugin {
3685
3654
  get configurations() {
3686
3655
  return [];
3687
3656
  }
3688
- async initPlugin(server) {
3689
- }
3690
- async registerMiddlewares(server) {
3691
- }
3692
- async registerActionHandlers(server) {
3693
- }
3694
3657
  async registerEventHandlers(server) {
3695
3658
  const events = [
3696
3659
  "entity.create",
@@ -3701,12 +3664,6 @@ class WebhooksPlugin {
3701
3664
  server.registerEventHandler(event, this.handleEntityEvent.bind(this, server, event));
3702
3665
  }
3703
3666
  }
3704
- async registerMessageHandlers(server) {
3705
- }
3706
- async registerTaskProcessors(server) {
3707
- }
3708
- async onLoadingApplication(server, applicationConfig) {
3709
- }
3710
3667
  async configureModels(server, applicationConfig) {
3711
3668
  server.appendApplicationConfig({
3712
3669
  models: pluginConfig.models,
@@ -3717,7 +3674,6 @@ class WebhooksPlugin {
3717
3674
  async configureRoutes(server, applicationConfig) {
3718
3675
  }
3719
3676
  async onApplicationLoaded(server, applicationConfig) {
3720
- console.log("[webhooks.onApplicationLoaded] loading webhooks");
3721
3677
  this.#webhooks = await listWebhooks(server);
3722
3678
  }
3723
3679
  async onApplicationReady(server, applicationConfig) {
@@ -3734,16 +3690,18 @@ class WebhooksPlugin {
3734
3690
  if (payload.namespace === "meta" || payload.namespace === "sys") {
3735
3691
  return;
3736
3692
  }
3693
+ const logger = server.getLogger();
3737
3694
  for (const webhook of this.#webhooks) {
3738
- if (___namespace.indexOf(webhook.events, event) === -1) {
3695
+ if (lodash.indexOf(webhook.events, event) === -1) {
3739
3696
  continue;
3740
3697
  }
3741
3698
  if (webhook.namespace != payload.namespace ||
3742
3699
  webhook.modelSingularCode !== payload.modelSingularCode) {
3743
3700
  continue;
3744
3701
  }
3745
- console.debug(`Triggering webhook. ${webhook.url}`);
3702
+ logger.debug(`Triggering webhook. ${webhook.url}`);
3746
3703
  // TODO: It's better to trigger webhook through message queue.
3704
+ const requestBody = { event, payload };
3747
3705
  try {
3748
3706
  await fetchWithTimeout(webhook.url, {
3749
3707
  method: "post",
@@ -3751,12 +3709,11 @@ class WebhooksPlugin {
3751
3709
  "Content-Type": "application/json",
3752
3710
  "x-webhook-secret": webhook.secret || "",
3753
3711
  },
3754
- body: JSON.stringify({ event, payload }),
3712
+ body: JSON.stringify(requestBody),
3755
3713
  });
3756
3714
  }
3757
- catch (err) {
3758
- console.warn(new Error("Failed to call webhook. " + err.message));
3759
- console.warn(err);
3715
+ catch (error) {
3716
+ logger.error("Failed to call webhook.", { error, webhookUrl: webhook.url, requestBody });
3760
3717
  }
3761
3718
  }
3762
3719
  }
@@ -3991,36 +3948,17 @@ class AuthPlugin {
3991
3948
  get configurations() {
3992
3949
  return [];
3993
3950
  }
3994
- async initPlugin(server) {
3995
- }
3996
- async registerMiddlewares(server) {
3997
- }
3998
3951
  async registerActionHandlers(server) {
3999
3952
  for (const actionHandler of pluginActionHandlers$1) {
4000
3953
  server.registerActionHandler(this, actionHandler);
4001
3954
  }
4002
3955
  }
4003
- async registerEventHandlers(server) {
4004
- }
4005
- async registerMessageHandlers(server) {
4006
- }
4007
- async registerTaskProcessors(server) {
4008
- }
4009
- async onLoadingApplication(server, applicationConfig) {
4010
- }
4011
3956
  async configureModels(server, applicationConfig) {
4012
3957
  server.appendApplicationConfig({ models: pluginModels });
4013
3958
  }
4014
- async configureModelProperties(server, applicationConfig) {
4015
- }
4016
3959
  async configureRoutes(server, applicationConfig) {
4017
3960
  server.appendApplicationConfig({ routes: pluginRoutes$1 });
4018
3961
  }
4019
- async onApplicationLoaded(server, applicationConfig) {
4020
- console.log("authManager.onApplicationLoaded");
4021
- }
4022
- async onApplicationReady(server, applicationConfig) {
4023
- }
4024
3962
  async onPrepareRouteContext(server, routeContext) {
4025
3963
  const request = routeContext.request;
4026
3964
  let token;
@@ -4043,8 +3981,9 @@ class AuthPlugin {
4043
3981
  routeContext.state.userId = tokenPayload.aud;
4044
3982
  routeContext.state.userLogin = tokenPayload.act;
4045
3983
  }
4046
- catch (err) {
4047
- console.warn(err);
3984
+ catch (error) {
3985
+ const logger = server.getLogger();
3986
+ logger.debug("Verify JWT failed.", { error });
4048
3987
  }
4049
3988
  }
4050
3989
  }
@@ -4136,7 +4075,7 @@ const code$1 = "uploadFile";
4136
4075
  async function handler$1(plugin, ctx, options) {
4137
4076
  const { server, applicationConfig, routerContext, input } = ctx;
4138
4077
  let file = input.file || input.files;
4139
- if (_.isArray(file)) {
4078
+ if (lodash.isArray(file)) {
4140
4079
  file = file[0];
4141
4080
  }
4142
4081
  if (!file) {
@@ -4225,35 +4164,14 @@ class FileManager {
4225
4164
  get configurations() {
4226
4165
  return [];
4227
4166
  }
4228
- async initPlugin(server) {
4229
- }
4230
- async registerMiddlewares(server) {
4231
- }
4232
4167
  async registerActionHandlers(server) {
4233
4168
  server.registerActionHandler(this, downloadDocumentActionHandler);
4234
4169
  server.registerActionHandler(this, downloadFileActionHandler);
4235
4170
  server.registerActionHandler(this, uploadFileActionHandler);
4236
4171
  }
4237
- async registerEventHandlers(server) {
4238
- }
4239
- async registerMessageHandlers(server) {
4240
- }
4241
- async registerTaskProcessors(server) {
4242
- }
4243
- async onLoadingApplication(server, applicationConfig) {
4244
- }
4245
- async configureModels(server, applicationConfig) {
4246
- }
4247
- async configureModelProperties(server, applicationConfig) {
4248
- }
4249
4172
  async configureRoutes(server, applicationConfig) {
4250
4173
  server.appendApplicationConfig({ routes: pluginRoutes });
4251
4174
  }
4252
- async onApplicationLoaded(server, applicationConfig) {
4253
- console.log("fileManager.onApplicationLoaded");
4254
- }
4255
- async onApplicationReady(server, applicationConfig) {
4256
- }
4257
4175
  }
4258
4176
 
4259
4177
  const code = "runServerOperation";
@@ -4421,10 +4339,116 @@ class EntityWatchPlugin {
4421
4339
  }
4422
4340
  }
4423
4341
 
4342
+ function isAccessAllowed(policy, allowedActions) {
4343
+ let isAnyCheckPassed = true;
4344
+ let isAllCheckPassed = true;
4345
+ if (policy.any) {
4346
+ isAnyCheckPassed = false;
4347
+ for (const action of policy.any) {
4348
+ if (lodash.find(allowedActions, item => item === action) != null) {
4349
+ isAnyCheckPassed = true;
4350
+ break;
4351
+ }
4352
+ }
4353
+ }
4354
+ if (policy.all) {
4355
+ isAllCheckPassed = true;
4356
+ for (const action of policy.all) {
4357
+ if (lodash.find(allowedActions, item => item === action) == null) {
4358
+ isAnyCheckPassed = false;
4359
+ break;
4360
+ }
4361
+ }
4362
+ }
4363
+ return isAnyCheckPassed && isAllCheckPassed;
4364
+ }
4365
+
4366
+ class EntityAccessControlPlugin {
4367
+ constructor() {
4368
+ }
4369
+ get code() {
4370
+ return "entityAccessControl";
4371
+ }
4372
+ get description() {
4373
+ return "";
4374
+ }
4375
+ get extendingAbilities() {
4376
+ return [];
4377
+ }
4378
+ get configurableTargets() {
4379
+ return [];
4380
+ }
4381
+ get configurations() {
4382
+ return [];
4383
+ }
4384
+ async onLoadingApplication(server, applicationConfig) {
4385
+ const properties = [
4386
+ {
4387
+ name: "permissionPolicies",
4388
+ code: "permissionPolicies",
4389
+ columnName: "permission_policies",
4390
+ type: "json",
4391
+ },
4392
+ ];
4393
+ server.appendModelProperties("model", properties);
4394
+ }
4395
+ async configureRoutes(server, applicationConfig) {
4396
+ const logger = server.getLogger();
4397
+ logger.info("Configuring entity access checking policies...");
4398
+ const model = lodash.find(applicationConfig.models, (item) => item.singularCode === "model");
4399
+ if (!model) {
4400
+ return;
4401
+ }
4402
+ const { permissionPolicies } = model;
4403
+ if (!permissionPolicies) {
4404
+ return;
4405
+ }
4406
+ const routes = applicationConfig.routes;
4407
+ for (const route of routes) {
4408
+ const { actions } = route;
4409
+ if (!actions) {
4410
+ continue;
4411
+ }
4412
+ for (const action of route.actions) {
4413
+ if (action.code === "findCollectionEntityById") {
4414
+ if (permissionPolicies.find) {
4415
+ lodash.set(action, "config.permissionPolicy", permissionPolicies.find);
4416
+ }
4417
+ }
4418
+ }
4419
+ }
4420
+ }
4421
+ async onPrepareRouteContext(server, routeContext) {
4422
+ const userId = routeContext.state.userId;
4423
+ if (!userId) {
4424
+ return;
4425
+ }
4426
+ const actions = await server.queryDatabaseObject(`select distinct a.* from sys_actions a
4427
+ inner join oc_role_sys_action_links ra on a.id = ra.action_id
4428
+ inner join oc_role_user_links ru on ru.role_id = ra.role_id
4429
+ where ru.user_id = $1;`, [userId]);
4430
+ routeContext.state.allowedActions = actions.map(item => item.code);
4431
+ }
4432
+ async beforeRunRouteActions(server, handlerContext) {
4433
+ // Check permission
4434
+ const { routerContext } = handlerContext;
4435
+ const { routeConfig } = routerContext;
4436
+ for (const actionConfig of routeConfig.actions) {
4437
+ const permissionPolicy = actionConfig.config?.permissionPolicy;
4438
+ if (permissionPolicy) {
4439
+ if (!isAccessAllowed(permissionPolicy, routerContext.state.allowedActions || [])) {
4440
+ throw new Error(`Your action of '${actionConfig.code}' is not permitted.`);
4441
+ }
4442
+ }
4443
+ }
4444
+ }
4445
+ }
4446
+
4424
4447
  fixBigIntJSONSerialize();
4425
4448
 
4426
4449
  exports.AuthPlugin = AuthPlugin;
4427
4450
  exports.DataManagePlugin = DataManager;
4451
+ exports.EntityAccessControlPlugin = EntityAccessControlPlugin;
4428
4452
  exports.EntityWatchPlugin = EntityWatchPlugin;
4429
4453
  exports.FileManagePlugin = FileManager;
4430
4454
  exports.GlobalRequest = GlobalRequest;