@modular-rest/server 1.11.14 → 1.11.15

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 (79) hide show
  1. package/dist/application.d.ts +29 -0
  2. package/dist/application.js +217 -0
  3. package/dist/class/cms_trigger.d.ts +52 -0
  4. package/dist/class/cms_trigger.js +47 -0
  5. package/dist/class/collection_definition.d.ts +112 -0
  6. package/dist/class/collection_definition.js +87 -0
  7. package/dist/class/combinator.d.ts +43 -0
  8. package/dist/class/combinator.js +174 -0
  9. package/dist/class/database_trigger.d.ts +90 -0
  10. package/dist/class/database_trigger.js +64 -0
  11. package/dist/class/db_schemas.d.ts +25 -0
  12. package/dist/class/db_schemas.js +28 -0
  13. package/dist/class/directory.d.ts +20 -0
  14. package/dist/class/directory.js +87 -0
  15. package/dist/class/paginator.d.ts +31 -0
  16. package/dist/class/paginator.js +43 -0
  17. package/dist/class/reply.d.ts +29 -0
  18. package/dist/class/reply.js +44 -0
  19. package/dist/class/security.d.ts +186 -0
  20. package/dist/class/security.js +178 -0
  21. package/dist/class/trigger_operator.d.ts +92 -0
  22. package/dist/class/trigger_operator.js +99 -0
  23. package/dist/class/user.d.ts +81 -0
  24. package/dist/class/user.js +151 -0
  25. package/dist/class/validator.d.ts +19 -0
  26. package/dist/class/validator.js +101 -0
  27. package/dist/config.d.ts +113 -0
  28. package/dist/config.js +26 -0
  29. package/dist/defult-permissions.d.ts +2 -0
  30. package/dist/defult-permissions.js +31 -0
  31. package/dist/events.d.ts +23 -0
  32. package/dist/events.js +47 -0
  33. package/dist/helper/data_insertion.d.ts +38 -0
  34. package/dist/helper/data_insertion.js +110 -0
  35. package/dist/helper/presetup_services.d.ts +60 -0
  36. package/dist/helper/presetup_services.js +108 -0
  37. package/dist/index.d.ts +118 -0
  38. package/dist/middlewares.d.ts +53 -0
  39. package/dist/middlewares.js +106 -0
  40. package/dist/play-test.d.ts +1 -0
  41. package/dist/play-test.js +9 -0
  42. package/dist/services/data_provider/router.d.ts +4 -0
  43. package/dist/services/data_provider/router.js +412 -0
  44. package/dist/services/data_provider/service.d.ts +132 -0
  45. package/dist/services/data_provider/service.js +253 -0
  46. package/dist/services/data_provider/typeCasters.d.ts +9 -0
  47. package/dist/services/data_provider/typeCasters.js +18 -0
  48. package/dist/services/file/db.d.ts +1 -0
  49. package/dist/services/file/db.js +31 -0
  50. package/dist/services/file/router.d.ts +4 -0
  51. package/dist/services/file/router.js +115 -0
  52. package/dist/services/file/service.d.ts +204 -0
  53. package/dist/services/file/service.js +341 -0
  54. package/dist/services/functions/router.d.ts +4 -0
  55. package/dist/services/functions/router.js +68 -0
  56. package/dist/services/functions/service.d.ts +132 -0
  57. package/dist/services/functions/service.js +159 -0
  58. package/dist/services/jwt/router.d.ts +4 -0
  59. package/dist/services/jwt/router.js +99 -0
  60. package/dist/services/jwt/service.d.ts +97 -0
  61. package/dist/services/jwt/service.js +135 -0
  62. package/dist/services/user_manager/db.d.ts +1 -0
  63. package/dist/services/user_manager/db.js +75 -0
  64. package/dist/services/user_manager/permissionManager.d.ts +19 -0
  65. package/dist/services/user_manager/permissionManager.js +42 -0
  66. package/dist/services/user_manager/router.d.ts +4 -0
  67. package/dist/services/user_manager/router.js +195 -0
  68. package/dist/services/user_manager/service.d.ts +317 -0
  69. package/dist/services/user_manager/service.js +632 -0
  70. package/package.json +3 -3
  71. package/src/application.ts +1 -1
  72. package/src/class/cms_trigger.ts +8 -14
  73. package/src/class/database_trigger.ts +10 -4
  74. package/src/class/user.ts +1 -1
  75. package/src/services/data_provider/router.ts +293 -0
  76. package/src/services/data_provider/service.ts +2 -1
  77. package/src/services/functions/router.ts +3 -2
  78. package/src/services/user_manager/db.ts +5 -5
  79. package/src/services/user_manager/service.ts +20 -15
