hola-server 0.6.9 → 0.6.12

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/core/meta.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const { is_undefined } = require('./validate');
2
2
  const { get_type } = require('./type');
3
- const { is_valid_role } = require('../setting');
3
+ const { validate_meta_role } = require('./role');
4
4
 
5
5
  const meta_manager = {};
6
6
  /**
@@ -13,6 +13,7 @@ const meta_manager = {};
13
13
  * create is false, this attribute can be shown in property list but sys property can't be shown in property list
14
14
  * secure: secure properties will not be read by client, this is useful for password
15
15
  * group: this is used to control user sharing entities, this means the entity is shared by user group, this is only valid for user field
16
+ * view: this is used to control for edit form, for one entity, may have many forms to edit the entity, so use view to seperate them, this can be string or array
16
17
  *
17
18
  * routes: configure customer defined routes
18
19
  * link property: field link property link to entity field and the field should ref to an entity.
@@ -20,7 +21,7 @@ const meta_manager = {};
20
21
  *
21
22
  *
22
23
  */
23
- const field_attrs = ["name", "type", "required", "ref", "link", "delete", "create", "list", "search", "update", "clone", "sys", "secure", "group"];
24
+ const field_attrs = ["name", "type", "required", "ref", "link", "delete", "create", "list", "search", "update", "clone", "sys", "secure", "group", "view"];
24
25
  const meta_attrs = ["collection", "roles", "primary_keys", "fields", "creatable", "readable", "updatable", "deleteable", "cloneable", "after_read",
25
26
  "before_create", "after_create", "before_clone", "after_clone", "before_update", "after_update", "before_delete", "after_delete", "create", "clone", "update", "batch_update", "after_batch_update", "delete",
26
27
  "ref_label", "ref_filter", "route", "user_field"];
@@ -90,6 +91,19 @@ const validate_field = (meta, field) => {
90
91
  });
91
92
  }
92
93
 
94
+ const editable = field.create || field.update;
95
+
96
+ if (field.view) {
97
+ if (!editable) {
98
+ throw new Error("view can just defined for editable (create/update) field only. Field:" + JSON.stringify(field) + "] and meta:" + meta.collection);
99
+ }
100
+ } else {
101
+ //no view defined for create/update field, then set view to default value "0"
102
+ if (editable) {
103
+ field.view = "0";
104
+ }
105
+ }
106
+
93
107
  const keys = Object.keys(field);
