@modular-rest/server 1.11.4 → 1.11.6

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.
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modular-rest/server",
3
- "version": "1.11.4",
3
+ "version": "1.11.6",
4
4
  "description": "a nodejs module based on KOAJS for developing Rest-APIs in a modular solution.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -32,8 +32,9 @@
32
32
  "keypair": "^1.0.4",
33
33
  "koa": "^2.5.3",
34
34
  "koa-body": "^4.2.0",
35
+ "koa-mount": "^4.0.0",
35
36
  "koa-router": "^7.4.0",
36
- "koa-static-server": "^1.5.2",
37
+ "koa-static": "^5.0.0",
37
38
  "mongoose": "^5.10.9",
38
39
  "nested-property": "^4.0.0"
39
40
  },
@@ -43,4 +44,4 @@
43
44
  "@types/koa__cors": "^5.0.0",
44
45
  "typescript": "^5.3.3"
45
46
  }
46
- }
47
+ }
@@ -1,7 +1,8 @@
1
1
  const koa = require("koa");
2
2
  const cors = require("@koa/cors");
3
3
  const koaBody = require("koa-body");
4
- const koaStatic = require("koa-static-server");
4
+ const koaStatic = require("koa-static");
5
+ const mount = require("koa-mount");
5
6
  const path = require("path");
6
7
  const Combination = require("./class/combinator");
7
8
  const DataProvider = require("./services/data_provider/service");
@@ -13,8 +14,8 @@ const defaultServiceRoot = __dirname + "/services";
13
14
  * @typedef {import('koa')} Koa
14
15
  * @typedef {import('http').Server} server
15
16
  * @typedef {import('@koa/cors').Options} Cors
16
- * @typedef {import('./class/security').PermissionGroup} PermissionGroup
17
- * @typedef {import('./class/database_trigger.js')} DatabaseTrigger
17
+ * @typedef {import('./class/security.js').PermissionGroup} PermissionGroup
18
+ * @typedef {import('./class/cms_trigger.js')} CmsTrigger
18
19
  */
19
20
 
20
21
  const { config, setConfig } = require("./config");
@@ -25,18 +26,19 @@ const { config, setConfig } = require("./config");
25
26
  * cors?: Cors; // CORS options.
26
27
  * modulesPath?: string; // Root directory of your router.js/db.js files.
27
28
  * uploadDirectory?: string; // Root directory of your uploaded files.
28
- * staticPath?: {
29
- * rootDir?: string; // Root directory of your static files.
30
- * rootPath?: string; // Root path of your static files.
31
- * notFoundFile?: string; // Not found file.
32
- * log?: boolean; // Log requests to console.
33
- * last?: boolean; // Don't execute any downstream middleware.
34
- * maxage?: number; // Browser cache max-age in milliseconds.
35
- * hidden?: boolean; // Allow transfer of hidden files.
36
- * gzip?: boolean; // Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested file exists.
37
- * brotli?: boolean; // Try to serve the brotli version of a file automatically when brotli is supported by a client and if the requested file exists.
38
- * index?: string; // Index file.
39
- * };
29
+ * koaBodyOptions?: object; // Options for koa-body.
30
+ * staticPath?: {
31
+ * rootDir: string; // Root directory of your static files.
32
+ * rootPath: string; // Root path of your static files, defaults to '/assets'.
33
+ * maxage?: number; // Browser cache max-age in milliseconds. Defaults to 0.
34
+ * hidden?: boolean; // Allow transfer of hidden files. Defaults to false.
35
+ * index?: string; // Default file name. Defaults to 'index.html'.
36
+ * defer?: boolean; // If true, serves after return next(), allowing any downstream middleware to respond first. Defaults to false.
37
+ * gzip?: boolean; // Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested file with .gz extension exists. Defaults to true.
38
+ * br?: boolean; // Try to serve the brotli version of a file automatically when brotli is supported by a client and if the requested file with .br extension exists. Note that brotli is only accepted over https. Defaults to false.
39
+ * setHeaders?: Function; // Function to set custom headers on response.
40
+ * extensions?: boolean|Array; // Try to match extensions from passed array to search for file when no extension is suffixed in URL. First found is served. Defaults to false.
41
+ * };
40
42
  * onBeforeInit?: (koaApp:Koa) => void; // A callback called before initializing the Koa server.