@@ -1,3 +1,5 @@
1
+ import { DatabaseTriggerContext } from './database_trigger';
2
+
1
3
  /**
2
4
  * Type for CMS operations that can trigger a callback
3
5
  * @typedef {('update-one' | 'insert-one' | 'remove-one')} CmsOperation
@@ -8,17 +10,6 @@
8
10
  */
9
11
  export type CmsOperation = 'update-one' | 'insert-one' | 'remove-one';
10
12
 
11
- /**
12
- * Context interface for CMS trigger callbacks
13
- * @interface CmsTriggerContext
14
- * @property {Record<string, any>} query - The query parameters used in the CMS operation
15
- * @property {any} queryResult - The result of the CMS operation
16
- */
17
- export interface CmsTriggerContext {
18
- query: Record<string, any>;
19
- queryResult: any;
20
- }
21
-
22
13
  /**
23
14
  * Defines a callback to be executed on specific CMS operations
24
15
  * @class CmsTrigger
@@ -28,7 +19,7 @@ export interface CmsTriggerContext {
28
19
  * ```typescript
29
20
  * const trigger = new CmsTrigger('insert-one', (context) => {
30
21
  * console.log('New CMS document inserted:', context.queryResult);
31
- * // Perform additional actions after CMS document insertion
22
+ * // Perform additional actions after CMS document insertion.
32
23
  * });
33
24
  *
34
25
  * // Use the trigger in RestOptions
@@ -40,7 +31,7 @@ export interface CmsTriggerContext {
40
31
  */
