@modular-rest/server 1.6.6 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +8 -3
- package/src/application.js +32 -68
- package/src/class/collection_definition.js +32 -24
- package/src/class/combinator.js +82 -71
- package/src/class/database_trigger.js +16 -12
- package/src/class/security.js +113 -31
- package/src/class/trigger_operator.js +35 -31
- package/src/index.js +3 -21
- package/src/middlewares.js +14 -1
- package/src/services/data_provider/service.js +175 -177
- package/src/services/user_manager/service.js +5 -1
- package/tsconfig.json +10 -0
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ a nodejs module based on KOAJS for developing Rest-APIs in a modular solution.
|
|
|
6
6
|
this module has one method:
|
|
7
7
|
- `createRest`: is the main functionality of the module.
|
|
8
8
|
|
|
9
|
+
**Note**: active "javascript.implicitProjectConfig.checkJs": true in your vscode settings.
|
|
10
|
+
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
13
|
Install using [npm](https://www.npmjs.com/package/modular-rest):
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modular-rest/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
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": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"generate:types": "tsc"
|
|
8
9
|
},
|
|
10
|
+
"types": "./types/index.d.ts",
|
|
9
11
|
"repository": {
|
|
10
12
|
"type": "git",
|
|
11
13
|
"url": "git+https://github.com/navidshad/modular-rest.git"
|
|
@@ -35,5 +37,8 @@
|
|
|
35
37
|
"mongoose": "^5.10.9",
|
|
36
38
|
"nested-property": "^4.0.0"
|
|
37
39
|
},
|
|
38
|
-
"devDependencies": {
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/koa": "^2.14.0",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
}
|
|
39
44
|
}
|
package/src/application.js
CHANGED
|
@@ -1,89 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
const koa = require("koa");
|
|
2
2
|
const cors = require("@koa/cors");
|
|
3
3
|
const koaBody = require("koa-body");
|
|
4
4
|
const koaStatic = require("koa-static-server");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const Combination = require("./class/combinator");
|
|
7
|
+
const DataProvider = require("./services/data_provider/service");
|
|
8
|
+
const UserService = require("./services/user_manager/service");
|
|
9
9
|
|
|
10
10
|
let defaultServiceRoot = __dirname + "/services";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// */
|
|
16
|
-
// rootDir?: string | undefined;
|
|
17
|
-
// /**
|
|
18
|
-
// * optional rewrite path
|
|
19
|
-
// */
|
|
20
|
-
// rootPath?: string | undefined;
|
|
21
|
-
// /**
|
|
22
|
-
// * optional default file to serve if requested static is missing
|
|
23
|
-
// */
|
|
24
|
-
// notFoundFile?: string | undefined;
|
|
25
|
-
// /**
|
|
26
|
-
// * request access log to console
|
|
27
|
-
// */
|
|
28
|
-
// log?: boolean | undefined;
|
|
29
|
-
// /**
|
|
30
|
-
// * don't execute any downstream middleware. defaults to true
|
|
31
|
-
// */
|
|
32
|
-
// last?: boolean | undefined;
|
|
33
|
-
// /**
|
|
34
|
-
// * Browser cache max-age in milliseconds. defaults to 0
|
|
35
|
-
// */
|
|
36
|
-
// maxage?: number | undefined;
|
|
37
|
-
// /**
|
|
38
|
-
// * Allow transfer of hidden files. defaults to false
|
|
39
|
-
// */
|
|
40
|
-
// hidden?: boolean | undefined;
|
|
41
|
-
// /**
|
|
42
|
-
// * Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested
|
|
43
|
-
// */
|
|
44
|
-
// gzip?: boolean | undefined;
|
|
45
|
-
// /**
|
|
46
|
-
// * Try to serve the brotli version of a file automatically when brotli is supported by a client and in the requested
|
|
47
|
-
// */
|
|
48
|
-
// brotli?: boolean | undefined;
|
|
49
|
-
// index?: string | undefined;
|
|
50
|
-
// }
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {import('koa')} Koa
|
|
14
|
+
*/
|
|
51
15
|
|
|
52
16
|
/**
|
|
53
17
|
* @param {{
|
|
54
|
-
* cors
|
|
55
|
-
* modulesPath
|
|
56
|
-
*
|
|
57
|
-
* rootDir
|
|
58
|
-
* rootPath
|
|
59
|
-
* notFoundFile
|
|
60
|
-
* log
|
|
61
|
-
* last
|
|
62
|
-
* maxage
|
|
63
|
-
* hidden
|
|
64
|
-
* gzip
|
|
65
|
-
* brotli
|
|
66
|
-
* index
|
|
18
|
+
* cors?: any; // Options for @koa/cors middleware.
|
|
19
|
+
* modulesPath?: string; // Root directory of your router.js/db.js files.
|
|
20
|
+
* staticPath?: {
|
|
21
|
+
* rootDir?: string; // Root directory of your static files.
|
|
22
|
+
* rootPath?: string; // Root path of your static files.
|
|
23
|
+
* notFoundFile?: string; // Not found file.
|
|
24
|
+
* log?: boolean; // Log requests to console.
|
|
25
|
+
* last?: boolean; // Don't execute any downstream middleware.
|
|
26
|
+
* maxage?: number; // Browser cache max-age in milliseconds.
|
|
27
|
+
* hidden?: boolean; // Allow transfer of hidden files.
|
|
28
|
+
* 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.
|
|
29
|
+
* 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.
|
|
30
|
+
* index?: string; // Index file.
|
|
67
31
|
* };
|
|
68
|
-
* onBeforeInit
|
|
69
|
-
* onAfterInit
|
|
70
|
-
* port
|
|
71
|
-
* dontListen
|
|
72
|
-
* mongo
|
|
32
|
+
* onBeforeInit?: (koaApp:Koa) => void; // A callback called before initializing the Koa server.
|
|
33
|
+
* onAfterInit?: (koaApp:Koa) => void; // A callback called after server initialization.
|
|
34
|
+
* port?: number; // Server port.
|
|
35
|
+
* dontListen?: boolean; // If true, the server will not run and will only return the Koa app object.
|
|
36
|
+
* mongo?: {
|
|
73
37
|
* dbPrefix: string; // A prefix for your database name.
|
|
74
38
|
* mongoBaseAddress: string; // The address of your MongoDB server without any database specification.
|
|
75
|
-
* addressMap
|
|
39
|
+
* addressMap?: string; // Specific addresses for each database.
|
|
76
40
|
* };
|
|
77
|
-
* keypair
|
|
41
|
+
* keypair?: {
|
|
78
42
|
* private: string; // Private key for RSA authentication.
|
|
79
43
|
* public: string; // Public key for RSA authentication.
|
|
80
44
|
* };
|
|
81
|
-
* adminUser
|
|
45
|
+
* adminUser?: {
|
|
82
46
|
* email: string; // Admin user email.
|
|
83
47
|
* password: string; // Admin user password.
|
|
84
48
|
* };
|
|
85
49
|
* verificationCodeGeneratorMethod: () => string; // A method to return a verification code when registering a new user.
|
|
86
|
-
* collectionDefinitions
|
|
50
|
+
* collectionDefinitions?: CollectionDefinition[]; // An array of additional collection definitions.
|
|
87
51
|
* }} options
|
|
88
52
|
*/
|
|
89
53
|
module.exports = async function createRest(options) {
|
|
@@ -121,8 +85,8 @@ module.exports = async function createRest(options) {
|
|
|
121
85
|
/**
|
|
122
86
|
* Plug In KoaStatic
|
|
123
87
|
*/
|
|
124
|
-
if (options.
|
|
125
|
-
app.use(koaStatic(options.
|
|
88
|
+
if (options.staticPath) {
|
|
89
|
+
app.use(koaStatic(options.staticPath));
|
|
126
90
|
}
|
|
127
91
|
|
|
128
92
|
/**
|
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* @param {object} option
|
|
7
|
-
* @param {string} option.db database name
|
|
8
|
-
* @param {string} option.collection collection name
|
|
9
|
-
* @param {object} option.schema mongoose schema
|
|
10
|
-
* @param {array} option.permissions a list of Permission for this collection
|
|
11
|
-
* @param {array} option.trigger a DatabaseTrigger
|
|
12
|
-
*/
|
|
13
|
-
constructor({ db, collection, schema, permissions, trigger }) {
|
|
14
|
-
// string
|
|
15
|
-
this.database = db;
|
|
16
|
-
// string
|
|
17
|
-
this.collection = collection;
|
|
18
|
-
// schema object of mongoose
|
|
19
|
-
this.schema = schema;
|
|
20
|
-
// a list of Permission for this collection
|
|
21
|
-
this.permissions = permissions;
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./security.js').Permission} Permission
|
|
3
|
+
* @typedef {import('./database_trigger.js')} DatabaseTrigger
|
|
4
|
+
*/
|
|
22
5
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
class CollectionDefinition {
|
|
7
|
+
/**
|
|
8
|
+
* This class helps to create a mongoose collection
|
|
9
|
+
* associated with permissions and triggers.
|
|
10
|
+
*
|
|
11
|
+
* @class
|
|
12
|
+
* @param {Object} option
|
|
13
|
+
* @param {string} option.db - Database name
|
|
14
|
+
* @param {string} option.collection - Collection name
|
|
15
|
+
* @param {Object} option.schema - Mongoose schema
|
|
16
|
+
* @param {Array<Permission>} option.permissions - A list of permissions for this collection
|
|
17
|
+
* @param {Array<DatabaseTrigger>=} option.trigger - A database trigger
|
|
18
|
+
*/
|
|
19
|
+
constructor({ db, collection, schema, permissions, trigger }) {
|
|
20
|
+
// string
|
|
21
|
+
this.database = db;
|
|
22
|
+
// string
|
|
23
|
+
this.collection = collection;
|
|
24
|
+
// schema object of mongoose
|
|
25
|
+
this.schema = schema;
|
|
26
|
+
// a list of Permission for this collection
|
|
27
|
+
this.permissions = permissions;
|
|
28
|
+
|
|
29
|
+
this.trigger = trigger;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = CollectionDefinition;
|
package/src/class/combinator.js
CHANGED
|
@@ -1,90 +1,101 @@
|
|
|
1
|
-
let Router = require(
|
|
2
|
-
let directory = require(
|
|
1
|
+
let Router = require("koa-router");
|
|
2
|
+
let directory = require("./directory.js");
|
|
3
3
|
|
|
4
4
|
class Combinator {
|
|
5
|
+
async combineRoutesByFilePath(rootDirectory, app) {
|
|
6
|
+
// find route paths
|
|
7
|
+
let option = { name: "router", filter: [".js"] };
|
|
8
|
+
let routerPaths = await directory
|
|
9
|
+
.find(rootDirectory, option)
|
|
10
|
+
.then()
|
|
11
|
+
.catch((e) => {
|
|
12
|
+
console.log(e);
|
|
13
|
+
});
|
|
5
14
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.catch(e => { console.log(e) });
|
|
15
|
+
// create and combine routes into the app
|
|
16
|
+
for (let i = 0; i < routerPaths.length; i++) {
|
|
17
|
+
let service = require(routerPaths[i]);
|
|
18
|
+
let name = service.name;
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let service = require(routerPaths[i]);
|
|
15
|
-
let name = service.name;
|
|
20
|
+
var serviceRouter = new Router();
|
|
21
|
+
serviceRouter.use(`/${name}`, service.main.routes());
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
var serviceRouter = new Router();
|
|
19
|
-
serviceRouter.use(`/${name}`, service.main.routes());
|
|
20
|
-
|
|
21
|
-
app.use(serviceRouter.routes());
|
|
22
|
-
}
|
|
23
|
+
app.use(serviceRouter.routes());
|
|
23
24
|
}
|
|
25
|
+
}
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {object} option
|
|
30
|
+
* @param {string} option.rootDirectory root directory of files
|
|
31
|
+
* @param {object} option.filename an object of {name, extension}
|
|
32
|
+
* @param {string} option.filename.name name of file
|
|
33
|
+
* @param {string} option.filename.extension the extension of the file
|
|
34
|
+
* @param {boolean} option.combineWithRoot combine all file content and return theme as a object
|
|
35
|
+
* @param {boolean} option.convertToArray return file content as an array instead an object
|
|
36
|
+
*/
|
|
37
|
+
async combineModulesByFilePath({
|
|
38
|
+
rootDirectory,
|
|
39
|
+
filename,
|
|
40
|
+
combineWithRoot,
|
|
41
|
+
convertToArray,
|
|
42
|
+
}) {
|
|
43
|
+
// find route paths
|
|
44
|
+
let rootObject_temp;
|
|
45
|
+
let option = { name: filename.name, filter: [filename.extension] };
|
|
46
|
+
let modulesPath = await directory
|
|
47
|
+
.find(rootDirectory, option)
|
|
48
|
+
.then()
|
|
49
|
+
.catch((e) => {
|
|
50
|
+
console.log(e);
|
|
51
|
+
});
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
// create and combine routes into the app
|
|
54
|
+
for (let i = 0; i < modulesPath.length; i++) {
|
|
55
|
+
let moduleObject = require(modulesPath[i]);
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
delete moduleObject.name;
|
|
57
|
+
//act by otherOption
|
|
58
|
+
if (combineWithRoot) {
|
|
59
|
+
if (moduleObject.name) delete moduleObject.name;
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
rootObject_temp = [...rootObject_temp, ...moduleObject];
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
rootObject_temp = this.extendObj(rootObject_temp, moduleObject);
|
|
60
|
-
}
|
|
61
|
-
// else if (typeof)
|
|
62
|
-
}
|
|
63
|
-
// default act
|
|
64
|
-
else {
|
|
65
|
-
let name = moduleObject.name;
|
|
66
|
-
rootObject_temp[name] = moduleObject;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
61
|
+
if (moduleObject.length) {
|
|
62
|
+
if (!rootObject_temp) rootObject_temp = [];
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
rootObject_temp = Object.values(rootObject_temp);
|
|
64
|
+
rootObject_temp = [...rootObject_temp, ...moduleObject];
|
|
65
|
+
} else {
|
|
66
|
+
rootObject_temp = this.extendObj(rootObject_temp, moduleObject);
|
|
74
67
|
}
|
|
68
|
+
// else if (typeof)
|
|
69
|
+
}
|
|
70
|
+
// default act
|
|
71
|
+
else {
|
|
72
|
+
let name = moduleObject.name;
|
|
73
|
+
rootObject_temp[name] = moduleObject;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
// options
|
|
78
|
+
// convertToArray
|
|
79
|
+
if (convertToArray) {
|
|
80
|
+
rootObject_temp = Object.values(rootObject_temp);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
// set result to main rootObject
|
|
84
|
+
return rootObject_temp;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
extendObj(obj, src) {
|
|
88
|
+
for (var key in src) {
|
|
89
|
+
if (src.hasOwnProperty(key)) obj[key] = src[key];
|
|
86
90
|
}
|
|
91
|
+
return obj;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static get instance() {
|
|
95
|
+
return instance;
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
|
-
|
|
99
|
+
const instance = new Combinator();
|
|
100
|
+
|
|
90
101
|
module.exports = Combinator.instance;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DatabaseTrigger` is a class that defines a callback to be called on a specific database transaction.
|
|
3
|
+
*
|
|
4
|
+
* @class
|
|
5
|
+
*/
|
|
1
6
|
class DatabaseTrigger {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new instance of `DatabaseTrigger`.
|
|
9
|
+
*
|
|
10
|
+
* @param {'find' | 'find-one' | 'count' | 'update-one' | 'insert-one' | 'remove-one' | 'aggregate'} operation - The operation to be triggered. Supported operations are:
|
|
11
|
+
* @param {function(query, queryResult)} callback - The callback to be called when the operation is executed.
|
|
12
|
+
*/
|
|
13
|
+
constructor(operation, callback = (query, queryResult) => {}) {
|
|
14
|
+
this.operation = operation;
|
|
15
|
+
this.callback = callback;
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
module.exports = DatabaseTrigger;
|
|
19
|
+
module.exports = DatabaseTrigger;
|
package/src/class/security.js
CHANGED
|
@@ -1,44 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class representing an access definition.
|
|
3
|
+
*/
|
|
1
4
|
class AccessDefinition {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Create an access definition.
|
|
7
|
+
* @param {Object} options - The options for the access definition.
|
|
8
|
+
* @param {string} options.database - The name of the database.
|
|
9
|
+
* @param {string} options.collection - The name of the collection.
|
|
10
|
+
* @param {Array.<Permission>} options.permissionList - The list of permissions.
|
|
11
|
+
*/
|
|
12
|
+
constructor({ database, collection, permissionList }) {
|
|
13
|
+
this.database = database;
|
|
14
|
+
this.collection = collection;
|
|
15
|
+
this.permissionList = permissionList;
|
|
16
|
+
}
|
|
7
17
|
}
|
|
8
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {('god_access'|'user_access'|'upload_file_access'|'remove_file_access'|'anonymous_access')} PermissionType
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Class representing a permission.
|
|
25
|
+
*/
|
|
9
26
|
class Permission {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Create a permission.
|
|
29
|
+
* @param {Object} options - The options for the permission.
|
|
30
|
+
* @param {PermissionType} options.type - The type of the permission.
|
|
31
|
+
* @param {boolean} [options.read=false] - The read access of the permission.
|
|
32
|
+
* @param {boolean} [options.write=false] - The write access of the permission.
|
|
33
|
+
* @param {boolean} [options.onlyOwnData=false] - If true, users can perform CRUD on documents that they created already.
|
|
34
|
+
* @param {string} [options.ownerIdField='refId'] - The name of the field that contains the owner's id of the document.
|
|
35
|
+
*/
|
|
36
|
+
constructor({
|
|
37
|
+
type,
|
|
38
|
+
read = false,
|
|
39
|
+
write = false,
|
|
40
|
+
onlyOwnData = false,
|
|
41
|
+
ownerIdField = "refId",
|
|
42
|
+
}) {
|
|
43
|
+
this.type = type;
|
|
44
|
+
this.read = read;
|
|
45
|
+
this.write = write;
|
|
46
|
+
this.onlyOwnData = onlyOwnData;
|
|
47
|
+
this.ownerIdField = ownerIdField;
|
|
48
|
+
}
|
|
19
49
|
}
|
|
20
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Class representing different types of permissions.
|
|
53
|
+
* Each static getter returns a string that represents a specific type of permission.
|
|
54
|
+
*/
|
|
21
55
|
class PermissionTypes {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Create permission types.
|
|
58
|
+
* Each property represents a specific type of permission.
|
|
59
|
+
*/
|
|
60
|
+
constructor() {
|
|
61
|
+
this.god_access = "god_access"; // Represents god access permission type
|
|
62
|
+
this.user_access = "user_access"; // Represents user access permission type
|
|
63
|
+
this.upload_file_access = "upload_file_access"; // Represents upload file access permission type
|
|
64
|
+
this.remove_file_access = "remove_file_access"; // Represents remove file access permission type
|
|
65
|
+
this.anonymous_access = "anonymous_access"; // Represents anonymous access permission type
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the string representing god access permission type.
|
|
70
|
+
* @return {string} The god access permission type.
|
|
71
|
+
*/
|
|
72
|
+
static get god_access() {
|
|
73
|
+
return "god_access";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the string representing user access permission type.
|
|
78
|
+
* @return {string} The user access permission type.
|
|
79
|
+
*/
|
|
80
|
+
static get user_access() {
|
|
81
|
+
return "user_access";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the string representing upload file access permission type.
|
|
86
|
+
* @return {string} The upload file access permission type.
|
|
87
|
+
*/
|
|
88
|
+
static get upload_file_access() {
|
|
89
|
+
return "upload_file_access";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the string representing remove file access permission type.
|
|
94
|
+
* @return {string} The remove file access permission type.
|
|
95
|
+
*/
|
|
96
|
+
static get remove_file_access() {
|
|
97
|
+
return "remove_file_access";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the string representing anonymous access permission type.
|
|
102
|
+
* @return {string} The anonymous access permission type.
|
|
103
|
+
*/
|
|
104
|
+
static get anonymous_access() {
|
|
105
|
+
return "anonymous_access";
|
|
106
|
+
}
|
|
35
107
|
}
|
|
36
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Class representing access types.
|
|
111
|
+
*/
|
|
37
112
|
class AccessTypes {
|
|
38
|
-
|
|
39
|
-
|
|
113
|
+
static get read() {
|
|
114
|
+
return "read";
|
|
115
|
+
}
|
|
116
|
+
static get write() {
|
|
117
|
+
return "write";
|
|
118
|
+
}
|
|
40
119
|
}
|
|
41
120
|
|
|
42
121
|
module.exports = {
|
|
43
|
-
|
|
44
|
-
|
|
122
|
+
AccessDefinition,
|
|
123
|
+
Permission,
|
|
124
|
+
PermissionTypes,
|
|
125
|
+
AccessTypes,
|
|
126
|
+
};
|
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
class TriggerOperator {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
constructor() {
|
|
3
|
+
this.triggers = [];
|
|
4
|
+
}
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
/**
|
|
7
|
+
* add a collection trigger
|
|
8
|
+
* @param {object} trigger DatabaseTrigger object
|
|
9
|
+
*/
|
|
10
|
+
addTrigger(trigger) {
|
|
11
|
+
this.triggers.push(trigger);
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Call a trigger
|
|
16
|
+
* @param {string} operation operation name
|
|
17
|
+
* @param {string} database database name
|
|
18
|
+
* @param {string} collection collection name
|
|
19
|
+
* @param {string} data
|
|
20
|
+
*/
|
|
21
|
+
call(operation, database, collection, data) {
|
|
22
|
+
let result;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
this.triggers.forEach((trigger) => {
|
|
25
|
+
if (
|
|
26
|
+
operation == trigger.operation &&
|
|
27
|
+
database == trigger.database &&
|
|
28
|
+
collection == trigger.collection
|
|
29
|
+
)
|
|
30
|
+
result = trigger.callback(data.input, data.output);
|
|
31
|
+
});
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static get instance() {
|
|
37
|
+
return instance;
|
|
38
|
+
}
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
module.exports = TriggerOperator.instance;
|
|
41
|
+
const instance = new TriggerOperator();
|
|
42
|
+
module.exports = TriggerOperator.instance;
|
package/src/index.js
CHANGED
|
@@ -14,47 +14,29 @@ const CollectionDefinition = require("./class/collection_definition");
|
|
|
14
14
|
const Schemas = require("./class/db_schemas");
|
|
15
15
|
const DatabaseTrigger = require("./class/database_trigger");
|
|
16
16
|
const SecurityClass = require("./class/security");
|
|
17
|
-
|
|
18
17
|
const middleware = require("./middlewares");
|
|
19
18
|
const userManager = require("./services/user_manager/service");
|
|
20
19
|
|
|
21
20
|
module.exports = {
|
|
22
21
|
createRest,
|
|
23
22
|
|
|
24
|
-
//
|
|
25
|
-
// Utilities
|
|
26
|
-
//
|
|
23
|
+
// Route utilities
|
|
27
24
|
reply,
|
|
28
25
|
TypeCasters,
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @type {import('./class/paginator').create}
|
|
32
|
-
*/
|
|
33
26
|
paginator,
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @type {import('./class/validator')}
|
|
37
|
-
*/
|
|
38
27
|
validator,
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
* @type {import('./services/data_provider/service').getCollection}
|
|
42
|
-
* @return {import('mongoose').Model} Mongoose model https://mongoosejs.com/docs/api/model.html
|
|
43
|
-
*/
|
|
29
|
+
// Service utilities
|
|
44
30
|
getCollection,
|
|
45
31
|
|
|
46
|
-
//
|
|
47
|
-
// Base class
|
|
48
|
-
//
|
|
32
|
+
// Database
|
|
49
33
|
CollectionDefinition,
|
|
50
34
|
Schemas,
|
|
51
35
|
Schema,
|
|
52
36
|
DatabaseTrigger,
|
|
53
|
-
|
|
54
37
|
...SecurityClass,
|
|
55
38
|
|
|
56
39
|
// Middlewares
|
|
57
40
|
middleware,
|
|
58
|
-
|
|
59
41
|
userManager: userManager.main,
|
|
60
42
|
};
|
package/src/middlewares.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator module
|
|
3
|
+
* @module class/validator
|
|
4
|
+
*/
|
|
1
5
|
let validateObject = require("./class/validator");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* User manager service
|
|
9
|
+
* @module services/user_manager/service
|
|
10
|
+
*/
|
|
2
11
|
const userManager = require("./services/user_manager/service");
|
|
3
12
|
|
|
4
13
|
/**
|
|
@@ -9,7 +18,7 @@ const userManager = require("./services/user_manager/service");
|
|
|
9
18
|
* @param {Function} next - Koa next function
|
|
10
19
|
* @returns {Promise<void>}
|
|
11
20
|
*/
|
|
12
|
-
|
|
21
|
+
async function auth(ctx, next) {
|
|
13
22
|
let headers = ctx.header;
|
|
14
23
|
let headersValidated = validateObject(headers, "authorization");
|
|
15
24
|
|
|
@@ -27,4 +36,8 @@ module.exports.auth = async (ctx, next) => {
|
|
|
27
36
|
console.log(err);
|
|
28
37
|
ctx.throw(err.status || 412, err.message);
|
|
29
38
|
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
auth,
|
|
30
43
|
};
|
|
@@ -1,196 +1,195 @@
|
|
|
1
|
-
let name =
|
|
2
|
-
const colog = require(
|
|
3
|
-
let {
|
|
4
|
-
AccessTypes,
|
|
5
|
-
AccessDefinition
|
|
6
|
-
} = require('../../class/security');
|
|
1
|
+
let name = "dataProvider";
|
|
2
|
+
const colog = require("colog");
|
|
3
|
+
let { AccessTypes, AccessDefinition } = require("../../class/security");
|
|
7
4
|
|
|
8
|
-
const Mongoose = require(
|
|
9
|
-
Mongoose.set(
|
|
5
|
+
const Mongoose = require("mongoose");
|
|
6
|
+
Mongoose.set("useCreateIndex", true);
|
|
10
7
|
|
|
11
8
|
let connections = {};
|
|
12
9
|
let collections = {};
|
|
13
10
|
let permissionDefinitions = {};
|
|
14
11
|
|
|
15
|
-
let triggers = require(
|
|
16
|
-
let TypeCasters = require(
|
|
12
|
+
let triggers = require("../../class/trigger_operator");
|
|
13
|
+
let TypeCasters = require("./typeCasters");
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
*
|
|
16
|
+
*
|
|
20
17
|
* @param {string} dbName database name
|
|
21
18
|
* @param {array} CollectionDefinitionList an array of CollectionDefinition instance
|
|
22
19
|
* @param {object} mongoOption
|
|
23
20
|
* @param {string} mongoOption.dbPrefix
|
|
24
21
|
* @param {string} mongoOption.mongoBaseAddress
|
|
25
22
|
*/
|
|
26
|
-
function connectToDatabaseByCollectionDefinitionList(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
23
|
+
function connectToDatabaseByCollectionDefinitionList(
|
|
24
|
+
dbName,
|
|
25
|
+
collectionDefinitionList = [],
|
|
26
|
+
mongoOption
|
|
27
|
+
) {
|
|
28
|
+
return new Promise((done, reject) => {
|
|
29
|
+
// Create db connection
|
|
30
|
+
//
|
|
31
|
+
const fullDbName = (mongoOption.dbPrefix || "") + dbName;
|
|
32
|
+
const connectionString = mongoOption.mongoBaseAddress + "/" + fullDbName;
|
|
33
|
+
|
|
34
|
+
colog.info(`- Connecting to database ${connectionString}`);
|
|
35
|
+
|
|
36
|
+
let connection = Mongoose.createConnection(connectionString, {
|
|
37
|
+
...mongoOption,
|
|
38
|
+
useUnifiedTopology: true,
|
|
39
|
+
useNewUrlParser: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Store connection
|
|
43
|
+
connections[dbName] = connection;
|
|
44
|
+
|
|
45
|
+
// add db models from schemas
|
|
46
|
+
collectionDefinitionList.forEach((collectionDefinition) => {
|
|
47
|
+
let collection = collectionDefinition.collection;
|
|
48
|
+
let schema = collectionDefinition.schema;
|
|
49
|
+
|
|
50
|
+
if (collections[dbName] == undefined) collections[dbName] = {};
|
|
51
|
+
|
|
52
|
+
if (permissionDefinitions[dbName] == undefined)
|
|
53
|
+
permissionDefinitions[dbName] = {};
|
|
54
|
+
|
|
55
|
+
// create model from schema
|
|
56
|
+
// and store in on global collection object
|
|
57
|
+
let model = connection.model(collection, schema);
|
|
58
|
+
collections[dbName][collection] = model;
|
|
59
|
+
|
|
60
|
+
// define Access Definition from component permissions
|
|
61
|
+
// and store it on global access definition object
|
|
62
|
+
permissionDefinitions[dbName][collection] = new AccessDefinition({
|
|
63
|
+
database: dbName,
|
|
64
|
+
collection: collection,
|
|
65
|
+
permissionList: collectionDefinition.permissions,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// add trigger
|
|
69
|
+
if (collectionDefinition.trigger != undefined) {
|
|
70
|
+
triggers.addTrigger(collectionDefinition.trigger);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
connection.on("connected", () => {
|
|
75
|
+
colog.success(`- ${fullDbName} database has been connected`);
|
|
76
|
+
done();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
/**
|
|
85
|
-
*
|
|
82
|
+
*
|
|
86
83
|
* @param {object} option
|
|
87
84
|
* @param {array} option.list an array of CollectionDefinition instance
|
|
88
85
|
* @param {object} option.mongoOption
|
|
89
86
|
* @param {string} option.mongoOption.dbPrefix
|
|
90
87
|
* @param {string} option.mongoOption.mongoBaseAddress
|
|
91
88
|
*/
|
|
92
|
-
async function addCollectionDefinitionByList({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
89
|
+
async function addCollectionDefinitionByList({ list, mongoOption }) {
|
|
90
|
+
let clusteredByDBName = {};
|
|
91
|
+
|
|
92
|
+
// cluster list by their database name.
|
|
93
|
+
list.forEach((collectionDefinition) => {
|
|
94
|
+
let database = collectionDefinition.database;
|
|
95
|
+
if (!clusteredByDBName[database]) clusteredByDBName[database] = [];
|
|
96
|
+
clusteredByDBName[database].push(collectionDefinition);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// connect to databases
|
|
100
|
+
for (const dbName in clusteredByDBName) {
|
|
101
|
+
if (clusteredByDBName.hasOwnProperty(dbName)) {
|
|
102
|
+
const collectionDefinitionList = clusteredByDBName[dbName];
|
|
103
|
+
await connectToDatabaseByCollectionDefinitionList(
|
|
104
|
+
dbName,
|
|
105
|
+
collectionDefinitionList,
|
|
106
|
+
mongoOption
|
|
107
|
+
);
|
|
111
108
|
}
|
|
109
|
+
}
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Get a collection from a database.
|
|
114
|
+
* @param {string} db - The database name.
|
|
115
|
+
* @param {string} collection - The collection name.
|
|
116
|
+
* @returns {import('mongoose').Model} The found collection.
|
|
117
|
+
*/
|
|
114
118
|
function getCollection(db, collection) {
|
|
115
|
-
|
|
119
|
+
let fountCollection;
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
if (collections.hasOwnProperty(db)) {
|
|
122
|
+
if (collections[db].hasOwnProperty(collection))
|
|
123
|
+
fountCollection = collections[db][collection];
|
|
124
|
+
}
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
return fountCollection;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
function _getPermissionList(db, collection, operationType) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
permissionList.push(permission);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
130
|
+
let permissionList = [];
|
|
131
|
+
let permissionDefinition;
|
|
132
|
+
|
|
133
|
+
if (!permissionDefinitions.hasOwnProperty(db)) return permissionList;
|
|
134
|
+
|
|
135
|
+
permissionDefinition = permissionDefinitions[db][collection];
|
|
136
|
+
|
|
137
|
+
permissionDefinition.permissionList.forEach((permission) => {
|
|
138
|
+
if (permission.onlyOwnData == true) {
|
|
139
|
+
permissionList.push(permission);
|
|
140
|
+
} else if (operationType == AccessTypes.read && permission.read == true) {
|
|
141
|
+
permissionList.push(permission);
|
|
142
|
+
} else if (operationType == AccessTypes.write && permission.write == true) {
|
|
143
|
+
permissionList.push(permission);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
return permissionList;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
function checkAccess(db, collection, operationType, queryOrDoc, user) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
});
|
|
151
|
+
let key = false;
|
|
152
|
+
const permissionList = _getPermissionList(db, collection, operationType);
|
|
153
|
+
|
|
154
|
+
permissionList.forEach((permission) => {
|
|
155
|
+
let permissionType = permission.type;
|
|
156
|
+
|
|
157
|
+
if (permission.onlyOwnData == true) {
|
|
158
|
+
const userId = user.id;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
if (
|
|
162
|
+
queryOrDoc[permission.ownerIdField].toString() === userId.toString()
|
|
163
|
+
)
|
|
164
|
+
key = true;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
key = false;
|
|
167
|
+
}
|
|
168
|
+
} else if (operationType == AccessTypes.read) {
|
|
169
|
+
if (permission.read && user.permission[permissionType] == true)
|
|
170
|
+
key = true;
|
|
171
|
+
} else if (operationType == AccessTypes.write) {
|
|
172
|
+
if (permission.write && user.permission[permissionType] == true)
|
|
173
|
+
key = true;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
177
176
|
|
|
178
|
-
|
|
177
|
+
return key;
|
|
179
178
|
}
|
|
180
179
|
|
|
181
180
|
function getAsID(strId) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
181
|
+
let id;
|
|
182
|
+
try {
|
|
183
|
+
id = Mongoose.Types.ObjectId(strId);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.log("strId did not cast objectId", e);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return id;
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
function performPopulateToQueryObject(queryObj, popArr = []) {
|
|
193
|
-
|
|
192
|
+
/*
|
|
194
193
|
https://mongoosejs.com/docs/populate.html
|
|
195
194
|
popArr must be contains this objects
|
|
196
195
|
{
|
|
@@ -198,36 +197,35 @@ function performPopulateToQueryObject(queryObj, popArr = []) {
|
|
|
198
197
|
select: 'name -_id',
|
|
199
198
|
}
|
|
200
199
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
popArr.forEach((pop) => queryObj.populate(pop));
|
|
201
|
+
return queryObj;
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
function performAdditionalOptionsToQueryObject(queryObj, options) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
205
|
+
/**
|
|
206
|
+
* https://mongoosejs.com/docs/api/query.html#query_Query-sort
|
|
207
|
+
*
|
|
208
|
+
* Options must be contain a method name and an argument of above methods.
|
|
209
|
+
* {
|
|
210
|
+
* sort: '-_id',
|
|
211
|
+
* limit: 10,
|
|
212
|
+
* }
|
|
213
|
+
*/
|
|
214
|
+
Object.keys(options).forEach((method) => {
|
|
215
|
+
queryObj = queryObj[method](options[method]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return queryObj;
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
|
|
223
221
|
module.exports = {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
222
|
+
name,
|
|
223
|
+
getCollection,
|
|
224
|
+
addCollectionDefinitionByList,
|
|
225
|
+
checkAccess,
|
|
226
|
+
getAsID,
|
|
227
|
+
performPopulateToQueryObject,
|
|
228
|
+
performAdditionalOptionsToQueryObject,
|
|
229
|
+
triggers,
|
|
230
|
+
TypeCasters,
|
|
231
|
+
};
|
|
@@ -373,8 +373,12 @@ class UserManager {
|
|
|
373
373
|
let authM = DataProvider.getCollection("cms", "auth");
|
|
374
374
|
return authM.updateOne(query, update).exec().then();
|
|
375
375
|
}
|
|
376
|
+
|
|
377
|
+
static get instance() {
|
|
378
|
+
return instance;
|
|
379
|
+
}
|
|
376
380
|
}
|
|
377
381
|
|
|
378
|
-
|
|
382
|
+
const instance = new UserManager();
|
|
379
383
|
module.exports.name = "userManager";
|
|
380
384
|
module.exports.main = UserManager.instance;
|