41
43
  * onAfterInit?: (koaApp:Koa) => void; // A callback called after server initialization.
42
44
  * port?: number; // Server port.
@@ -57,7 +59,8 @@ const { config, setConfig } = require("./config");
57
59
  * verificationCodeGeneratorMethod: () => string; // A method to return a verification code when registering a new user.
58
60
  * collectionDefinitions?: CollectionDefinition[]; // An array of additional collection definitions.
59
61
  * permissionGroups?: PermissionGroup[]; // An array of additional permission groups.
60
- * authTriggers?: DatabaseTrigger[]; // An array of additional database triggers for the auth collection.
62
+ * authTriggers?: CmsTrigger[]; // An array of additional database triggers for the auth collection.
63
+ * fileTriggers?: CmsTrigger[]; // An array of additional database triggers for the auth collection.
61
64
  * }} options
62
65
  * @returns {Promise<{app: Koa, server: Server}>}
63
66
  */
@@ -90,6 +93,7 @@ async function createRest(options) {
90
93
  */
91
94
  const bodyParserOptions = {
92
95
  multipart: true,
96
+ ...(config.koaBodyOptions || {}),
93
97
  };
94
98
  app.use(koaBody(bodyParserOptions));
95
99
 
@@ -97,7 +101,18 @@ async function createRest(options) {
97
101
  * Plug In KoaStatic
98
102
  */
99
103
  if (config.staticPath) {
100
- app.use(koaStatic(config.staticPath));
104
+ const defaultStaticPath = config.staticPath.rootDir;
105
+ const defaultStaticRootPath = config.staticPath.rootPath || "/assets";
106
+
107
+ delete config.staticPath.rootDir;
108
+ delete config.staticPath.rootPath;
109
+
110
+ app.use(
111
+ mount(
112
+ defaultStaticRootPath,
113
+ koaStatic(defaultStaticPath, config.staticPath)
114
+ )
115
+ );
101
116
  }
102
117
 
103
118
  /**
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `CmsTrigger` is a class that defines a callback to be called on a specific database transaction.
3
+ *
4
+ * @class
5
+ */
6
+ class CmsTrigger {
7
+ /**
8
+ * Creates a new instance of `CmsTrigger`.
9
+ *
10
+ * @param {'update-one' | 'insert-one' | 'remove-one' } operation - The operation to be triggered.
11
+ * @param {function({query: any, queryResult: any}): void} [callback=(context) => {}] - The callback to be called when the operation is executed. The callback function takes an object as parameter with two properties: 'query' and 'queryResult'.
12
+ * @constructor
13
+ */
14
+ constructor(operation, callback = (context) => {}) {
15
+ this.operation = operation;
16
+ this.callback = callback;
17
+ }
18
+ }
19
+
20
+ module.exports = CmsTrigger;
@@ -11,6 +11,7 @@ module.exports = {
11
11
  // Tag being used as the parent dir for files
12
12
  // uploadDir/$format/$tag/timestamp.format
13
13
  tag: String,
14
+ size: Number,
14
15
  },
15
16
  { timestamps: true }
16
17
  ),
@@ -6,8 +6,9 @@
6
6
  * @returns {Object} - An object containing pagination information.
7
7
  */
8
8
  function create(count, perPage, page) {
9
- let totalPgaes = Math.ceil(count / perPage);
10
- if (page > totalPgaes) page = 1;
9
+ const totalPages = Math.ceil(count / perPage);
10
+
11
+ if (page > totalPages) page = 1;
11
12
 
12
13
  let from = 0;
13
14
  if (perPage == 1) from = page - 1;
@@ -16,7 +17,7 @@ function create(count, perPage, page) {
16
17
  if (page <= 1) from = 0;
17
18
 
18
19
  let result = {
19
- 'pages': totalPgaes,
20
+ 'pages': totalPages,
20
21
  'page': page,
21
22
  'from': from,
22
23
  'to': perPage
@@ -9,7 +9,7 @@ function create(status, detail = {}) {
9
9
 
10
10
  let result = detail || {};
11
11
 
12
- // defin status
12
+ // define status
13
13
  switch (status) {
14
14
  case 's':
15
15
  result['status'] = 'success';
@@ -8,13 +8,13 @@
8
8
  function validate(obj, requiredFields) {
9
9
  /*
10
10
  this method could validate an Object by given field's name list and return bool.
11
- - requiredFields: is a string that contains keys being spareted by " ".
11
+ - requiredFields: is a string that contains keys being spared by " ".
12
12
  */
13
13
  let type = typeof requiredFields;
14
14
  let result;
15
15
 
16
16
  if (type == 'string')
17
- result = ckeckSimple(obj, requiredFields);
17
+ result = checkSimple(obj, requiredFields);
18
18
  else if (type == 'object')
19
19
  result = checkComplex(obj, requiredFields);
20
20
 
@@ -25,7 +25,7 @@ function validate(obj, requiredFields) {
25
25
 
26
26
  module.exports = validate;
27
27
 
28
- function ckeckSimple(obj, requiredFields = '') {
28
+ function checkSimple(obj, requiredFields = '') {
29
29
  let isValide = false;
30
30
  let requires = requiredFields.split(' ');
31
31
 
package/src/config.js CHANGED
@@ -1,27 +1,28 @@
1
1
  /**
2
2
  * @typedef {import('koa')} Koa
3
3
  * @typedef {import('@koa/cors').Options} Cors
4
- * @typedef {import('./class/collection_definition')} CollectionDefinition
5
- * @typedef {import('./class/security').PermissionGroup} PermissionGroup
6
- * @typedef {import('./class/database_trigger.js')} DatabaseTrigger
4
+ * @typedef {import('./class/collection_definition.js')} CollectionDefinition
5
+ * @typedef {import('./class/security.js').PermissionGroup} PermissionGroup
6
+ * @typedef {import('./class/cms_trigger.js')} CmsTrigger
7
7
  */
8
8
 
9
9
  /**
10
10
  * @typedef {{
11
11
  * cors?: Cors; // CORS options.
12
12
  * modulesPath?: string; // Root directory of your router.js/db.js files.
13
- * staticPath?: {
14
- * rootDir?: string; // Root directory of your static files.
15
- * rootPath?: string; // Root path of your static files.
16
- * notFoundFile?: string; // Not found file.
17
- * log?: boolean; // Log requests to console.
18
- * last?: boolean; // Don't execute any downstream middleware.
19
- * maxage?: number; // Browser cache max-age in milliseconds.
20
- * hidden?: boolean; // Allow transfer of hidden files.
21
- * gzip?: boolean; // Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested file exists.
22
- * brotli?: boolean; // Try to serve the brotli version of a file automatically when brotli is supported by a client and if the requested file exists.
23
- * index?: string; // Index file.
24
- * };
13
+ * koaBodyOptions?: object; // Options for koa-body.
14
+ * staticPath?: {
15
+ * rootDir: string; // Root directory of your static files.
16
+ * rootPath: string; // Root path of your static files, defaults to '/assets'.
17
+ * maxage?: number; // Browser cache max-age in milliseconds. Defaults to 0.
18
+ * hidden?: boolean; // Allow transfer of hidden files. Defaults to false.
19
+ * index?: string; // Default file name. Defaults to 'index.html'.
20
+ * defer?: boolean; // If true, serves after return next(), allowing any downstream middleware to respond first. Defaults to false.
21
+ * gzip?: boolean; // Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested file with .gz extension exists. Defaults to true.
22
+ * br?: boolean; // Try to serve the brotli version of a file automatically when brotli is supported by a client and if the requested file with .br extension exists. Note that brotli is only accepted over https. Defaults to false.
23
+ * setHeaders?: Function; // Function to set custom headers on response.
24
+ * extensions?: boolean|Array; // Try to match extensions from passed array to search for file when no extension is suffixed in URL. First found is served. Defaults to false.
25
+ * };
25
26
  * onBeforeInit?: (koaApp:Koa) => void; // A callback called before initializing the Koa server.
26
27
  * onAfterInit?: (koaApp:Koa) => void; // A callback called after server initialization.
27
28
  * port?: number; // Server port.
@@ -43,6 +44,7 @@
43
44
  * collectionDefinitions?: CollectionDefinition[]; // An array of additional collection definitions.
44
45
  * permissionGroups?: PermissionGroup[]; // An array of additional permission groups.
45
46
  * authTriggers?: DatabaseTrigger[]; // An array of additional database triggers for the auth collection.
47
+ * fileTriggers?: CmsTrigger[]; // An array of additional database triggers for the auth collection.
46
48
  * }} Config
47
49
  * @exports Config
48
50
  */
package/src/index.js CHANGED
@@ -9,36 +9,58 @@ const validator = require("./class/validator");
9
9
  const { getCollection } = require("./services/data_provider/service");
10
10
  const { defineFunction } = require("./services/functions/service");
11
11
  const TypeCasters = require("./services/data_provider/typeCasters");
12
+ const userManager = require("./services/user_manager/service");
13
+ const {
14
+ getFile,
15
+ getFileLink,
16
+ getFilePath,
17
+ removeFile,
18
+ storeFile,
19
+ } = require("./services/file/service");
12
20
 
13
21
  // Base class
14
22
  const CollectionDefinition = require("./class/collection_definition");
15
23
  const Schemas = require("./class/db_schemas");
16
24
  const DatabaseTrigger = require("./class/database_trigger");
25
+ const CmsTrigger = require("./class/cms_trigger");
17
26
  const SecurityClass = require("./class/security");
18
27
  const middleware = require("./middlewares");
19
- const userManager = require("./services/user_manager/service");
20
28
 
21
29
  module.exports = {
22
30
  createRest,
23
31
 
24
- // Route utilities
25
- reply,
26
- TypeCasters,
27
- paginator,
28
- validator,
29
-
30
- // Service utilities
31
- getCollection,
32
- defineFunction,
33
-
34
32
  // Database
35
33
  CollectionDefinition,
36
34
  Schemas,
37
35
  Schema,
38
36
  DatabaseTrigger,
37
+ CmsTrigger,
39
38
  ...SecurityClass,
40
39
 
41
- // Middlewares
40
+ // Function
41
+ defineFunction,
42
+
43
+ // Private utilities
44
+ TypeCasters,
45
+ validator,
46
+
47
+ // Route utilities
48
+ reply,
49
+ paginator,
50
+
51
+ // Database utilities
52
+ getCollection,
53
+
54
+ // File Utilities
55
+ getFile,
56
+ getFileLink,
57
+ getFilePath,
58
+ removeFile,
59
+ storeFile,
60
+
61
+ // Middleware utilities
42
62
  middleware,
63
+
64
+ // User utilities
43
65
  userManager: userManager.main,
44
66
  };
@@ -19,7 +19,10 @@ dataProvider.use("/", middleware.auth, async (ctx, next) => {
19
19
 
20
20
  // fields validation
21
21
  if (!bodyValidated.isValid) {
22
- ctx.throw(412, JSON.stringify(reply("e", { e: bodyValidated.requires })));
22
+ ctx.throw(
23
+ 412,
24
+ JSON.stringify(reply("e", { error: bodyValidated.requires }))
25
+ );
23
26
  }
24
27
 
25
28
  // type caster
@@ -57,7 +60,10 @@ dataProvider.post("/find", async (ctx) => {
57
60
 
58
61
  // fields validation
59
62
  if (!bodyValidate.isValid) {
60
- ctx.throw(412, JSON.stringify(reply("e", { e: bodyValidate.requires })));
63
+ ctx.throw(
64
+ 412,
65
+ JSON.stringify(reply("e", { error: bodyValidate.requires }))
66
+ );
61
67
  }
62
68
 
63
69
  // access validation
@@ -79,7 +85,7 @@ dataProvider.post("/find", async (ctx) => {
79
85
  if (collection == null) {
80
86
  ctx.throw(
81
87
  412,
82
- JSON.stringify(reply("e", { e: "wrong database or collection" }))
88
+ JSON.stringify(reply("e", { error: "wrong database or collection" }))
83
89
  );
84
90
  }
85
91
 
@@ -125,7 +131,10 @@ dataProvider.post("/find-one", async (ctx) => {
125
131
 
126
132
  // fields validation
127
133
  if (!bodyValidate.isValid) {
128
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
134
+ ctx.throw(
135
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
136
+ 412
137
+ );
129
138
  }
130
139
 
131
140
  // access validation
@@ -142,7 +151,7 @@ dataProvider.post("/find-one", async (ctx) => {
142
151
  let collection = service.getCollection(body.database, body.collection);
143
152
  if (collection == null) {
144
153
  ctx.throw(
145
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
154
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
146
155
  412
147
156
  );
148
157
  }
@@ -194,7 +203,10 @@ dataProvider.post("/count", async (ctx) => {
194
203
 
195
204
  // fields validation
196
205
  if (!bodyValidate.isValid) {
197
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
206
+ ctx.throw(
207
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
208
+ 412
209
+ );
198
210
  }
199
211
 
200
212
  // access validation
@@ -211,7 +223,7 @@ dataProvider.post("/count", async (ctx) => {
211
223
  let collection = service.getCollection(body.database, body.collection);
212
224
  if (collection == null) {
213
225
  ctx.throw(
214
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
226
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
215
227
  412
216
228
  );
217
229
  }
@@ -241,7 +253,10 @@ dataProvider.post("/update-one", async (ctx) => {
241
253
 
242
254
  // fields validation
243
255
  if (!bodyValidate.isValid) {
244
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
256
+ ctx.throw(
257
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
258
+ 412
259
+ );
245
260
  }
246
261
 
247
262
  // access validation
@@ -258,7 +273,7 @@ dataProvider.post("/update-one", async (ctx) => {
258
273
  let collection = service.getCollection(body.database, body.collection);
259
274
  if (collection == null) {
260
275
  ctx.throw(
261
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
276
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
262
277
  412
263
278
  );
264
279
  }
@@ -291,7 +306,10 @@ dataProvider.post("/insert-one", async (ctx) => {
291
306
 
292
307
  // fields validation
293
308
  if (!bodyValidate.isValid) {
294
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
309
+ ctx.throw(
310
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
311
+ 412
312
+ );
295
313
  }
296
314
 
297
315
  // access validation
@@ -312,7 +330,7 @@ dataProvider.post("/insert-one", async (ctx) => {
312
330
  let collection = service.getCollection(body.database, body.collection);
313
331
  if (collection == null) {
314
332
  ctx.throw(
315
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
333
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
316
334
  412
317
335
  );
318
336
  }
@@ -341,7 +359,10 @@ dataProvider.post("/remove-one", async (ctx) => {
341
359
 
342
360
  // fields validation
343
361
  if (!bodyValidate.isValid) {
344
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
362
+ ctx.throw(
363
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
364
+ 412
365
+ );
345
366
  }
346
367
 
347
368
  // access validation
@@ -358,7 +379,7 @@ dataProvider.post("/remove-one", async (ctx) => {
358
379
  let collection = service.getCollection(body.database, body.collection);
359
380
  if (collection == null) {
360
381
  ctx.throw(
361
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
382
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
362
383
  412
363
384
  );
364
385
  }
@@ -391,7 +412,10 @@ dataProvider.post("/aggregate", async (ctx) => {
391
412
 
392
413
  // fields validation
393
414
  if (!bodyValidate.isValid) {
394
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
415
+ ctx.throw(
416
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
417
+ 412
418
+ );
395
419
  }
396
420
 
397
421
  // access validation
@@ -408,7 +432,7 @@ dataProvider.post("/aggregate", async (ctx) => {
408
432
  let collection = service.getCollection(body.database, body.collection);
409
433
  if (collection == null) {
410
434
  ctx.throw(
411
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
435
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
412
436
  412
413
437
  );
414
438
  }
@@ -438,7 +462,10 @@ dataProvider.post("/findByIds", async (ctx, next) => {
438
462
 
439
463
  // fields validation
440
464
  if (!bodyValidate.isValid) {
441
- ctx.throw(JSON.stringify(reply("e", { e: bodyValidate.requires })), 412);
465
+ ctx.throw(
466
+ JSON.stringify(reply("e", { error: bodyValidate.requires })),
467
+ 412
468
+ );
442
469
  }
443
470
 
444
471
  // access validation
@@ -455,7 +482,7 @@ dataProvider.post("/findByIds", async (ctx, next) => {
455
482
  let collection = service.getCollection(body.database, body.collection);
456
483
  if (collection == null) {
457
484
  ctx.throw(
458
- JSON.stringify(reply("e", { e: "wrong database or collection" })),
485
+ JSON.stringify(reply("e", { error: "wrong database or collection" })),
459
486
  412
460
487
  );
461
488
  }
@@ -1,9 +1,10 @@
1
- let Mongoose = require('mongoose');
1
+ const Mongoose = require('mongoose');
2
+
2
3
  module.exports = {
3
4
  'ObjectId': Mongoose.Types.ObjectId,
4
5
  'Date': (dateValue) => {
5
- let strDate = dateValue.toString();
6
- let mongoDateFormateInString = new Date(strDate).toISOString().split('T')[0];
6
+ const strDate = dateValue.toString();
7
+ const mongoDateFormateInString = new Date(strDate).toISOString().split('T')[0];
7
8
  return new Date(mongoDateFormateInString);
8
9
  }
9
10
  }
@@ -1,27 +1,29 @@
1
- var mongoose = require('mongoose');
2
- var Schemas = require('../../class/db_schemas');
1
+ var mongoose = require("mongoose");
2
+ var Schemas = require("../../class/db_schemas");
3
3
 
4
- let CollectionDefinition = require('../../class/collection_definition');
5
- let { Permission, PermissionTypes } = require('../../class/security');
4
+ let CollectionDefinition = require("../../class/collection_definition");
5
+ let { Permission, PermissionTypes } = require("../../class/security");
6
+ const { config } = require("../../config");
6
7
 
7
8
  module.exports = [
8
- new CollectionDefinition({
9
- db: 'cms',
10
- collection: 'file',
11
- schema: Schemas.file,
12
- permissions: [
13
- new Permission({
14
- type: PermissionTypes.upload_file_access,
15
- read: true,
16
- write: true,
17
- onlyOwnData: false,
18
- }),
19
- new Permission({
20
- type: PermissionTypes.remove_file_access,
21
- read: true,
22
- write: true,
23
- onlyOwnData: false,
24
- }),
25
- ],
26
- }),
27
- ]
9
+ new CollectionDefinition({
10
+ db: "cms",
11
+ collection: "file",
12
+ schema: Schemas.file,
13
+ permissions: [
14
+ new Permission({
15
+ type: PermissionTypes.upload_file_access,
16
+ read: true,
17
+ write: true,
18
+ onlyOwnData: false,
19
+ }),
20
+ new Permission({
21
+ type: PermissionTypes.remove_file_access,
22
+ read: true,
23
+ write: true,
24
+ onlyOwnData: false,
25
+ }),
26
+ ],
27
+ triggers: config.fileTriggers || [],
28
+ }),
29
+ ];