94
108
  keys.forEach(key => {
95
109
  if (!field_attrs.includes(key)) {
@@ -191,6 +205,7 @@ const get_all_metas = () => {
191
205
  return Object.keys(meta_manager);
192
206
  }
193
207
 
208
+
194
209
  /**
195
210
  * Wrap the meta info from user side:
196
211
  * 1) validate the meta structure and keep it is valid
@@ -212,6 +227,18 @@ class EntityMeta {
212
227
  this.exportable = is_undefined(meta.exportable) ? false : meta.exportable;
213
228
  this.editable = this.creatable || this.updatable;
214
229
 
230
+ //b:batch mode, c:create, d:delete, e:export, i:import, o:clone, p:page, r: refresh, s:search, u:update
231
+ const modes = [];
232
+ this.creatable && (modes.push("c"));
233
+ //prefer the infinite scrolling mode, so doesn't include p mode
234
+ this.readable && (modes.push("rs"));
235
+ this.updatable && (modes.push("u"));
236
+ this.deleteable && (modes.push("db"));
237
+ this.cloneable && (modes.push("o"));
238
+ this.importable && (modes.push("i"));
239
+ this.exportable && (modes.push("e"));
240
+ this.mode = modes.join("");
241
+
215
242
  this.ref_label = this.meta.ref_label;
216
243
  this.ref_filter = this.meta.ref_filter;
217
244
  this.ref_fields = this.meta.fields.filter(field => field.ref);
@@ -287,11 +314,26 @@ class EntityMeta {
287
314
  if (!Array.isArray(this.roles)) {
288
315
  throw new Error("roles of meta [" + this.collection + "] should be array");
289
316
  }
317
+
290
318
  this.roles.forEach(role => {
291
319
  const role_config = role.split(":");
320
+ if (role_config.length != 2) {
321
+ throw new Error("wrong role config [" + role + "] in meta [" + this.collection + "]. You should use : to seperate the role name with mode.");
322
+ }
323
+
292
324
  const role_name = role_config[0];
293
- if (!is_valid_role(role_name)) {
294
- throw new Error("role [" + role_name + "] not defined in setting");
325
+ if (!validate_meta_role(role_name)) {
326
+ throw new Error("role [" + role_name + "] in meta [" + this.collection + "] not defined in setting's role config.");
327
+ }
328
+
329
+ const role_mode = role_config[1];
330
+ if (role_mode != "*") {
331
+ for (let i = 0; i < role_mode.length; i++) {
332
+ const mode = role_mode.charAt(i);
333
+ if (!this.mode.includes(mode)) {
334
+ throw new Error("role [" + role_name + "] in meta [" + this.collection + "] with mode [" + mode + "] doesn't comply with entity mode [" + this.mode + "]");
335
+ }
336
+ }
295
337
  }
296
338
  });
297
339
  }
package/core/role.js ADDED
@@ -0,0 +1,97 @@
1
+ const { get_settings } = require("../setting");
2
+
3
+ /**
4
+ * Validate the role defination in meta config
5
+ * @param {*} role_name
6
+ * @returns
7
+ */
8
+ const validate_meta_role = (role_name) => {
9
+ const settings = get_settings();
10
+ //there is role defined in meta but no roles config in settings
11
+ if (!settings.roles) {
12
+ return false;
13
+ }
14
+ return is_valid_role(role_name);
15
+ }
16
+
17
+ const is_valid_role = (role_name) => {
18
+ const settings = get_settings();
19
+ const roles = settings.roles.filter(role => role.name == role_name);
20
+ return roles.length == 1;
21
+ }
22
+
23
+ const is_root_role = (role_name) => {
24
+ const settings = get_settings();
25
+ //no role defined, then there is no role limition, so treat each user as root
26
+ if (!settings.roles) {
27
+ return true;
28
+ }
29
+
30
+ if (is_valid_role(role_name)) {
31
+ return settings.roles.filter(role => role.name == role_name)[0].root == true;
32
+ } else {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * get user's role from user session
39
+ * @param {request} req
40
+ * @returns
41
+ */
42
+ const get_session_user_role = (req) => {
43
+ const user = req && req.session ? req.session.user : null;
44
+ return user ? user.role : null;
45
+ }
46
+
47
+ /**
48
+ * Get the meta mode based on user's role
49
+ * @param {request} req
50
+ * @param {meta} meta
51
+ * @returns
52
+ */
53
+ const get_user_role_mode = (req, meta) => {
54
+ const settings = get_settings();
55
+ //no role defined in settings or no roles defined in meta, use meta mode
56
+ if (!settings.roles || !meta.roles) {
57
+ return meta.mode;
58
+ }
59
+
60
+ const user_role = get_session_user_role(req);
61
+ if (!user_role) {
62
+ return "";
63
+ }
64
+
65
+ if (is_valid_role(user_role)) {
66
+ const roles = meta.roles;
67
+ for (let i = 0; i < roles.length; i++) {
68
+ const role = roles[i];
69
+ const role_settings = role.split(":");
70
+ const role_name = role_settings[0];
71
+ const role_mode = role_settings[1];
72
+ if (user_role == role_name) {
73
+ // * stands to get the mode from meta definition
74
+ if (role_mode == "*") {
75
+ return meta.mode;
76
+ } else {
77
+ return role_mode;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return "";
83
+ }
84
+
85
+ /**
86
+ * Check whether the user has the mode right on the meta or not
87
+ * @param {http request} req
88
+ * @param {meta defination} meta
89
+ * @param {meta mode} mode
90
+ * @returns
91
+ */
92
+ const check_user_role = (req, meta, mode) => {
93
+ const role_mode = get_user_role_mode(req, meta);
94
+ return role_mode.includes(mode);
95
+ }
96
+
97
+ module.exports = { is_root_role, validate_meta_role, check_user_role, get_user_role_mode };
package/db/entity.js CHANGED
@@ -235,10 +235,11 @@ class Entity {
235
235
  /**
236
236
  * Validate the param object and invoke the logic to save it to db
237
237
  * @param {param obj from user input} param_obj
238
+ * @param {which view to create the entity} view
238
239
  * @returns object with code and err
239
240
  */
240
- async create_entity(param_obj) {
241
- const fields = this.meta.create_fields
241
+ async create_entity(param_obj, view) {
242
+ const fields = this.meta.create_fields.filter(field => field.view == view);
242
243
  const { obj, error_field_names } = convert_type(param_obj, fields);
243
244
  if (error_field_names.length > 0) {
244
245
  if (is_log_error()) {
@@ -397,10 +398,13 @@ class Entity {
397
398
  * Validate the param object and invoke the logic to update entity
398
399
  * @param {object id of the entity} _id object id of the entity, if it is null, then use primary key
399
400
  * @param {param object from user input} param_obj
401
+ * @param {which view to update the entity} view
400
402
  *
401
403
  */
402
- async update_entity(_id, param_obj) {
403
- const { obj, error_field_names } = convert_update_type(param_obj, this.meta.update_fields);
404
+ async update_entity(_id, param_obj, view) {
405
+ const fields = this.meta.update_fields.filter(field => field.view == view);
406
+
407
+ const { obj, error_field_names } = convert_update_type(param_obj, fields);
404
408
  if (error_field_names.length > 0) {
405
409
  if (is_log_error()) {
406
410
  log_error(LOG_ENTITY, "update_entity error fields:" + JSON.stringify(error_field_names));
package/http/session.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const express_session = require('express-session');
2
2
  const MongoStore = require('connect-mongo');
3
- const { get_settings, is_valid_role, is_root_role } = require('../setting');
3
+ const { get_settings } = require('../setting');
4
4
 
5
5
  const init_session = (app) => {
6
6
  const server = get_settings().server;
@@ -24,54 +24,9 @@ const get_session_userid = (req) => {
24
24
  return user ? user.id : null;
25
25
  }
26
26
 
27
- const get_session_user_role = (req) => {
28
- const user = req && req.session ? req.session.user : null;
29
- return user ? user.role : null;
30
- }
31
-
32
- //b:batch mode, c:create, d:delete, e:export, i:import, o:clone, p:page, r: refresh, s:search, u:update
33
- const mode_all = "bcdeiorsu";
34
-
35
- const get_user_role_mode = (req, roles) => {
36
- const server = get_settings().server;
37
- if (server.check_user == false) {
38
- return mode_all;
39
- }
40
-
41
- const user_role = get_session_user_role(req);
42
- if (!user_role) {
43
- return "";
44
- }
45
-
46
- if (is_root_role(user_role)) {
47
- return mode_all;
48
- } else if (is_valid_role(user_role)) {
49
- for (let i = 0; i < roles.length; i++) {
50
- const role = roles[i];
51
- const role_settings = role.split(":");
52
- const role_name = role_settings[0];
53
- const role_mode = role_settings[1];
54
- if (user_role == role_name) {
55
- if (role_mode == "*") {
56
- return mode_all;
57
- } else {
58
- return role_mode;
59
- }
60
- }
61
- }
62
- }
63
-
64
- return "";
65
- }
66
-
67
- const check_user_role = (req, roles, mode) => {
68
- const role_mode = get_user_role_mode(req, roles);
69
- return role_mode.includes(mode);
70
- }
71
-
72
27
  const get_session_user_groups = (req) => {
73
28
  const group = req && req.session ? req.session.group : null;
74
29
  return group && Array.isArray(group) ? group : null;
75
30
  }
76
31
 
77
- module.exports = { init_session, get_session_userid, get_session_user_role, get_session_user_groups, check_user_role, get_user_role_mode };
32
+ module.exports = { init_session, get_session_userid, get_session_user_groups };
package/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  const { get_db } = require('./db/db');
2
2
  const { Entity } = require('./db/entity');
3
- const { init_settings, is_root_role } = require('./setting');
3
+ const { init_settings } = require('./setting');
4
4
  const { init_router } = require('./http/router');
5
5
  const { init_express_server } = require('./http/express');
6
6
  const { register_type, get_type } = require('./core/type');
7
7
  const { EntityMeta, get_entity_meta } = require('./core/meta');
8
+ const { is_root_role } = require('./core/role');
8
9
 
9
10
 
10
11
  const array = require('./core/array');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hola-server",
3
- "version": "0.6.9",
3
+ "version": "0.6.12",
4
4
  "description": "a meta programming framework used to build nodejs restful api",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/router/clone.js CHANGED
@@ -1,9 +1,10 @@
1
1
  const { set_file_fields, save_file_fields_to_db } = require('../db/gridfs');
2
2
  const { SUCCESS, NO_PARAMS, NO_RIGHTS } = require('../http/code');
3
- const { get_session_userid, check_user_role } = require('../http/session');
3
+ const { get_session_userid } = require('../http/session');
4
4
  const { wrap_http } = require('../http/error');
5
5
  const { post_params, required_post_params } = require('../http/params');
6
6
  const { has_value } = require('../core/validate');
7
+ const { check_user_role } = require('../core/role');
7
8
  const { Entity } = require('../db/entity');
8
9
 
9
10
  const multer = require('multer');
@@ -19,12 +20,10 @@ const init_clone_router = function (router, meta) {
19
20
  const cp_upload = meta.upload_fields.length > 0 ? upload_file.fields(meta.upload_fields) : upload_file.none();
20
21
 
21
22
  router.post('/clone', cp_upload, wrap_http(async function (req, res) {
22
- if (meta.roles) {
23
- const has_right = check_user_role(req, meta.roles, "o");
24
- if (!has_right) {
25
- res.json({ code: NO_RIGHTS, err: "no rights error" });
26
- return;
27
- }
23
+ const has_right = check_user_role(req, meta, "o");
24
+ if (!has_right) {
25
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
26
+ return;
28
27
  }
29
28
 
30
29
  let params = required_post_params(req, ["_id"]);
package/router/create.js CHANGED
@@ -1,9 +1,10 @@
1
1
  const { set_file_fields, save_file_fields_to_db } = require('../db/gridfs');
2
2
  const { SUCCESS, NO_RIGHTS } = require('../http/code');
3
- const { get_session_userid, check_user_role } = require('../http/session');
3
+ const { get_session_userid } = require('../http/session');
4
4
  const { wrap_http } = require('../http/error');
5
5
  const { post_params } = require('../http/params');
6
6
  const { has_value } = require('../core/validate');
7
+ const { check_user_role } = require('../core/role');
7
8
  const { Entity } = require('../db/entity');
8
9
 
9
10
  const multer = require('multer');
@@ -19,12 +20,16 @@ const init_create_router = function (router, meta) {
19
20
  const cp_upload = meta.upload_fields.length > 0 ? upload_file.fields(meta.upload_fields) : upload_file.none();
20
21
 
21
22
  router.post('/create', cp_upload, wrap_http(async function (req, res) {
22
- if (meta.roles) {
23
- const has_right = check_user_role(req, meta.roles, "c");
24
- if (!has_right) {
25
- res.json({ code: NO_RIGHTS, err: "no rights error" });
26
- return;
27
- }
23
+ const has_right = check_user_role(req, meta, "c");
24
+ if (!has_right) {
25
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
26
+ return;
27
+ }
28
+
29
+ //which view to create the entity
30
+ let { _view } = post_params(req, ["_view"]);
31
+ if (!_view) {
32
+ _view = "0";
28
33
  }
29
34
 
30
35
  const param_obj = post_params(req, meta.field_names);
@@ -38,7 +43,7 @@ const init_create_router = function (router, meta) {
38
43
  param_obj[meta.user_field] = user_id;
39
44
  }
40
45
 
41
- const { code, err } = await entity.create_entity(param_obj);
46
+ const { code, err } = await entity.create_entity(param_obj, _view);
42
47
  if (!has_value(code)) {
43
48
  throw new Error("the method should return code");
44
49
  }
package/router/delete.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const { required_post_params } = require('../http/params');
2
2
  const { has_value } = require('../core/validate');
3
3
  const { NO_PARAMS, NO_RIGHTS } = require('../http/code');
4
- const { check_user_role } = require('../http/session');
4
+ const { check_user_role } = require('../core/role');
5
5
  const { wrap_http } = require('../http/error');
6
6
  const { Entity } = require('../db/entity');
7
7
 
@@ -14,12 +14,10 @@ const init_delete_router = function (router, meta) {
14
14
  const entity = new Entity(meta);
15
15
 
16
16
  router.post('/delete', wrap_http(async function (req, res) {
17
- if (meta.roles) {
18
- const has_right = check_user_role(req, meta.roles, "d");
19
- if (!has_right) {
20
- res.json({ code: NO_RIGHTS, err: "no rights error" });
21
- return;
22
- }
17
+ const has_right = check_user_role(req, meta, "d");
18
+ if (!has_right) {
19
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
20
+ return;
23
21
  }
24
22
 
25
23
  const params = required_post_params(req, ["ids"]);
package/router/read.js CHANGED
@@ -1,7 +1,8 @@
1
1
  const { required_post_params, get_params } = require('../http/params');
2
2
  const { has_value } = require('../core/validate');
3
3
  const { NO_PARAMS, SUCCESS, NO_RIGHTS } = require('../http/code');
4
- const { get_session_userid, get_session_user_groups, check_user_role, get_user_role_mode } = require('../http/session');
4
+ const { check_user_role, get_user_role_mode } = require('../core/role');
5
+ const { get_session_userid, get_session_user_groups } = require('../http/session');
5
6
  const { wrap_http } = require('../http/error');
6
7
  const { Entity } = require('../db/entity');
7
8
 
@@ -14,12 +15,10 @@ const init_read_router = function (router, meta) {
14
15
  const entity = new Entity(meta);
15
16
 
16
17
  router.get('/meta', wrap_http(async function (req, res) {
17
- if (meta.roles) {
18
- const has_right = check_user_role(req, meta.roles, "r");
19
- if (!has_right) {
20
- res.json({ code: NO_RIGHTS, err: "no rights error" });
21
- return;
22
- }
18
+ const has_right = check_user_role(req, meta, "r");
19
+ if (!has_right) {
20
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
21
+ return;
23
22
  }
24
23
 
25
24
  const entity_meta = {
@@ -38,24 +37,20 @@ const init_read_router = function (router, meta) {
38
37
  }));
39
38
 
40
39
  router.get('/mode', wrap_http(async function (req, res) {
41
- if (meta.roles) {
42
- const has_right = check_user_role(req, meta.roles, "r");
43
- if (!has_right) {
44
- res.json({ code: NO_RIGHTS, err: "no rights error" });
45
- return;
46
- }
40
+ const has_right = check_user_role(req, meta, "r");
41
+ if (!has_right) {
42
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
43
+ return;
47
44
  }
48
45
 
49
- res.json({ code: SUCCESS, data: get_user_role_mode(req, meta.roles) });
46
+ res.json({ code: SUCCESS, data: get_user_role_mode(req, meta) });
50
47
  }));
51
48
 
52
49
  router.get('/ref', wrap_http(async function (req, res) {
53
- if (meta.roles) {
54
- const has_right = check_user_role(req, meta.roles, "r");
55
- if (!has_right) {
56
- res.json({ code: NO_RIGHTS, err: "no rights error" });
57
- return;
58
- }
50
+ const has_right = check_user_role(req, meta, "r");
51
+ if (!has_right) {
52
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
53
+ return;
59
54
  }
60
55
 
61
56
  const { ref_by_entity } = get_params(req, ["ref_by_entity"]);
@@ -65,12 +60,10 @@ const init_read_router = function (router, meta) {
65
60
  }));
66
61
 
67
62
  router.post('/list', wrap_http(async function (req, res) {
68
- if (meta.roles) {
69
- const has_right = check_user_role(req, meta.roles, "r");
70
- if (!has_right) {
71
- res.json({ code: NO_RIGHTS, err: "no rights error" });
72
- return;
73
- }
63
+ const has_right = check_user_role(req, meta, "r");
64
+ if (!has_right) {
65
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
66
+ return;
74
67
  }
75
68
 
76
69
  const query_params = required_post_params(req, ["_query"]);
@@ -107,12 +100,10 @@ const init_read_router = function (router, meta) {
107
100
  }));
108
101
 
109
102
  router.post('/read_entity', wrap_http(async function (req, res) {
110
- if (meta.roles) {
111
- const has_right = check_user_role(req, meta.roles, "r");
112
- if (!has_right) {
113
- res.json({ code: NO_RIGHTS, err: "no rights error" });
114
- return;
115
- }
103
+ const has_right = check_user_role(req, meta, "r");
104
+ if (!has_right) {
105
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
106
+ return;
116
107
  }
117
108
 
118
109
  let params = required_post_params(req, ["_id", "attr_names"]);
@@ -130,12 +121,10 @@ const init_read_router = function (router, meta) {
130
121
  }));
131
122
 
132
123
  router.post('/read_property', wrap_http(async function (req, res) {
133
- if (meta.roles) {
134
- const has_right = check_user_role(req, meta.roles, "r");
135
- if (!has_right) {
136
- res.json({ code: NO_RIGHTS, err: "no rights error" });
137
- return;
138
- }
124
+ const has_right = check_user_role(req, meta, "r");
125
+ if (!has_right) {
126
+ res.json({ code: NO_RIGHTS, err: "no rights error" });
127
+ return;
139
128
  }
140
129
 
141
130
  let params = required_post_params(req, ["_id", "attr_names"]);
package/router/update.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { set_file_fields, save_file_fields_to_db } = require('../db/gridfs');
2
- const { required_post_params, post_update_params } = require('../http/params');
2
+ const { required_post_params, post_update_params, post_params } = require('../http/params');
3
3
  const { SUCCESS, NO_PARAMS, NO_RIGHTS } = require('../http/code');
4
4
  const { check_user_role } = require('../http/session');
5
5
  const { has_value } = require('../core/validate');
@@ -36,10 +36,16 @@ const init_update_router = function (router, meta) {
36
36
  }
37
37
  }
38
38
 
39
+ //which view to update the entity
40
+ let { _view } = post_params(req, ["_view"]);
41
+ if (!_view) {
42
+ _view = "0";
43
+ }
44
+
39
45
  const param_obj = post_update_params(req, meta.field_names);
40
46
  set_file_fields(meta, req, param_obj);
41
47
 
42
- const { code, err } = await entity.update_entity(params["_id"], param_obj);
48
+ const { code, err } = await entity.update_entity(params["_id"], param_obj, _view);
43
49
  if (!has_value(code)) {
44
50
  throw new Error("the method should return code");
45
51
  }
package/setting.js CHANGED
@@ -43,26 +43,4 @@ const get_settings = () => {
43
43
  return settings;
44
44
  }
45
45
 
46
- const is_valid_role = (role_name) => {
47
- //no role defined, then no role check
48
- if (!settings.roles) {
49
- return true;
50
- }
51
- const roles = settings.roles.filter(role => role.name == role_name);
52
- return roles.length == 1;
53
- }
54
-
55
- const is_root_role = (role_name) => {
56
- //no role defined, then every one is root
57
- if (!settings.roles) {
58
- return true;
59
- }
60
-
61
- if (!is_valid_role(role_name)) {
62
- return false;
63
- }
64
-
65
- return settings.roles.filter(role => role.name == role_name)[0].root == true;
66
- }
67
-
68
- module.exports = { init_settings, get_settings, is_root_role, is_valid_role };
46
+ module.exports = { init_settings, get_settings };