41
32
  export class CmsTrigger {
42
33
  operation: CmsOperation;
43
- callback: (context: CmsTriggerContext) => void;
34
+ callback: (context: DatabaseTriggerContext) => void;
44
35
 
45
36
  /**
46
37
  * Creates a new CmsTrigger instance
@@ -59,7 +50,10 @@ export class CmsTrigger {
59
50
  * });
60
51
  * ```
61
52
  */
62
- constructor(operation: CmsOperation, callback: (context: CmsTriggerContext) => void = () => {}) {
53
+ constructor(
54
+ operation: CmsOperation,
55
+ callback: (context: DatabaseTriggerContext) => void = () => {}
56
+ ) {
63
57
  this.operation = operation;
64
58
  this.callback = callback;
65
59
  }
@@ -13,12 +13,18 @@ export type DatabaseOperation =
13
13
  /**
14
14
  * Context interface for database trigger callbacks
15
15
  * @interface DatabaseTriggerContext
16
- * @property {Record<string, any>} query - The query parameters used in the database operation
17
- * @property {any | any[]} queryResult - The result of the database operation
16
+ * @property {Record<string, any>} doc - The document data for insert/update operations
17
+ * @property {Record<string, any>} query - The query data for find/find-one operations
18
+ * @property {Record<string, any>} update - The update data for update operations
19
+ * @property {Record<string, any>[]} pipelines - The aggregation pipelines for aggregate operations
20
+ * @property {Record<string, any> } queryResult - The result of the database operation
18
21
  */
19
22
  export interface DatabaseTriggerContext {
20
- query: Record<string, any>;
21
- queryResult: any | any[];
23
+ doc?: Record<string, any>;
24
+ query?: Record<string, any>;
25
+ update?: Record<string, any>;
26
+ pipelines?: Record<string, any>[];
27
+ queryResult: Record<string, any>;
22
28
  }
23
29
 
24
30
  /**
package/src/class/user.ts CHANGED
@@ -48,7 +48,7 @@ export class User {
48
48
  phone: string,
49
49
  email: string,
50
50
  password: string,
51
- type: string,
51
+ type: 'user' | 'anonymous',
52
52
  model: any
53
53
  ) {
54
54
  this.id = id;
@@ -6,6 +6,7 @@ import nestedProperty from 'nested-property';
6
6
  import * as service from './service';
7
7
  import * as middleware from '../../middlewares';
8
8
  import { Context, Next } from 'koa';
9
+ import mongoose from 'mongoose';
9
10
 
10
11
  const name = 'data-provider';
11
12
 
@@ -91,6 +92,12 @@ dataProvider.post('/find', async (ctx: Context) => {
91
92
  await queryRequest
92
93
  .exec()
93
94
  .then(async docs => {
95
+ // Call trigger
96
+ service.triggers.call('find', body.database, body.collection, {
97
+ query: body.query,
98
+ queryResult: docs,
99
+ });
100
+
94
101
  ctx.body = { data: docs };
95
102
  })
96
103
  .catch(err => {
@@ -144,6 +151,12 @@ dataProvider.post('/find-one', async (ctx: Context) => {
144
151
  await queryRequest
145
152
  .exec()
146
153
  .then(async doc => {
154
+ // Call trigger
155
+ service.triggers.call('find-one', body.database, body.collection, {
156
+ query: body.query,
157
+ queryResult: doc,
158
+ });
159
+
147
160
  ctx.body = { data: doc };
148
161
  })
149
162
  .catch(err => {
@@ -179,7 +192,14 @@ dataProvider.post('/count', async (ctx: Context) => {
179
192
 
180
193
  await collection
181
194
  .countDocuments(body.query)
195
+ .exec()
182
196
  .then(count => {
197
+ // Call trigger
198
+ service.triggers.call('count', body.database, body.collection, {
199
+ query: body.query,
200
+ queryResult: count,
201
+ });
202
+
183
203
  ctx.body = { data: count };
184
204
  })
185
205
  .catch(err => {
@@ -188,4 +208,277 @@ dataProvider.post('/count', async (ctx: Context) => {
188
208
  });
189
209
  });
190
210
 
211
+ dataProvider.post('/update-one', async (ctx: Context) => {
212
+ const body = ctx.request.body;
213
+ const bodyValidate = validateObject(body, 'database collection query update');
214
+
215
+ // fields validation
216
+ if (!bodyValidate.isValid) {
217
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
218
+ }
219
+
220
+ // access validation
221
+ const hasAccess = service.checkAccess(
222
+ body.database,
223
+ body.collection,
224
+ AccessTypes.write,
225
+ body.query,
226
+ ctx.state.user
227
+ );
228
+ if (!hasAccess) ctx.throw(403, 'access denied');
229
+
230
+ // collection validation
231
+ const collection = service.getCollection(body.database, body.collection);
232
+ if (collection == null) {
233
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
234
+ }
235
+
236
+ // get removing doc as output for triggers
237
+ const output: any = await collection.findOne(body.query).exec().then();
238
+
239
+ // operate on db
240
+ await collection
241
+ .updateOne(body.query, body.update, body.options)
242
+ .exec()
243
+ .then(writeOpResult => {
244
+ // Call trigger
245
+ service.triggers.call('update-one', body.database, body.collection, {
246
+ query: body.query,
247
+ update: body.update,
248
+ queryResult: writeOpResult,
249
+ });
250
+
251
+ ctx.body = { data: writeOpResult };
252
+ })
253
+ .catch(err => {
254
+ ctx.status = err.status || 500;
255
+ ctx.body = err.message;
256
+ });
257
+ });
258
+
259
+ dataProvider.post('/insert-one', async (ctx: Context) => {
260
+ const body = ctx.request.body;
261
+ const bodyValidate = validateObject(body, 'database collection doc');
262
+
263
+ // fields validation
264
+ if (!bodyValidate.isValid) {
265
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
266
+ }
267
+
268
+ // access validation
269
+ const hasAccess = service.checkAccess(
270
+ body.database,
271
+ body.collection,
272
+ AccessTypes.write,
273
+ body.doc,
274
+ ctx.state.user
275
+ );
276
+ if (!hasAccess) {
277
+ console.log(body);
278
+ console.log(ctx.state.user.permission);
279
+ ctx.throw(403, 'access denied');
280
+ }
281
+
282
+ // collection validation
283
+ const collection = service.getCollection(body.database, body.collection);
284
+ if (collection == null) {
285
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
286
+ }
287
+
288
+ // operate on db
289
+ await new collection(body.doc)
290
+ .save()
291
+ .then(async newDoc => {
292
+ // Call trigger
293
+ service.triggers.call('insert-one', body.database, body.collection, {
294
+ doc: body.doc,
295
+ queryResult: newDoc,
296
+ });
297
+
298
+ ctx.body = { data: newDoc };
299
+ })
300
+ .catch(err => {
301
+ ctx.status = err.status || 500;
302
+ ctx.body = err.message;
303
+ });
304
+ });
305
+
306
+ dataProvider.post('/remove-one', async (ctx: Context) => {
307
+ const body = ctx.request.body;
308
+ const bodyValidate = validateObject(body, 'database collection query');
309
+
310
+ // fields validation
311
+ if (!bodyValidate.isValid) {
312
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
313
+ }
314
+
315
+ // access validation
316
+ const hasAccess = service.checkAccess(
317
+ body.database,
318
+ body.collection,
319
+ AccessTypes.write,
320
+ body.query,
321
+ ctx.state.user
322
+ );
323
+ if (!hasAccess) ctx.throw(403, 'access denied');
324
+
325
+ // collection validation
326
+ const collection = service.getCollection(body.database, body.collection);
327
+ if (collection == null) {
328
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
329
+ }
330
+
331
+ // get removing doc as output for triggers
332
+ const output: any = await collection.findOne(body.query).exec().then();
333
+
334
+ // operate on db
335
+ await collection
336
+ .deleteOne(body.query)
337
+ .exec()
338
+ .then(async (result: any) => {
339
+ // Call trigger
340
+ service.triggers.call('remove-one', body.database, body.collection, {
341
+ query: body.query,
342
+ queryResult: result,
343
+ });
344
+
345
+ ctx.body = { data: result };
346
+ })
347
+ .catch((err: Error) => {
348
+ ctx.status = (err as any).status || 500;
349
+ ctx.body = err.message;
350
+ });
351
+ });
352
+
353
+ dataProvider.post('/aggregate', async (ctx: Context) => {
354
+ const body = ctx.request.body;
355
+ const bodyValidate = validateObject(body, 'database collection accessQuery');
356
+
357
+ // fields validation
358
+ if (!bodyValidate.isValid) {
359
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
360
+ }
361
+
362
+ // access validation
363
+ const hasAccess = service.checkAccess(
364
+ body.database,
365
+ body.collection,
366
+ AccessTypes.read,
367
+ body.accessQuery,
368
+ ctx.state.user
369
+ );
370
+ if (!hasAccess) ctx.throw(403, 'access denied');
371
+
372
+ // collection validation
373
+ const collection = service.getCollection(body.database, body.collection);
374
+ if (collection == null) {
375
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
376
+ }
377
+
378
+ // operate on db
379
+ await collection
380
+ .aggregate(body.pipelines)
381
+ .exec()
382
+ .then(async (result: any) => {
383
+ // Call trigger
384
+ service.triggers.call('aggregate', body.database, body.collection, {
385
+ pipelines: body.pipelines,
386
+ queryResult: result,
387
+ });
388
+
389
+ ctx.body = { data: result };
390
+ })
391
+ .catch((err: any) => {
392
+ ctx.status = err.status || 500;
393
+ ctx.body = err.message;
394
+ });
395
+ });
396
+
397
+ dataProvider.post('/findByIds', async (ctx: Context, next: Next) => {
398
+ const body = ctx.request.body;
399
+ const bodyValidate = validateObject(body, 'database collection ids');
400
+
401
+ // fields validation
402
+ if (!bodyValidate.isValid) {
403
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
404
+ }
405
+
406
+ // access validation
407
+ const hasAccess = service.checkAccess(
408
+ body.database,
409
+ body.collection,
410
+ AccessTypes.read,
411
+ body.accessQuery || {},
412
+ ctx.state.user
413
+ );
414
+ if (!hasAccess) ctx.throw(403, 'access denied');
415
+
416
+ // collection validation
417
+ const collection = service.getCollection(body.database, body.collection);
418
+ if (collection == null) {
419
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
420
+ }
421
+
422
+ const or: Array<{ _id: any }> = [];
423
+
424
+ try {
425
+ body.ids.forEach((id: any) => {
426
+ const castedid = service.getAsID(id);
427
+ or.push({ _id: castedid });
428
+ });
429
+ } catch (e) {
430
+ console.log('ids.forEach', e);
431
+ }
432
+
433
+ const pipelines = [
434
+ {
435
+ $match: { $or: or },
436
+ },
437
+ // {
438
+ // $sort: body.sort || { _id: 1 }
439
+ // }
440
+ ];
441
+
442
+ // operate on db
443
+ await collection
444
+ .aggregate(pipelines)
445
+ .exec()
446
+ .then(async (result: any[]) => {
447
+ ctx.state = { data: result };
448
+ await next();
449
+ })
450
+ .catch((err: Error) => {
451
+ ctx.status = (err as any).status || 500;
452
+ ctx.body = err.message;
453
+ });
454
+ });
455
+
456
+ // Final middleware for converting mongoose documents to JSON
457
+ dataProvider.use('/', async (ctx: Context, next: Next) => {
458
+ // this event is responsible to covert whole mongoose doc to json form
459
+ // including getters, public properties
460
+ // each mongoose doc must have a "toJson" method being defined on its own Schema.
461
+
462
+ const state = ctx.state;
463
+ // let result;
464
+
465
+ // // array
466
+ // if(!isNaN(state.length)) {
467
+ // result = [];
468
+
469
+ // for (let index = 0; index < state.length; index++) {
470
+ // const element = state[index];
471
+ // if(element.hasOwnProperty('toJson'))
472
+ // result.push(element.toJson());
473
+ // else result.push(element);
474
+ // }
475
+ // }
476
+ // // object
477
+ // else {
478
+ // result = state.toJson();
479
+ // }
480
+
481
+ ctx.body = state;
482
+ });
483
+
191
484
  export { name, dataProvider as main };
@@ -302,4 +302,5 @@ export function performAdditionalOptionsToQueryObject<T = any>(
302
302
  return queryObj;
303
303
  }
304
304
 
305
- export { TypeCasters };
305
+ // Instead, export triggerOperator as triggers
306
+ export { triggerOperator as triggers, TypeCasters };
@@ -25,9 +25,10 @@ functionRouter.post('/run', middleware.auth, async (ctx: Context) => {
25
25
 
26
26
  try {
27
27
  const result = await service.runFunction(name, args, ctx.state.user);
28
- ctx.body = JSON.stringify(reply('s', { data: result }));
28
+ ctx.body = reply('s', { data: result });
29
29
  } catch (e) {
30
- ctx.throw(400, JSON.stringify(reply('e', { error: (e as Error).message })));
30
+ ctx.status = 400;
31
+ ctx.body = reply('e', { message: (e as Error).message });
31
32
  }
32
33
  });
33
34
 
@@ -12,11 +12,11 @@ interface AuthDocument extends mongoose.Document {
12
12
 
13
13
  const authSchema = new Schema(
14
14
  {
15
- permissionGroup: String,
16
- email: String,
17
- phone: String,
18
- password: String,
19
- type: { type: String, default: 'user', enum: ['user', 'anonymous'] },
15
+ permissionGroup: { type: String, required: true },
16
+ email: { type: String, required: false },
17
+ phone: { type: String, required: false },
18
+ password: { type: String, required: true },
19
+ type: { type: String, required: true, default: 'user', enum: ['user', 'anonymous'] },
20
20
  },
21
21
  { timestamps: true }
22
22
  );
@@ -391,17 +391,22 @@ class UserManager {
391
391
  }
392
392
 
393
393
  try {
394
- // Create anonymous user document
395
- const anonymousUserDoc = await userModel.create({
396
- type: 'anonymous',
397
- permissionGroup: getDefaultAnonymousPermissionGroup().title,
398
- phone: '',
399
- email: '',
400
- password: '',
401
- });
394
+ // Check if anonymous user already exists
395
+ let anonymousUser = await userModel.findOne({ type: 'anonymous' }).exec();
396
+
397
+ if (!anonymousUser) {
398
+ // Create anonymous user document
399
+ anonymousUser = await userModel.create({
400
+ type: 'anonymous',
401
+ permissionGroup: getDefaultAnonymousPermissionGroup().title,
402
+ phone: '',
403
+ email: 'anonymous',
404
+ password: '',
405
+ });
406
+ }
402
407
 
403
408
  // Load user from document
404
- const user = await User.loadFromModel(anonymousUserDoc);
409
+ const user = await User.loadFromModel(anonymousUser);
405
410
 
406
411
  // Get token payload
407
412
  const payload = user.getBrief();
@@ -490,7 +495,7 @@ class UserManager {
490
495
  const user = await User.loadFromModel(gottenFromDB);
491
496
 
492
497
  // Update password
493
- user.password = Buffer.from(password).toString('base64');
498
+ user.password = password;
494
499
 
495
500
  // Save to database
496
501
  await user.save();
@@ -563,7 +568,7 @@ class UserManager {
563
568
  const user = await User.loadFromModel(gottenFromDB);
564
569
 
565
570
  // Update password
566
- user.password = Buffer.from(password).toString('base64');
571
+ user.password = password;
567
572
 
568
573
  // Save to database
569
574
  await user.save();
@@ -619,9 +624,9 @@ class UserManager {
619
624
  ...detail,
620
625
  type: detail.type || 'user',
621
626
  permissionGroup: detail.permissionGroup || getDefaultPermissionGroups().title,
622
- phone: detail.phone || '',
623
- email: detail.email || '',
624
- password: detail.password ? Buffer.from(detail.password).toString('base64') : '',
627
+ phone: detail.phone || undefined,
628
+ email: detail.email || undefined,
629
+ password: detail.password || undefined,
625
630
  });
626
631
 
627
632
  // Load user from document
@@ -674,7 +679,7 @@ class UserManager {
674
679
  }
675
680
 
676
681
  const user = await User.loadFromModel(userDoc);
677
- user.password = Buffer.from(newPass).toString('base64');
682
+ user.password = newPass;
678
683
  await user.save();
679
684
  }
680
685