@modular-rest/server 1.11.13 → 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.
- package/.nvmrc +1 -0
- package/.prettierrc.json +9 -0
- package/.releaserc.json +24 -0
- package/README.md +79 -94
- package/dist/application.d.ts +29 -0
- package/dist/application.js +217 -0
- package/dist/class/cms_trigger.d.ts +52 -0
- package/dist/class/cms_trigger.js +47 -0
- package/dist/class/collection_definition.d.ts +112 -0
- package/dist/class/collection_definition.js +87 -0
- package/dist/class/combinator.d.ts +43 -0
- package/dist/class/combinator.js +174 -0
- package/dist/class/database_trigger.d.ts +90 -0
- package/dist/class/database_trigger.js +64 -0
- package/dist/class/db_schemas.d.ts +25 -0
- package/dist/class/db_schemas.js +28 -0
- package/dist/class/directory.d.ts +20 -0
- package/dist/class/directory.js +87 -0
- package/dist/class/paginator.d.ts +31 -0
- package/dist/class/paginator.js +43 -0
- package/dist/class/reply.d.ts +29 -0
- package/dist/class/reply.js +44 -0
- package/dist/class/security.d.ts +186 -0
- package/dist/class/security.js +178 -0
- package/dist/class/trigger_operator.d.ts +92 -0
- package/dist/class/trigger_operator.js +99 -0
- package/dist/class/user.d.ts +81 -0
- package/dist/class/user.js +151 -0
- package/dist/class/validator.d.ts +19 -0
- package/dist/class/validator.js +101 -0
- package/dist/config.d.ts +113 -0
- package/dist/config.js +26 -0
- package/dist/defult-permissions.d.ts +2 -0
- package/dist/defult-permissions.js +31 -0
- package/dist/events.d.ts +23 -0
- package/dist/events.js +47 -0
- package/dist/helper/data_insertion.d.ts +38 -0
- package/dist/helper/data_insertion.js +110 -0
- package/dist/helper/presetup_services.d.ts +60 -0
- package/dist/helper/presetup_services.js +108 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.js +79 -0
- package/dist/middlewares.d.ts +53 -0
- package/dist/middlewares.js +106 -0
- package/dist/play-test.d.ts +1 -0
- package/dist/play-test.js +9 -0
- package/dist/services/data_provider/router.d.ts +4 -0
- package/dist/services/data_provider/router.js +412 -0
- package/dist/services/data_provider/service.d.ts +132 -0
- package/dist/services/data_provider/service.js +253 -0
- package/dist/services/data_provider/typeCasters.d.ts +9 -0
- package/dist/services/data_provider/typeCasters.js +18 -0
- package/dist/services/file/db.d.ts +1 -0
- package/dist/services/file/db.js +31 -0
- package/dist/services/file/router.d.ts +4 -0
- package/dist/services/file/router.js +115 -0
- package/dist/services/file/service.d.ts +204 -0
- package/dist/services/file/service.js +341 -0
- package/dist/services/functions/router.d.ts +4 -0
- package/dist/services/functions/router.js +68 -0
- package/dist/services/functions/service.d.ts +132 -0
- package/dist/services/functions/service.js +159 -0
- package/dist/services/jwt/router.d.ts +4 -0
- package/dist/services/jwt/router.js +99 -0
- package/dist/services/jwt/service.d.ts +97 -0
- package/dist/services/jwt/service.js +135 -0
- package/dist/services/user_manager/db.d.ts +1 -0
- package/dist/services/user_manager/db.js +75 -0
- package/dist/services/user_manager/permissionManager.d.ts +19 -0
- package/dist/services/user_manager/permissionManager.js +42 -0
- package/dist/services/user_manager/router.d.ts +4 -0
- package/dist/services/user_manager/router.js +195 -0
- package/dist/services/user_manager/service.d.ts +317 -0
- package/dist/services/user_manager/service.js +632 -0
- package/docs/.keep +0 -0
- package/docs/system-access-type.md +26 -0
- package/package.json +59 -46
- package/src/application.ts +206 -0
- package/src/class/cms_trigger.ts +62 -0
- package/src/class/collection_definition.ts +134 -0
- package/src/class/combinator.ts +176 -0
- package/src/class/database_trigger.ts +105 -0
- package/src/class/db_schemas.ts +44 -0
- package/src/class/{directory.js → directory.ts} +40 -18
- package/src/class/paginator.ts +51 -0
- package/src/class/reply.ts +59 -0
- package/src/class/security.ts +250 -0
- package/src/class/trigger_operator.ts +142 -0
- package/src/class/user.ts +199 -0
- package/src/class/validator.ts +123 -0
- package/src/config.ts +122 -0
- package/src/defult-permissions.ts +31 -0
- package/src/events.ts +59 -0
- package/src/helper/data_insertion.ts +94 -0
- package/src/helper/presetup_services.ts +96 -0
- package/src/index.ts +146 -0
- package/src/middlewares.ts +75 -0
- package/src/play-test.ts +8 -0
- package/src/services/data_provider/router.ts +484 -0
- package/src/services/data_provider/service.ts +306 -0
- package/src/services/data_provider/typeCasters.ts +15 -0
- package/src/services/file/db.ts +29 -0
- package/src/services/file/router.ts +88 -0
- package/src/services/file/service.ts +387 -0
- package/src/services/functions/router.ts +35 -0
- package/src/services/functions/service.ts +203 -0
- package/src/services/jwt/router.ts +73 -0
- package/src/services/jwt/service.ts +139 -0
- package/src/services/user_manager/db.ts +87 -0
- package/src/services/user_manager/permissionManager.ts +49 -0
- package/src/services/user_manager/router.ts +193 -0
- package/src/services/user_manager/service.ts +703 -0
- package/tsconfig.json +16 -9
- package/typedoc.mjs +41 -0
- package/LICENSE +0 -21
- package/package-lock.json +0 -1373
- package/src/application.js +0 -239
- package/src/class/cms_trigger.js +0 -20
- package/src/class/collection_definition.js +0 -33
- package/src/class/combinator.js +0 -133
- package/src/class/database_trigger.js +0 -20
- package/src/class/db_schemas.js +0 -18
- package/src/class/paginator.js +0 -31
- package/src/class/reply.js +0 -37
- package/src/class/security.js +0 -141
- package/src/class/trigger_operator.js +0 -39
- package/src/class/user.js +0 -112
- package/src/class/validator.js +0 -91
- package/src/config.js +0 -67
- package/src/events.js +0 -15
- package/src/helper/data_insertion.js +0 -64
- package/src/helper/presetup_services.js +0 -31
- package/src/index.js +0 -66
- package/src/middlewares.js +0 -44
- package/src/services/data_provider/router.js +0 -552
- package/src/services/data_provider/service.js +0 -262
- package/src/services/data_provider/typeCasters.js +0 -10
- package/src/services/file/db.js +0 -29
- package/src/services/file/router.js +0 -92
- package/src/services/file/service.js +0 -231
- package/src/services/functions/router.js +0 -37
- package/src/services/functions/service.js +0 -74
- package/src/services/jwt/router.js +0 -82
- package/src/services/jwt/service.js +0 -37
- package/src/services/user_manager/db.js +0 -83
- package/src/services/user_manager/permissionManager.js +0 -43
- package/src/services/user_manager/router.js +0 -176
- package/src/services/user_manager/service.js +0 -377
- package/types/application.d.ts +0 -97
- package/types/class/cms_trigger.d.ts +0 -24
- package/types/class/collection_definition.d.ts +0 -36
- package/types/class/combinator.d.ts +0 -30
- package/types/class/database_trigger.d.ts +0 -28
- package/types/class/db_schemas.d.ts +0 -2
- package/types/class/directory.d.ts +0 -2
- package/types/class/paginator.d.ts +0 -8
- package/types/class/reply.d.ts +0 -8
- package/types/class/security.d.ts +0 -109
- package/types/class/trigger_operator.d.ts +0 -19
- package/types/class/user.d.ts +0 -24
- package/types/class/validator.d.ts +0 -9
- package/types/config.d.ts +0 -101
- package/types/events.d.ts +0 -7
- package/types/helper/data_insertion.d.ts +0 -4
- package/types/helper/presetup_services.d.ts +0 -5
- package/types/index.d.ts +0 -72
- package/types/middlewares.d.ts +0 -10
- package/types/services/data_provider/router.d.ts +0 -3
- package/types/services/data_provider/service.d.ts +0 -40
- package/types/services/data_provider/typeCasters.d.ts +0 -3
- package/types/services/file/db.d.ts +0 -3
- package/types/services/file/router.d.ts +0 -3
- package/types/services/file/service.d.ts +0 -81
- package/types/services/functions/router.d.ts +0 -3
- package/types/services/functions/service.d.ts +0 -23
- package/types/services/jwt/router.d.ts +0 -3
- package/types/services/jwt/service.d.ts +0 -10
- package/types/services/user_manager/db.d.ts +0 -3
- package/types/services/user_manager/permissionManager.d.ts +0 -3
- package/types/services/user_manager/router.d.ts +0 -3
- package/types/services/user_manager/service.d.ts +0 -131
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.auth = auth;
|
|
37
|
+
const validator_1 = require("./class/validator");
|
|
38
|
+
const userManager = __importStar(require("./services/user_manager/service"));
|
|
39
|
+
/**
|
|
40
|
+
* Authentication middleware that secures routes by validating user tokens and managing access control.
|
|
41
|
+
*
|
|
42
|
+
* This middleware performs several key functions:
|
|
43
|
+
* 1. Validates that the incoming request contains an authorization token in the header
|
|
44
|
+
* 2. Verifies the token is valid by checking against the user management service
|
|
45
|
+
* 3. Retrieves the associated user object if the token is valid
|
|
46
|
+
* 4. Attaches the authenticated {@link User} object on ctx.state.user for use in subsequent middleware/routes
|
|
47
|
+
* 5. Throws appropriate HTTP errors (401, 412) if authentication fails
|
|
48
|
+
*
|
|
49
|
+
* The middleware integrates with the permission system to enable role-based access control.
|
|
50
|
+
* The attached user object provides methods like hasPermission() to check specific permissions.
|
|
51
|
+
*
|
|
52
|
+
* Common usage patterns:
|
|
53
|
+
* - Protecting sensitive API endpoints
|
|
54
|
+
* - Implementing role-based access control
|
|
55
|
+
* - Getting the current authenticated user
|
|
56
|
+
* - Validating user permissions before allowing actions
|
|
57
|
+
*
|
|
58
|
+
* @throws {Error} 401 - If no authorization header is present
|
|
59
|
+
* @throws {Error} 412 - If token validation fails
|
|
60
|
+
* @param ctx - Koa Context object containing request/response data
|
|
61
|
+
* @param next - Function to invoke next middleware
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Inside the router.ts file
|
|
66
|
+
* import { auth } from '@modular-rest/server';
|
|
67
|
+
* import { Router } from 'koa-router';
|
|
68
|
+
*
|
|
69
|
+
* const name = 'flowers';
|
|
70
|
+
*
|
|
71
|
+
* const flowerRouter = new Router();
|
|
72
|
+
*
|
|
73
|
+
* flowerRouter.get('/list', auth, (ctx) => {
|
|
74
|
+
* // Get the authenticated user
|
|
75
|
+
* const user = ctx.state.user;
|
|
76
|
+
*
|
|
77
|
+
* // Then you can check the user's role and permission
|
|
78
|
+
* if(user.hasPermission('get_flower')) {
|
|
79
|
+
* ctx.body = 'This is a list of flowers: Rose, Lily, Tulip';
|
|
80
|
+
* } else {
|
|
81
|
+
* ctx.status = 403;
|
|
82
|
+
* ctx.body = 'You are not authorized to access this resource';
|
|
83
|
+
* }
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* module.exports.name = name;
|
|
87
|
+
* module.exports.main = flowerRouter;
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async function auth(ctx, next) {
|
|
91
|
+
const headers = ctx.header;
|
|
92
|
+
const headersValidated = (0, validator_1.validator)(headers, 'authorization');
|
|
93
|
+
if (!headersValidated.isValid)
|
|
94
|
+
ctx.throw(401, 'authentication is required');
|
|
95
|
+
const token = headers.authorization;
|
|
96
|
+
await userManager.main
|
|
97
|
+
.getUserByToken(token)
|
|
98
|
+
.then(async (user) => {
|
|
99
|
+
ctx.state.user = user;
|
|
100
|
+
await next();
|
|
101
|
+
})
|
|
102
|
+
.catch(err => {
|
|
103
|
+
console.log(err);
|
|
104
|
+
ctx.throw(err.status || 412, err.message);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.main = exports.name = void 0;
|
|
40
|
+
const security_1 = require("../../class/security");
|
|
41
|
+
const koa_router_1 = __importDefault(require("koa-router"));
|
|
42
|
+
const validator_1 = require("../../class/validator");
|
|
43
|
+
const reply_1 = require("../../class/reply");
|
|
44
|
+
const nested_property_1 = __importDefault(require("nested-property"));
|
|
45
|
+
const service = __importStar(require("./service"));
|
|
46
|
+
const middleware = __importStar(require("../../middlewares"));
|
|
47
|
+
const name = 'data-provider';
|
|
48
|
+
exports.name = name;
|
|
49
|
+
const dataProvider = new koa_router_1.default();
|
|
50
|
+
exports.main = dataProvider;
|
|
51
|
+
dataProvider.use('/', middleware.auth, async (ctx, next) => {
|
|
52
|
+
const body = ctx.request.body;
|
|
53
|
+
const bodyValidated = (0, validator_1.validateObject)(body, 'database collection');
|
|
54
|
+
// fields validation
|
|
55
|
+
if (!bodyValidated.isValid) {
|
|
56
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidated.requires })));
|
|
57
|
+
}
|
|
58
|
+
// type caster
|
|
59
|
+
if (body.types && body.hasOwnProperty(body.bodyKey || '.')) {
|
|
60
|
+
const bodyKey = body.bodyKey;
|
|
61
|
+
for (const key in body.types) {
|
|
62
|
+
if (body.types.hasOwnProperty(key) && typeof body.types[key] == 'object') {
|
|
63
|
+
const typeDetail = body.types[key];
|
|
64
|
+
try {
|
|
65
|
+
const value = nested_property_1.default.get(body[bodyKey], typeDetail.path);
|
|
66
|
+
const newProperty = service.TypeCasters[typeDetail.type](value);
|
|
67
|
+
nested_property_1.default.set(body[bodyKey], typeDetail.path, newProperty);
|
|
68
|
+
console.log('newProperty', newProperty, JSON.stringify(body[bodyKey]));
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
console.log('type caster error', e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await next();
|
|
77
|
+
});
|
|
78
|
+
dataProvider.post('/find', async (ctx) => {
|
|
79
|
+
const body = ctx.request.body;
|
|
80
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection query');
|
|
81
|
+
// fields validation
|
|
82
|
+
if (!bodyValidate.isValid) {
|
|
83
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
84
|
+
}
|
|
85
|
+
// access validation
|
|
86
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.read, body.query, ctx.state.user);
|
|
87
|
+
if (!hasAccess) {
|
|
88
|
+
console.log(body);
|
|
89
|
+
console.log(ctx.state.user.permission);
|
|
90
|
+
ctx.throw(403, 'access denied');
|
|
91
|
+
}
|
|
92
|
+
// collection validation
|
|
93
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
94
|
+
if (collection == null) {
|
|
95
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
96
|
+
}
|
|
97
|
+
// operate on db
|
|
98
|
+
let queryRequest = collection.find(body.query, body.projection);
|
|
99
|
+
if (body.options) {
|
|
100
|
+
queryRequest = service.performAdditionalOptionsToQueryObject(queryRequest, body.options);
|
|
101
|
+
}
|
|
102
|
+
if (body.populates) {
|
|
103
|
+
try {
|
|
104
|
+
queryRequest = service.performPopulateToQueryObject(queryRequest, body.populates);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
ctx.status = 412;
|
|
108
|
+
ctx.body = err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
await queryRequest
|
|
112
|
+
.exec()
|
|
113
|
+
.then(async (docs) => {
|
|
114
|
+
// Call trigger
|
|
115
|
+
service.triggers.call('find', body.database, body.collection, {
|
|
116
|
+
query: body.query,
|
|
117
|
+
queryResult: docs,
|
|
118
|
+
});
|
|
119
|
+
ctx.body = { data: docs };
|
|
120
|
+
})
|
|
121
|
+
.catch(err => {
|
|
122
|
+
ctx.status = err.status || 500;
|
|
123
|
+
ctx.body = err.message;
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
dataProvider.post('/find-one', async (ctx) => {
|
|
127
|
+
const body = ctx.request.body;
|
|
128
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection query');
|
|
129
|
+
// fields validation
|
|
130
|
+
if (!bodyValidate.isValid) {
|
|
131
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
132
|
+
}
|
|
133
|
+
// access validation
|
|
134
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.read, body.query, ctx.state.user);
|
|
135
|
+
if (!hasAccess)
|
|
136
|
+
ctx.throw(403, 'access denied');
|
|
137
|
+
// collection validation
|
|
138
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
139
|
+
if (collection == null) {
|
|
140
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
141
|
+
}
|
|
142
|
+
// operate on db
|
|
143
|
+
let queryRequest = collection.findOne(body.query, body.projection, body.options);
|
|
144
|
+
if (body.options) {
|
|
145
|
+
queryRequest = service.performAdditionalOptionsToQueryObject(queryRequest, body.options);
|
|
146
|
+
}
|
|
147
|
+
if (body.populates) {
|
|
148
|
+
try {
|
|
149
|
+
queryRequest = service.performPopulateToQueryObject(queryRequest, body.populates);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
ctx.status = 412;
|
|
153
|
+
ctx.body = err;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// operate on db
|
|
157
|
+
await queryRequest
|
|
158
|
+
.exec()
|
|
159
|
+
.then(async (doc) => {
|
|
160
|
+
// Call trigger
|
|
161
|
+
service.triggers.call('find-one', body.database, body.collection, {
|
|
162
|
+
query: body.query,
|
|
163
|
+
queryResult: doc,
|
|
164
|
+
});
|
|
165
|
+
ctx.body = { data: doc };
|
|
166
|
+
})
|
|
167
|
+
.catch(err => {
|
|
168
|
+
ctx.status = err.status || 500;
|
|
169
|
+
ctx.body = err.message;
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
dataProvider.post('/count', async (ctx) => {
|
|
173
|
+
const body = ctx.request.body;
|
|
174
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection query');
|
|
175
|
+
// fields validation
|
|
176
|
+
if (!bodyValidate.isValid) {
|
|
177
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
178
|
+
}
|
|
179
|
+
// access validation
|
|
180
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.read, body.query, ctx.state.user);
|
|
181
|
+
if (!hasAccess)
|
|
182
|
+
ctx.throw(403, 'access denied');
|
|
183
|
+
// collection validation
|
|
184
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
185
|
+
if (collection == null) {
|
|
186
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
187
|
+
}
|
|
188
|
+
await collection
|
|
189
|
+
.countDocuments(body.query)
|
|
190
|
+
.exec()
|
|
191
|
+
.then(count => {
|
|
192
|
+
// Call trigger
|
|
193
|
+
service.triggers.call('count', body.database, body.collection, {
|
|
194
|
+
query: body.query,
|
|
195
|
+
queryResult: count,
|
|
196
|
+
});
|
|
197
|
+
ctx.body = { data: count };
|
|
198
|
+
})
|
|
199
|
+
.catch(err => {
|
|
200
|
+
ctx.status = err.status || 500;
|
|
201
|
+
ctx.body = err.message;
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
dataProvider.post('/update-one', async (ctx) => {
|
|
205
|
+
const body = ctx.request.body;
|
|
206
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection query update');
|
|
207
|
+
// fields validation
|
|
208
|
+
if (!bodyValidate.isValid) {
|
|
209
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
210
|
+
}
|
|
211
|
+
// access validation
|
|
212
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.write, body.query, ctx.state.user);
|
|
213
|
+
if (!hasAccess)
|
|
214
|
+
ctx.throw(403, 'access denied');
|
|
215
|
+
// collection validation
|
|
216
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
217
|
+
if (collection == null) {
|
|
218
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
219
|
+
}
|
|
220
|
+
// get removing doc as output for triggers
|
|
221
|
+
const output = await collection.findOne(body.query).exec().then();
|
|
222
|
+
// operate on db
|
|
223
|
+
await collection
|
|
224
|
+
.updateOne(body.query, body.update, body.options)
|
|
225
|
+
.exec()
|
|
226
|
+
.then(writeOpResult => {
|
|
227
|
+
// Call trigger
|
|
228
|
+
service.triggers.call('update-one', body.database, body.collection, {
|
|
229
|
+
query: body.query,
|
|
230
|
+
update: body.update,
|
|
231
|
+
queryResult: writeOpResult,
|
|
232
|
+
});
|
|
233
|
+
ctx.body = { data: writeOpResult };
|
|
234
|
+
})
|
|
235
|
+
.catch(err => {
|
|
236
|
+
ctx.status = err.status || 500;
|
|
237
|
+
ctx.body = err.message;
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
dataProvider.post('/insert-one', async (ctx) => {
|
|
241
|
+
const body = ctx.request.body;
|
|
242
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection doc');
|
|
243
|
+
// fields validation
|
|
244
|
+
if (!bodyValidate.isValid) {
|
|
245
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
246
|
+
}
|
|
247
|
+
// access validation
|
|
248
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.write, body.doc, ctx.state.user);
|
|
249
|
+
if (!hasAccess) {
|
|
250
|
+
console.log(body);
|
|
251
|
+
console.log(ctx.state.user.permission);
|
|
252
|
+
ctx.throw(403, 'access denied');
|
|
253
|
+
}
|
|
254
|
+
// collection validation
|
|
255
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
256
|
+
if (collection == null) {
|
|
257
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
258
|
+
}
|
|
259
|
+
// operate on db
|
|
260
|
+
await new collection(body.doc)
|
|
261
|
+
.save()
|
|
262
|
+
.then(async (newDoc) => {
|
|
263
|
+
// Call trigger
|
|
264
|
+
service.triggers.call('insert-one', body.database, body.collection, {
|
|
265
|
+
doc: body.doc,
|
|
266
|
+
queryResult: newDoc,
|
|
267
|
+
});
|
|
268
|
+
ctx.body = { data: newDoc };
|
|
269
|
+
})
|
|
270
|
+
.catch(err => {
|
|
271
|
+
ctx.status = err.status || 500;
|
|
272
|
+
ctx.body = err.message;
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
dataProvider.post('/remove-one', async (ctx) => {
|
|
276
|
+
const body = ctx.request.body;
|
|
277
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection query');
|
|
278
|
+
// fields validation
|
|
279
|
+
if (!bodyValidate.isValid) {
|
|
280
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
281
|
+
}
|
|
282
|
+
// access validation
|
|
283
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.write, body.query, ctx.state.user);
|
|
284
|
+
if (!hasAccess)
|
|
285
|
+
ctx.throw(403, 'access denied');
|
|
286
|
+
// collection validation
|
|
287
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
288
|
+
if (collection == null) {
|
|
289
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
290
|
+
}
|
|
291
|
+
// get removing doc as output for triggers
|
|
292
|
+
const output = await collection.findOne(body.query).exec().then();
|
|
293
|
+
// operate on db
|
|
294
|
+
await collection
|
|
295
|
+
.deleteOne(body.query)
|
|
296
|
+
.exec()
|
|
297
|
+
.then(async (result) => {
|
|
298
|
+
// Call trigger
|
|
299
|
+
service.triggers.call('remove-one', body.database, body.collection, {
|
|
300
|
+
query: body.query,
|
|
301
|
+
queryResult: result,
|
|
302
|
+
});
|
|
303
|
+
ctx.body = { data: result };
|
|
304
|
+
})
|
|
305
|
+
.catch((err) => {
|
|
306
|
+
ctx.status = err.status || 500;
|
|
307
|
+
ctx.body = err.message;
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
dataProvider.post('/aggregate', async (ctx) => {
|
|
311
|
+
const body = ctx.request.body;
|
|
312
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection accessQuery');
|
|
313
|
+
// fields validation
|
|
314
|
+
if (!bodyValidate.isValid) {
|
|
315
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
316
|
+
}
|
|
317
|
+
// access validation
|
|
318
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.read, body.accessQuery, ctx.state.user);
|
|
319
|
+
if (!hasAccess)
|
|
320
|
+
ctx.throw(403, 'access denied');
|
|
321
|
+
// collection validation
|
|
322
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
323
|
+
if (collection == null) {
|
|
324
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
325
|
+
}
|
|
326
|
+
// operate on db
|
|
327
|
+
await collection
|
|
328
|
+
.aggregate(body.pipelines)
|
|
329
|
+
.exec()
|
|
330
|
+
.then(async (result) => {
|
|
331
|
+
// Call trigger
|
|
332
|
+
service.triggers.call('aggregate', body.database, body.collection, {
|
|
333
|
+
pipelines: body.pipelines,
|
|
334
|
+
queryResult: result,
|
|
335
|
+
});
|
|
336
|
+
ctx.body = { data: result };
|
|
337
|
+
})
|
|
338
|
+
.catch((err) => {
|
|
339
|
+
ctx.status = err.status || 500;
|
|
340
|
+
ctx.body = err.message;
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
dataProvider.post('/findByIds', async (ctx, next) => {
|
|
344
|
+
const body = ctx.request.body;
|
|
345
|
+
const bodyValidate = (0, validator_1.validateObject)(body, 'database collection ids');
|
|
346
|
+
// fields validation
|
|
347
|
+
if (!bodyValidate.isValid) {
|
|
348
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: bodyValidate.requires })));
|
|
349
|
+
}
|
|
350
|
+
// access validation
|
|
351
|
+
const hasAccess = service.checkAccess(body.database, body.collection, security_1.AccessTypes.read, body.accessQuery || {}, ctx.state.user);
|
|
352
|
+
if (!hasAccess)
|
|
353
|
+
ctx.throw(403, 'access denied');
|
|
354
|
+
// collection validation
|
|
355
|
+
const collection = service.getCollection(body.database, body.collection);
|
|
356
|
+
if (collection == null) {
|
|
357
|
+
ctx.throw(412, JSON.stringify((0, reply_1.create)('e', { error: 'wrong database or collection' })));
|
|
358
|
+
}
|
|
359
|
+
const or = [];
|
|
360
|
+
try {
|
|
361
|
+
body.ids.forEach((id) => {
|
|
362
|
+
const castedid = service.getAsID(id);
|
|
363
|
+
or.push({ _id: castedid });
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
console.log('ids.forEach', e);
|
|
368
|
+
}
|
|
369
|
+
const pipelines = [
|
|
370
|
+
{
|
|
371
|
+
$match: { $or: or },
|
|
372
|
+
},
|
|
373
|
+
// {
|
|
374
|
+
// $sort: body.sort || { _id: 1 }
|
|
375
|
+
// }
|
|
376
|
+
];
|
|
377
|
+
// operate on db
|
|
378
|
+
await collection
|
|
379
|
+
.aggregate(pipelines)
|
|
380
|
+
.exec()
|
|
381
|
+
.then(async (result) => {
|
|
382
|
+
ctx.state = { data: result };
|
|
383
|
+
await next();
|
|
384
|
+
})
|
|
385
|
+
.catch((err) => {
|
|
386
|
+
ctx.status = err.status || 500;
|
|
387
|
+
ctx.body = err.message;
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
// Final middleware for converting mongoose documents to JSON
|
|
391
|
+
dataProvider.use('/', async (ctx, next) => {
|
|
392
|
+
// this event is responsible to covert whole mongoose doc to json form
|
|
393
|
+
// including getters, public properties
|
|
394
|
+
// each mongoose doc must have a "toJson" method being defined on its own Schema.
|
|
395
|
+
const state = ctx.state;
|
|
396
|
+
// let result;
|
|
397
|
+
// // array
|
|
398
|
+
// if(!isNaN(state.length)) {
|
|
399
|
+
// result = [];
|
|
400
|
+
// for (let index = 0; index < state.length; index++) {
|
|
401
|
+
// const element = state[index];
|
|
402
|
+
// if(element.hasOwnProperty('toJson'))
|
|
403
|
+
// result.push(element.toJson());
|
|
404
|
+
// else result.push(element);
|
|
405
|
+
// }
|
|
406
|
+
// }
|
|
407
|
+
// // object
|
|
408
|
+
// else {
|
|
409
|
+
// result = state.toJson();
|
|
410
|
+
// }
|
|
411
|
+
ctx.body = state;
|
|
412
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import mongoose, { Model, PopulateOptions, Query } from 'mongoose';
|
|
2
|
+
import triggerOperator from '../../class/trigger_operator';
|
|
3
|
+
import TypeCasters from './typeCasters';
|
|
4
|
+
import { CollectionDefinition } from '../../class/collection_definition';
|
|
5
|
+
import { User } from '../../class/user';
|
|
6
|
+
/**
|
|
7
|
+
* Service name constant
|
|
8
|
+
* @constant {string}
|
|
9
|
+
*/
|
|
10
|
+
export declare const name = "dataProvider";
|
|
11
|
+
/**
|
|
12
|
+
* MongoDB connection options
|
|
13
|
+
* @interface MongoOption
|
|
14
|
+
* @property {string} [dbPrefix] - Prefix for database names
|
|
15
|
+
* @property {string} mongoBaseAddress - MongoDB connection URL
|
|
16
|
+
*/
|
|
17
|
+
export interface MongoOption {
|
|
18
|
+
dbPrefix?: string;
|
|
19
|
+
mongoBaseAddress: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Collection definition options
|
|
23
|
+
* @interface CollectionDefinitionOption
|
|
24
|
+
* @property {CollectionDefinition[]} list - List of collection definitions
|
|
25
|
+
* @property {MongoOption} mongoOption - MongoDB connection options
|
|
26
|
+
*/
|
|
27
|
+
interface CollectionDefinitionListOption {
|
|
28
|
+
list: CollectionDefinition[];
|
|
29
|
+
mongoOption: MongoOption;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Adds collection definitions and connects to their respective databases
|
|
33
|
+
* @function addCollectionDefinitionByList
|
|
34
|
+
* @param {CollectionDefinitionListOption} options - Collection definition options
|
|
35
|
+
* @returns {Promise<void>} A promise that resolves when all collections are set up
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* await addCollectionDefinitionByList({
|
|
39
|
+
* list: [
|
|
40
|
+
* new CollectionDefinition({
|
|
41
|
+
* database: 'myapp',
|
|
42
|
+
* collection: 'users',
|
|
43
|
+
* schema: userSchema,
|
|
44
|
+
* permissions: [new Permission({ type: 'user_access', read: true })]
|
|
45
|
+
* })
|
|
46
|
+
* ],
|
|
47
|
+
* mongoOption: {
|
|
48
|
+
* mongoBaseAddress: 'mongodb://localhost:27017',
|
|
49
|
+
* dbPrefix: 'myapp_'
|
|
50
|
+
* }
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function addCollectionDefinitionByList({ list, mongoOption, }: CollectionDefinitionListOption): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Gets a Mongoose model for a specific collection
|
|
57
|
+
* @function getCollection
|
|
58
|
+
* @param {string} db - Database name
|
|
59
|
+
* @param {string} collection - Collection name
|
|
60
|
+
* @returns {Model<T>} Mongoose model for the collection
|
|
61
|
+
* @throws {Error} If the collection doesn't exist
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const userModel = getCollection('myapp', 'users');
|
|
65
|
+
* const users = await userModel.find();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function getCollection<T>(db: string, collection: string): Model<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Checks if a user has access to perform an operation on a collection
|
|
71
|
+
* @function checkAccess
|
|
72
|
+
* @param {string} db - Database name
|
|
73
|
+
* @param {string} collection - Collection name
|
|
74
|
+
* @param {string} operationType - Type of operation (read/write)
|
|
75
|
+
* @param {Record<string, any>} queryOrDoc - Query or document being accessed
|
|
76
|
+
* @param {User} user - User performing the operation
|
|
77
|
+
* @returns {boolean} Whether the user has access
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const hasAccess = checkAccess('myapp', 'users', 'read', {}, currentUser);
|
|
81
|
+
* if (hasAccess) {
|
|
82
|
+
* const users = await getCollection('myapp', 'users').find();
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function checkAccess(db: string, collection: string, operationType: string, queryOrDoc: Record<string, any>, user: User): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Converts a string ID to a MongoDB ObjectId
|
|
89
|
+
* @function getAsID
|
|
90
|
+
* @param {string} strId - String ID to convert
|
|
91
|
+
* @returns {mongoose.Types.ObjectId | undefined} MongoDB ObjectId or undefined if invalid
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const id = getAsID('507f1f77bcf86cd799439011');
|
|
95
|
+
* if (id) {
|
|
96
|
+
* const doc = await collection.findById(id);
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function getAsID(strId: string): mongoose.Types.ObjectId | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Applies populate options to a Mongoose query
|
|
103
|
+
* @function performPopulateToQueryObject
|
|
104
|
+
* @param {Query<T, any>} queryObj - Mongoose query object
|
|
105
|
+
* @param {PopulateOptions[]} [popArr=[]] - Array of populate options
|
|
106
|
+
* @returns {Query<T, any>} Query with populate options applied
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const query = collection.find();
|
|
110
|
+
* const populatedQuery = performPopulateToQueryObject(query, [
|
|
111
|
+
* { path: 'author', select: 'name email' }
|
|
112
|
+
* ]);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare function performPopulateToQueryObject<T = any>(queryObj: Query<T, any>, popArr?: PopulateOptions[]): Query<T, any>;
|
|
116
|
+
/**
|
|
117
|
+
* Applies additional options to a Mongoose query
|
|
118
|
+
* @function performAdditionalOptionsToQueryObject
|
|
119
|
+
* @param {Query<T, any>} queryObj - Mongoose query object
|
|
120
|
+
* @param {Record<string, any>} options - Additional query options
|
|
121
|
+
* @returns {Query<T, any>} Query with additional options applied
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const query = collection.find();
|
|
125
|
+
* const queryWithOptions = performAdditionalOptionsToQueryObject(query, {
|
|
126
|
+
* sort: { createdAt: -1 },
|
|
127
|
+
* limit: 10
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare function performAdditionalOptionsToQueryObject<T = any>(queryObj: Query<T, any>, options: Record<string, any>): Query<T, any>;
|
|
132
|
+
export { triggerOperator as triggers, TypeCasters };
|