jxp 2.11.1 → 2.12.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 +5 -1
- package/bin/server.js +16 -10
- package/libs/docs.js +9 -8
- package/libs/groups.js +15 -25
- package/libs/jxp.js +49 -56
- package/libs/login.js +20 -25
- package/libs/schema_description.js +1 -1
- package/libs/security.js +58 -54
- package/libs/setup.js +6 -5
- package/package.json +4 -3
- package/test/init.js +1 -1
- package/test/setup.test.js +3 -2
- package/test/test.js +27 -27
package/README.md
CHANGED
|
@@ -35,4 +35,8 @@ JXP is an opinionated RESTful API framework that lets you make an API just by de
|
|
|
35
35
|
* [Roles and Permissions](https://jxp.readthedocs.io/en/v2.0.0/permissions)
|
|
36
36
|
* [Hooks](https://jxp.readthedocs.io/en/v2.0.0/hooks)
|
|
37
37
|
* [Special Features](https://jxp.readthedocs.io/en/v2.0.0/special)
|
|
38
|
-
* [Changelog](https://jxp.readthedocs.io/en/v2.0.0/changelog)
|
|
38
|
+
* [Changelog](https://jxp.readthedocs.io/en/v2.0.0/changelog)
|
|
39
|
+
|
|
40
|
+
## Installing from source
|
|
41
|
+
|
|
42
|
+
`npm i --legacy-peer-deps`
|
package/bin/server.js
CHANGED
|
@@ -196,17 +196,23 @@ const mongoose = require("mongoose");
|
|
|
196
196
|
const JXP = require("../libs/jxp");
|
|
197
197
|
const config = require("config");
|
|
198
198
|
require("dotenv").config();
|
|
199
|
+
const pkg = require("../package.json");
|
|
199
200
|
|
|
200
201
|
config.callbacks = {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
// Examples:
|
|
203
|
+
// post: function(modelname, item, user) {
|
|
204
|
+
// console.log("Post callback");
|
|
205
|
+
// },
|
|
206
|
+
// put: function(modelname, item, user) {
|
|
207
|
+
// console.log("Put callback");
|
|
208
|
+
// },
|
|
209
|
+
// delete: function(modelname, item, user, opts) {
|
|
210
|
+
// console.log("Delete callback");
|
|
211
|
+
// }
|
|
212
|
+
|
|
213
|
+
post: function() {},
|
|
214
|
+
put: function() {},
|
|
215
|
+
delete: function() {}
|
|
210
216
|
};
|
|
211
217
|
|
|
212
218
|
config.pre_hooks = {
|
|
@@ -259,7 +265,7 @@ var server = new JXP(config);
|
|
|
259
265
|
let port = process.env.NODE_DOCKER_PORT || process.env.PORT || config.port || 4001;
|
|
260
266
|
if (process.env.NODE_ENV === "test") port = 4005;
|
|
261
267
|
server.listen(port, function() {
|
|
262
|
-
console.log('%s listening at %s',
|
|
268
|
+
console.log('%s listening at %s', `${pkg.name} v${pkg.version}`, server.url);
|
|
263
269
|
});
|
|
264
270
|
|
|
265
271
|
module.exports = server; // For testing
|
package/libs/docs.js
CHANGED
|
@@ -6,6 +6,7 @@ const md = require('jstransformer')(require('jstransformer-markdown-it'));
|
|
|
6
6
|
const util = require('util');
|
|
7
7
|
const readFile = util.promisify(fs.readFile);
|
|
8
8
|
const schema_description = require("./schema_description");
|
|
9
|
+
const errors = require("restify-errors");
|
|
9
10
|
|
|
10
11
|
class Docs {
|
|
11
12
|
constructor(opts) {
|
|
@@ -32,7 +33,7 @@ class Docs {
|
|
|
32
33
|
res.end();
|
|
33
34
|
} catch(err) {
|
|
34
35
|
console.error(err);
|
|
35
|
-
return
|
|
36
|
+
return errors.InternalServerError({ message: err.toString() });
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -42,20 +43,20 @@ class Docs {
|
|
|
42
43
|
res.send(models);
|
|
43
44
|
} catch (err) {
|
|
44
45
|
console.error(err);
|
|
45
|
-
return
|
|
46
|
+
return errors.InternalServerError({ message: err.toString() });
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
metaModel(req, res, next) {
|
|
50
51
|
try {
|
|
51
52
|
if (!req.Model) {
|
|
52
|
-
return
|
|
53
|
+
return errors.NotFoundError({ status: "error", error: "Model not found" })
|
|
53
54
|
}
|
|
54
55
|
res.send(req.Model.schema.paths);
|
|
55
56
|
next();
|
|
56
57
|
} catch (err) {
|
|
57
58
|
console.error(err);
|
|
58
|
-
return
|
|
59
|
+
return errors.InternalServerError({ message: err.toString() });
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -64,7 +65,7 @@ class Docs {
|
|
|
64
65
|
res.send(this.models);
|
|
65
66
|
next();
|
|
66
67
|
} catch(err) {
|
|
67
|
-
|
|
68
|
+
errors.InternalServerError({ message: err.toString() });
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -73,7 +74,7 @@ class Docs {
|
|
|
73
74
|
this.renderTemplate(res, "index", {});
|
|
74
75
|
next();
|
|
75
76
|
} catch(err) {
|
|
76
|
-
|
|
77
|
+
errors.InternalServerError({ message: err.toString() });
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -83,7 +84,7 @@ class Docs {
|
|
|
83
84
|
const md_contents = md.render(body.toString()).body;
|
|
84
85
|
this.renderTemplate(res, "md", { md_contents });
|
|
85
86
|
} catch(err) {
|
|
86
|
-
|
|
87
|
+
errors.InternalServerError({ message: err.toString() });
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -97,7 +98,7 @@ class Docs {
|
|
|
97
98
|
this.renderTemplate(res, "model", { model, fields, perms });
|
|
98
99
|
next();
|
|
99
100
|
} catch(err) {
|
|
100
|
-
|
|
101
|
+
errors.InternalServerError({ message: err.toString() });
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
}
|
package/libs/groups.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
const errors = require("restify-errors");
|
|
2
|
+
|
|
3
|
+
let Groups = null;
|
|
2
4
|
|
|
3
5
|
const init = config => {
|
|
4
6
|
const path = require("path");
|
|
@@ -10,13 +12,11 @@ const actionPut = async (req, res) => {
|
|
|
10
12
|
const group = req.body.group;
|
|
11
13
|
if (!group) {
|
|
12
14
|
console.error("Group required");
|
|
13
|
-
|
|
14
|
-
return;
|
|
15
|
+
return errors.BadRequestError("Group required");
|
|
15
16
|
}
|
|
16
17
|
if (!user_id) {
|
|
17
18
|
console.error("user_id required");
|
|
18
|
-
|
|
19
|
-
return;
|
|
19
|
+
return errors.BadRequestError("user_id required", { message: "user_id is required" });
|
|
20
20
|
}
|
|
21
21
|
try {
|
|
22
22
|
let userGroup = await Groups.findOne({ user_id: user_id });
|
|
@@ -39,8 +39,7 @@ const actionPut = async (req, res) => {
|
|
|
39
39
|
res.send(await userGroup.save());
|
|
40
40
|
} catch(err) {
|
|
41
41
|
console.error(err);
|
|
42
|
-
|
|
43
|
-
return;
|
|
42
|
+
return errors.InternalServerError({ message: err.toString()});
|
|
44
43
|
}
|
|
45
44
|
};
|
|
46
45
|
|
|
@@ -49,13 +48,11 @@ const actionPost = async (req, res) => {
|
|
|
49
48
|
const group = req.body.group;
|
|
50
49
|
if (!group) {
|
|
51
50
|
console.error("Group required");
|
|
52
|
-
|
|
53
|
-
return;
|
|
51
|
+
return errors.BadRequestError("Group required");
|
|
54
52
|
}
|
|
55
53
|
if (!user_id) {
|
|
56
54
|
console.error("user_id required");
|
|
57
|
-
|
|
58
|
-
return;
|
|
55
|
+
return errors.BadRequestError("user_id required", { message: "user_id is required" });
|
|
59
56
|
}
|
|
60
57
|
try {
|
|
61
58
|
let userGroup = await Groups.findOne({ user_id: user_id });
|
|
@@ -78,8 +75,7 @@ const actionPost = async (req, res) => {
|
|
|
78
75
|
res.send(await userGroup.save());
|
|
79
76
|
} catch (err) {
|
|
80
77
|
console.error(err);
|
|
81
|
-
|
|
82
|
-
return;
|
|
78
|
+
return errors.InternalServerError({ message: err.toString()});
|
|
83
79
|
}
|
|
84
80
|
};
|
|
85
81
|
|
|
@@ -87,8 +83,7 @@ const actionGet = async (req, res) => {
|
|
|
87
83
|
var user_id = req.params.user_id;
|
|
88
84
|
if (!user_id) {
|
|
89
85
|
console.error("user_id required");
|
|
90
|
-
|
|
91
|
-
return;
|
|
86
|
+
return errors.BadRequestError("user_id required", { message: "user_id is required" });
|
|
92
87
|
}
|
|
93
88
|
try {
|
|
94
89
|
let userGroup = await Groups.findOne({ user_id });
|
|
@@ -99,8 +94,7 @@ const actionGet = async (req, res) => {
|
|
|
99
94
|
res.send(userGroup);
|
|
100
95
|
} catch (err) {
|
|
101
96
|
console.error(err);
|
|
102
|
-
|
|
103
|
-
return;
|
|
97
|
+
return errors.InternalServerError({ message: err.toString()});
|
|
104
98
|
}
|
|
105
99
|
};
|
|
106
100
|
|
|
@@ -108,19 +102,16 @@ const actionDelete = async (req, res) => {
|
|
|
108
102
|
const user_id = req.params.user_id;
|
|
109
103
|
const group = req.query.group;
|
|
110
104
|
if (!group) {
|
|
111
|
-
|
|
112
|
-
return;
|
|
105
|
+
return errors.BadRequestError("Group required");
|
|
113
106
|
}
|
|
114
107
|
if (!user_id) {
|
|
115
108
|
console.error("user_id required");
|
|
116
|
-
|
|
117
|
-
return;
|
|
109
|
+
return errors.BadRequestError("user_id required", { message: "user_id is required" });
|
|
118
110
|
}
|
|
119
111
|
try {
|
|
120
112
|
let userGroup = await Groups.findOne({ user_id });
|
|
121
113
|
if (!userGroup) {
|
|
122
|
-
|
|
123
|
-
return;
|
|
114
|
+
return errors.BadRequestError("User not found");
|
|
124
115
|
}
|
|
125
116
|
let i = userGroup.groups.indexOf(group);
|
|
126
117
|
if (i > -1) {
|
|
@@ -129,8 +120,7 @@ const actionDelete = async (req, res) => {
|
|
|
129
120
|
res.send(await userGroup.save());
|
|
130
121
|
} catch (err) {
|
|
131
122
|
console.error(err);
|
|
132
|
-
|
|
133
|
-
return;
|
|
123
|
+
return errors.InternalServerError({ message: err.toString() });
|
|
134
124
|
}
|
|
135
125
|
};
|
|
136
126
|
|
package/libs/jxp.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const errors = require("restify-errors");
|
|
1
2
|
const restify = require("restify");
|
|
2
3
|
const path = require("path");
|
|
3
4
|
const security = require("./security");
|
|
@@ -29,7 +30,7 @@ const middlewareModel = (req, res, next) => {
|
|
|
29
30
|
return next();
|
|
30
31
|
} catch (err) {
|
|
31
32
|
console.error(new Date, err);
|
|
32
|
-
|
|
33
|
+
throw new errors.NotFoundError("Not Found", { message: `Model ${modelname} not found` });
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
36
|
|
|
@@ -50,8 +51,13 @@ const middlewareCheckAdmin = (req, res, next) => {
|
|
|
50
51
|
|
|
51
52
|
// Outputs whatever is in res.result as JSON
|
|
52
53
|
const outputJSON = (req, res, next) => {
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
try {
|
|
55
|
+
res.send(res.result);
|
|
56
|
+
next();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error(new Date(), err);
|
|
59
|
+
throw new errors.InternalServerError("Error generating output", { message: err.toString() });
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
// Outputs whatever is in res.result as CSV
|
|
@@ -59,7 +65,7 @@ const outputCSV = (req, res, next) => {
|
|
|
59
65
|
const json2csv = require('json2csv').parse;
|
|
60
66
|
const opts = { "flatten": true };
|
|
61
67
|
if (!res.result.data) {
|
|
62
|
-
|
|
68
|
+
throw new errors.InternalServerError("Error generating output", { message: "Not CSVable data" });
|
|
63
69
|
}
|
|
64
70
|
try {
|
|
65
71
|
const data = res.result.data.map(row => row._doc);
|
|
@@ -75,7 +81,7 @@ const outputCSV = (req, res, next) => {
|
|
|
75
81
|
next();
|
|
76
82
|
} catch (err) {
|
|
77
83
|
console.error(err);
|
|
78
|
-
|
|
84
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -83,13 +89,6 @@ const outputCSV = (req, res, next) => {
|
|
|
83
89
|
const actionGet = async (req, res) => {
|
|
84
90
|
const opname = `get ${req.modelname} ${ops++}`;
|
|
85
91
|
console.time(opname);
|
|
86
|
-
try {
|
|
87
|
-
if (res.user) {
|
|
88
|
-
req.Model.__user = res.user;
|
|
89
|
-
}
|
|
90
|
-
} catch(err) {
|
|
91
|
-
console.error(err);
|
|
92
|
-
}
|
|
93
92
|
const parseSearch = function(search) {
|
|
94
93
|
let result = {};
|
|
95
94
|
for (let i in search) {
|
|
@@ -102,8 +101,7 @@ const actionGet = async (req, res) => {
|
|
|
102
101
|
filters = parseFilter(req.query.filter);
|
|
103
102
|
} catch (err) {
|
|
104
103
|
console.trace(new Date(), err);
|
|
105
|
-
|
|
106
|
-
return;
|
|
104
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
107
105
|
}
|
|
108
106
|
let search = parseSearch(req.query.search);
|
|
109
107
|
for (let i in search) {
|
|
@@ -124,6 +122,9 @@ const actionGet = async (req, res) => {
|
|
|
124
122
|
countquery = Object.assign({ $text: { $search: req.query.search }}, countquery);
|
|
125
123
|
qcount = req.Model.find({ $text: { $search: req.query.search }});
|
|
126
124
|
}
|
|
125
|
+
if (res.user) {
|
|
126
|
+
q.options = ({ user: res.user });
|
|
127
|
+
}
|
|
127
128
|
try {
|
|
128
129
|
let count = await req.Model.estimatedDocumentCount();
|
|
129
130
|
if (count < 100000 && Object.keys(countquery).length !== 0) {
|
|
@@ -189,7 +190,8 @@ const actionGet = async (req, res) => {
|
|
|
189
190
|
} catch(err) {
|
|
190
191
|
console.error(new Date(), err);
|
|
191
192
|
if (debug) console.timeEnd(opname);
|
|
192
|
-
|
|
193
|
+
if (err.code) throw err;
|
|
194
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
193
195
|
}
|
|
194
196
|
};
|
|
195
197
|
|
|
@@ -197,20 +199,14 @@ const actionGetOne = async (req, res) => {
|
|
|
197
199
|
const opname = `getOne ${req.modelname}/${req.params.item_id} ${ops++}`;
|
|
198
200
|
console.time(opname);
|
|
199
201
|
try {
|
|
200
|
-
|
|
201
|
-
req.Model.__user = res.user;
|
|
202
|
-
}
|
|
203
|
-
const data = await getOne(req.Model, req.params.item_id, req.query);
|
|
202
|
+
const data = await getOne(req.Model, req.params.item_id, req.query, { user: res.user });
|
|
204
203
|
res.send({ data });
|
|
205
204
|
if (debug) console.timeEnd(opname);
|
|
206
205
|
} catch(err) {
|
|
207
206
|
console.error(new Date(), err);
|
|
208
207
|
if (debug) console.timeEnd(opname);
|
|
209
|
-
if (err.
|
|
210
|
-
|
|
211
|
-
} else {
|
|
212
|
-
res.send(err.code || 500, { status: "error", message: err.toString() });
|
|
213
|
-
}
|
|
208
|
+
if (err.code) throw err;
|
|
209
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
214
210
|
}
|
|
215
211
|
};
|
|
216
212
|
|
|
@@ -240,7 +236,7 @@ const actionPost = async (req, res) => {
|
|
|
240
236
|
} catch (err) {
|
|
241
237
|
console.error(new Date(), err);
|
|
242
238
|
if (debug) console.timeEnd(opname);
|
|
243
|
-
|
|
239
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
244
240
|
}
|
|
245
241
|
};
|
|
246
242
|
|
|
@@ -251,8 +247,7 @@ const actionPut = async (req, res) => {
|
|
|
251
247
|
let item = await req.Model.findById(req.params.item_id);
|
|
252
248
|
if (!item) {
|
|
253
249
|
console.error(new Date(), "Document not found");
|
|
254
|
-
|
|
255
|
-
return;
|
|
250
|
+
throw new errors.NotFoundError("Not Found", { message: "Document not found" });
|
|
256
251
|
}
|
|
257
252
|
_populateItem(item, datamunging.deserialize(req.body));
|
|
258
253
|
_versionItem(item);
|
|
@@ -276,8 +271,7 @@ const actionPut = async (req, res) => {
|
|
|
276
271
|
} catch (err) {
|
|
277
272
|
console.error(new Date(), err);
|
|
278
273
|
if (debug) console.timeEnd(opname);
|
|
279
|
-
|
|
280
|
-
return;
|
|
274
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
281
275
|
}
|
|
282
276
|
};
|
|
283
277
|
|
|
@@ -302,8 +296,7 @@ const actionUpdate = async (req, res) => {
|
|
|
302
296
|
} catch (err) {
|
|
303
297
|
console.error(new Date(), err);
|
|
304
298
|
if (debug) console.timeEnd(opname);
|
|
305
|
-
|
|
306
|
-
return;
|
|
299
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
307
300
|
}
|
|
308
301
|
};
|
|
309
302
|
|
|
@@ -317,8 +310,7 @@ const actionDelete = async (req, res) => {
|
|
|
317
310
|
let item = await req.Model.findById(req.params.item_id);
|
|
318
311
|
if (!item) {
|
|
319
312
|
console.error(new Date(), "Couldn't find item for delete");
|
|
320
|
-
|
|
321
|
-
return;
|
|
313
|
+
throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for delete" });
|
|
322
314
|
}
|
|
323
315
|
// Get linked models
|
|
324
316
|
const linked_models = [];
|
|
@@ -347,8 +339,7 @@ const actionDelete = async (req, res) => {
|
|
|
347
339
|
await models[linked_model.modelname].updateMany(q, { _deleted: true });
|
|
348
340
|
}
|
|
349
341
|
} else {
|
|
350
|
-
|
|
351
|
-
return;
|
|
342
|
+
throw new errors.ConflictError("Conflict Error", { message: `Parent link item exists in ${linked_model.modelname}/${linked_model.field}` });
|
|
352
343
|
}
|
|
353
344
|
}
|
|
354
345
|
}
|
|
@@ -381,8 +372,8 @@ const actionDelete = async (req, res) => {
|
|
|
381
372
|
} catch(err) {
|
|
382
373
|
console.error(new Date(), err);
|
|
383
374
|
if (debug) console.timeEnd(opname);
|
|
384
|
-
|
|
385
|
-
|
|
375
|
+
if (err.code) throw err;
|
|
376
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
386
377
|
}
|
|
387
378
|
};
|
|
388
379
|
|
|
@@ -401,8 +392,7 @@ const actionCount = async (req, res) => {
|
|
|
401
392
|
filters = parseFilter(req.query.filter);
|
|
402
393
|
} catch (err) {
|
|
403
394
|
console.trace(new Date(), err);
|
|
404
|
-
|
|
405
|
-
return;
|
|
395
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
406
396
|
}
|
|
407
397
|
let search = parseSearch(req.query.search);
|
|
408
398
|
for (let i in search) {
|
|
@@ -418,7 +408,8 @@ const actionCount = async (req, res) => {
|
|
|
418
408
|
} catch(err) {
|
|
419
409
|
console.error(new Date(), err);
|
|
420
410
|
if (debug) console.timeEnd(opname);
|
|
421
|
-
|
|
411
|
+
if (err.code) throw err;
|
|
412
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
422
413
|
}
|
|
423
414
|
};
|
|
424
415
|
|
|
@@ -431,7 +422,8 @@ const actionCall = async (req, res) => {
|
|
|
431
422
|
res.json(result);
|
|
432
423
|
} catch(err) {
|
|
433
424
|
console.error(new Date(), err);
|
|
434
|
-
|
|
425
|
+
if (err.code) throw err;
|
|
426
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
435
427
|
}
|
|
436
428
|
};
|
|
437
429
|
|
|
@@ -439,16 +431,15 @@ const actionCallItem = async (req, res) => {
|
|
|
439
431
|
try {
|
|
440
432
|
const item = req.Model.findById(req.params.item_id);
|
|
441
433
|
if (!item) {
|
|
442
|
-
|
|
443
|
-
return;
|
|
434
|
+
throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for call" });
|
|
444
435
|
}
|
|
445
436
|
req.params.__user = res.user || null;
|
|
446
437
|
const result = await req.Model[req.params.method_name](item);
|
|
447
438
|
res.json(result);
|
|
448
|
-
|
|
449
439
|
} catch(err) {
|
|
450
440
|
console.trace(err);
|
|
451
|
-
|
|
441
|
+
if (err.code) throw err;
|
|
442
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
452
443
|
}
|
|
453
444
|
};
|
|
454
445
|
|
|
@@ -456,7 +447,7 @@ const actionCallItem = async (req, res) => {
|
|
|
456
447
|
const actionQuery = async (req, res) => {
|
|
457
448
|
if (!req.body || !req.body.query || typeof req.body.query !== "object") {
|
|
458
449
|
console.error("query missing or not of type object")
|
|
459
|
-
|
|
450
|
+
throw new errors.InternalServerError("Query missing", { message: "query missing or not of type object" });
|
|
460
451
|
}
|
|
461
452
|
const opname = `query ${req.modelname} ${ops++}`;
|
|
462
453
|
console.time(opname);
|
|
@@ -525,7 +516,8 @@ const actionQuery = async (req, res) => {
|
|
|
525
516
|
} catch(err) {
|
|
526
517
|
console.error(new Date(), err);
|
|
527
518
|
if (debug) console.timeEnd(opname);
|
|
528
|
-
|
|
519
|
+
if (err.code) throw err;
|
|
520
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
529
521
|
}
|
|
530
522
|
};
|
|
531
523
|
|
|
@@ -534,7 +526,7 @@ const actionAggregate = async (req, res) => {
|
|
|
534
526
|
let query = (req.body.query) ? req.body.query : req.body; // Don't require to embed in query anymore
|
|
535
527
|
if (!query || !Array.isArray(query)) {
|
|
536
528
|
console.error("query missing or not of type array")
|
|
537
|
-
|
|
529
|
+
throw new errors.InternalServerError("Query missing", { message: "query missing or not of type array" });
|
|
538
530
|
}
|
|
539
531
|
query = query_manipulation.fix_query(query);
|
|
540
532
|
const opname = `aggregate ${req.modelname} ${ops++}`;
|
|
@@ -552,7 +544,7 @@ const actionAggregate = async (req, res) => {
|
|
|
552
544
|
} catch (err) {
|
|
553
545
|
console.error(new Date(), err);
|
|
554
546
|
if (debug) console.timeEnd(opname);
|
|
555
|
-
|
|
547
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
556
548
|
}
|
|
557
549
|
};
|
|
558
550
|
|
|
@@ -560,7 +552,7 @@ const actionAggregate = async (req, res) => {
|
|
|
560
552
|
const actionBulkWrite = async (req, res) => {
|
|
561
553
|
if (!req.body || !Array.isArray(req.body)) {
|
|
562
554
|
console.error("query missing or not of type array")
|
|
563
|
-
|
|
555
|
+
throw new errors.InternalServerError("Query Missing", { message: "query missing or not of type array" });
|
|
564
556
|
}
|
|
565
557
|
const opname = `bulkwrite ${req.modelname} ${ops++}`;
|
|
566
558
|
console.time(opname);
|
|
@@ -575,7 +567,7 @@ const actionBulkWrite = async (req, res) => {
|
|
|
575
567
|
} catch (err) {
|
|
576
568
|
console.error(new Date(), err);
|
|
577
569
|
if (debug) console.timeEnd(opname);
|
|
578
|
-
|
|
570
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
579
571
|
}
|
|
580
572
|
};
|
|
581
573
|
|
|
@@ -613,8 +605,8 @@ const actionBulkWrite = async (req, res) => {
|
|
|
613
605
|
|
|
614
606
|
// Utitlities
|
|
615
607
|
|
|
616
|
-
const getOne = async (Model, item_id, params) => {
|
|
617
|
-
const query = Model.findById(item_id);
|
|
608
|
+
const getOne = async (Model, item_id, params, options) => {
|
|
609
|
+
const query = Model.findById(item_id, {}, options);
|
|
618
610
|
if (params.populate) {
|
|
619
611
|
if ((typeof params.populate === "object") && !Array.isArray(params.populate)) {
|
|
620
612
|
for (let i in params.populate) {
|
|
@@ -636,11 +628,11 @@ const getOne = async (Model, item_id, params) => {
|
|
|
636
628
|
var item = await query.exec();
|
|
637
629
|
if (!item) {
|
|
638
630
|
// console.error("Could not find document");
|
|
639
|
-
|
|
631
|
+
throw new errors.NotFoundError("Could not find document", { message: "Could not find document" });
|
|
640
632
|
}
|
|
641
633
|
if (item._deleted && !params.showDeleted) {
|
|
642
634
|
// console.error("Document is deleted");
|
|
643
|
-
|
|
635
|
+
throw new errors.NotFoundError("Document is deleted", { message: "Document is deleted" });
|
|
644
636
|
}
|
|
645
637
|
item = item.toObject();
|
|
646
638
|
//Don't ever return passwords
|
|
@@ -648,7 +640,8 @@ const getOne = async (Model, item_id, params) => {
|
|
|
648
640
|
return item;
|
|
649
641
|
} catch(err) {
|
|
650
642
|
console.error(err);
|
|
651
|
-
|
|
643
|
+
if (err.code) throw err;
|
|
644
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
652
645
|
}
|
|
653
646
|
};
|
|
654
647
|
|
package/libs/login.js
CHANGED
|
@@ -3,13 +3,12 @@ const jwt = require("jsonwebtoken");
|
|
|
3
3
|
const bcrypt = require('bcryptjs');
|
|
4
4
|
const security = require("../libs/security");
|
|
5
5
|
const nodemailer = require('nodemailer');
|
|
6
|
+
const errors = require("restify-errors");
|
|
6
7
|
const smtpTransport = require('nodemailer-smtp-transport');
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const path = require("path");
|
|
9
|
+
let User = null;
|
|
9
10
|
|
|
10
11
|
var init = function (config) {
|
|
11
|
-
var path = require("path");
|
|
12
|
-
APIKey = require(path.join(config.model_dir, 'apikey_model'));
|
|
13
12
|
User = require(path.join(config.model_dir, 'user_model'));
|
|
14
13
|
security.init(config);
|
|
15
14
|
};
|
|
@@ -30,12 +29,11 @@ const recover = async (req, res) => {
|
|
|
30
29
|
const email = req.body.email;
|
|
31
30
|
if (!email) {
|
|
32
31
|
console.error("Missing email parameter");
|
|
33
|
-
|
|
34
|
-
return;
|
|
32
|
+
throw new errors.BadRequestError("Bad Request", { status: "fail", message: "Missing email parameter" });
|
|
35
33
|
}
|
|
36
34
|
const user = await User.findOne({ email });
|
|
37
35
|
if (!user) {
|
|
38
|
-
throw ("
|
|
36
|
+
throw new errors.NotFoundError("User Not Found", { status: "fail", message: "User not found" });
|
|
39
37
|
}
|
|
40
38
|
const result = await security.generateApiKey(user._id);
|
|
41
39
|
const token = jwt.sign({ apikey: result.apikey, email: user.email, id: user._id }, req.config.shared_secret, { expiresIn: "2d" });
|
|
@@ -57,26 +55,27 @@ const recover = async (req, res) => {
|
|
|
57
55
|
});
|
|
58
56
|
res.send({ status: "ok", message: "Sent recovery email" });
|
|
59
57
|
} catch (err) {
|
|
60
|
-
|
|
58
|
+
if (err.code) throw err;
|
|
59
|
+
throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized", info: err });
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
const logout = async (req, res) => {
|
|
65
64
|
try {
|
|
66
|
-
if (!res.user) throw("You don't seem to be logged in");
|
|
65
|
+
if (!res.user) throw new errors.ForbiddenError("Forbidden", { message: "You don't seem to be logged in" });
|
|
67
66
|
await security.revokeToken(res.user._id);
|
|
68
67
|
res.send({ status: "ok", message: "User logged out" });
|
|
69
68
|
} catch(err) {
|
|
70
69
|
console.error(err);
|
|
71
|
-
|
|
70
|
+
if (err.code) throw err;
|
|
71
|
+
throw new errors.InternalServerError("Server Error", { status: "error", message: err.toString() });
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
const oauth = (req, res, next) => { // Log in through an OAuth2 provider, defined in config.js
|
|
76
76
|
const provider_config = req.config.oauth[req.params.provider];
|
|
77
77
|
if (!provider_config) {
|
|
78
|
-
|
|
79
|
-
return;
|
|
78
|
+
throw new errors.InternalServerError("Internal Server Error", { message: req.params.provider + " config not defined" });
|
|
80
79
|
}
|
|
81
80
|
const state = Math.random().toString(36).substring(7);
|
|
82
81
|
const uri = `${provider_config.auth_uri}?client_id=${provider_config.app_id}&redirect_uri=${req.config.url}/login/oauth/callback/${req.params.provider}&scope=${provider_config.scope}&state=${state}&response_type=code`;
|
|
@@ -149,8 +148,7 @@ const login = async (req, res) => {
|
|
|
149
148
|
}
|
|
150
149
|
if ((!password) || (!email)) {
|
|
151
150
|
console.error(new Date(), "Missing email or password parameters");
|
|
152
|
-
|
|
153
|
-
return;
|
|
151
|
+
return errors.ForbiddenError({ status: "fail", message: "Missing email or password parameters" });
|
|
154
152
|
}
|
|
155
153
|
try {
|
|
156
154
|
const user = await User.findOne({ email });
|
|
@@ -171,28 +169,25 @@ const login = async (req, res) => {
|
|
|
171
169
|
provider: token.provider,
|
|
172
170
|
});
|
|
173
171
|
} catch (err) {
|
|
174
|
-
res.send(401, { status: "fail", message: "Authentication failed", err });
|
|
175
172
|
console.error(new Date(), `Authentication failed`, ip, err);
|
|
176
|
-
|
|
173
|
+
if (err.code) throw err;
|
|
174
|
+
return errors.ForbiddenError({ status: "fail", message: "Authentication failed" });
|
|
177
175
|
}
|
|
178
176
|
}
|
|
179
177
|
|
|
180
178
|
const getJWT = async (req, res) => {
|
|
181
179
|
var user = null;
|
|
182
180
|
if (!res.user.admin) {
|
|
183
|
-
|
|
184
|
-
return;
|
|
181
|
+
throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized" });
|
|
185
182
|
}
|
|
186
183
|
var email = req.params.email || req.body.email;
|
|
187
184
|
if (!email) {
|
|
188
|
-
|
|
189
|
-
return;
|
|
185
|
+
throw new errors.BadRequestError("Bad Request", { status: "fail", message: "Email required" });
|
|
190
186
|
}
|
|
191
187
|
try {
|
|
192
188
|
const result = await User.findOne({ email: email });
|
|
193
189
|
if (!result || !result._id) {
|
|
194
|
-
|
|
195
|
-
return;
|
|
190
|
+
throw new errors.NotFoundError("Not Found", { status: "fail", message: "User not found" });
|
|
196
191
|
}
|
|
197
192
|
user = result;
|
|
198
193
|
try {
|
|
@@ -202,11 +197,11 @@ const getJWT = async (req, res) => {
|
|
|
202
197
|
});
|
|
203
198
|
res.send({ email: user.email, token: token });
|
|
204
199
|
} catch (err) {
|
|
205
|
-
|
|
206
|
-
return;
|
|
200
|
+
throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized" });
|
|
207
201
|
}
|
|
208
202
|
} catch (err) {
|
|
209
|
-
|
|
203
|
+
if (err.code) throw err;
|
|
204
|
+
throw new errors.InternalServerError("Internal Server Error", { status: "error", message: err.toString() });
|
|
210
205
|
}
|
|
211
206
|
}
|
|
212
207
|
|
|
@@ -9,7 +9,7 @@ const getModelFileContents = (model_dir) => {
|
|
|
9
9
|
return reject(err);
|
|
10
10
|
}
|
|
11
11
|
let models = [];
|
|
12
|
-
for (file of files) {
|
|
12
|
+
for (const file of files) {
|
|
13
13
|
const modelname = path.basename(file, ".js").replace("_model", "");
|
|
14
14
|
try {
|
|
15
15
|
const modelobj = require(path.join(model_dir, file));
|
package/libs/security.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const bcrypt = require("bcryptjs");
|
|
2
2
|
const randToken = require("rand-token");
|
|
3
3
|
const path = require("path");
|
|
4
|
+
const errors = require("restify-errors");
|
|
4
5
|
var APIKey = null;
|
|
5
6
|
var Token = null;
|
|
6
7
|
var Groups = null;
|
|
@@ -17,10 +18,6 @@ const init = function(config) {
|
|
|
17
18
|
if (config.url) provider = config.url;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const fail = function (res, code, message) {
|
|
21
|
-
res.send(code, { status: "error", message });
|
|
22
|
-
};
|
|
23
|
-
|
|
24
21
|
const basicAuthData = function(req) {
|
|
25
22
|
if (!req.headers.authorization) {
|
|
26
23
|
return false;
|
|
@@ -51,7 +48,7 @@ const basicAuth = async ba => {
|
|
|
51
48
|
return user;
|
|
52
49
|
} catch(err) {
|
|
53
50
|
console.error(new Date(), err);
|
|
54
|
-
|
|
51
|
+
throw err;
|
|
55
52
|
}
|
|
56
53
|
};
|
|
57
54
|
|
|
@@ -84,7 +81,7 @@ const bearerAuth = async t => {
|
|
|
84
81
|
return user;
|
|
85
82
|
} catch (err) {
|
|
86
83
|
console.error(err);
|
|
87
|
-
|
|
84
|
+
throw err;
|
|
88
85
|
}
|
|
89
86
|
}
|
|
90
87
|
|
|
@@ -98,7 +95,7 @@ const apiKeyAuth = async apikey => {
|
|
|
98
95
|
return user;
|
|
99
96
|
} catch(err) {
|
|
100
97
|
console.error(new Date(), err);
|
|
101
|
-
|
|
98
|
+
throw err;
|
|
102
99
|
}
|
|
103
100
|
};
|
|
104
101
|
|
|
@@ -109,7 +106,7 @@ const getGroups = async user_id => {
|
|
|
109
106
|
return groups;
|
|
110
107
|
} catch(err) {
|
|
111
108
|
console.error(new Date(), err);
|
|
112
|
-
|
|
109
|
+
throw err;
|
|
113
110
|
}
|
|
114
111
|
};
|
|
115
112
|
|
|
@@ -131,7 +128,7 @@ const generateApiKey = async user_id => {
|
|
|
131
128
|
return apikey;
|
|
132
129
|
} catch(err) {
|
|
133
130
|
console.error(new Date(), err);
|
|
134
|
-
|
|
131
|
+
throw err;
|
|
135
132
|
}
|
|
136
133
|
};
|
|
137
134
|
|
|
@@ -156,16 +153,21 @@ const generateToken = async user_id => {
|
|
|
156
153
|
return token;
|
|
157
154
|
} catch (err) {
|
|
158
155
|
console.error(new Date(), err);
|
|
159
|
-
|
|
156
|
+
throw err;
|
|
160
157
|
}
|
|
161
158
|
};
|
|
162
159
|
|
|
163
160
|
const ensureToken = async user_id => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
try {
|
|
162
|
+
const token = await Token.findOne({ user_id, provider }).sort({ createdAt: -1 }).exec();
|
|
163
|
+
if (tokenIsValid(token)) {
|
|
164
|
+
return token;
|
|
165
|
+
}
|
|
166
|
+
return await generateToken(user_id);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(new Date(), err);
|
|
169
|
+
throw err;
|
|
167
170
|
}
|
|
168
|
-
return await generateToken(user_id);
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
const refreshToken = async user_id => {
|
|
@@ -187,7 +189,7 @@ const generateRefreshToken = async user_id => {
|
|
|
187
189
|
return refreshtoken;
|
|
188
190
|
} catch (err) {
|
|
189
191
|
console.error(new Date(), err);
|
|
190
|
-
|
|
192
|
+
throw err;
|
|
191
193
|
}
|
|
192
194
|
};
|
|
193
195
|
|
|
@@ -226,7 +228,8 @@ const refresh = async (req, res) => {
|
|
|
226
228
|
}
|
|
227
229
|
} catch (err) {
|
|
228
230
|
console.error(err);
|
|
229
|
-
|
|
231
|
+
if (err.code) throw err;
|
|
232
|
+
throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
|
|
230
233
|
}
|
|
231
234
|
}
|
|
232
235
|
|
|
@@ -241,7 +244,8 @@ const login = async (req, res) => {
|
|
|
241
244
|
res = Object.assign(res, authenticate_result);
|
|
242
245
|
} catch(err) {
|
|
243
246
|
console.error(err);
|
|
244
|
-
|
|
247
|
+
if (err.code) throw err;
|
|
248
|
+
throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
|
|
245
249
|
}
|
|
246
250
|
};
|
|
247
251
|
|
|
@@ -250,40 +254,37 @@ const authenticate = async req => {
|
|
|
250
254
|
if (!req.query.apikey && !req.headers.authorization && !(req.headers["X-API-Key"] || req.headers["x-api-key"])) {
|
|
251
255
|
return false;
|
|
252
256
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
user
|
|
276
|
-
}
|
|
277
|
-
} catch (err) {
|
|
278
|
-
return Promise.reject(err);
|
|
257
|
+
if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("basic") === 0) {
|
|
258
|
+
// Basic Auth
|
|
259
|
+
user = await basicAuth(basicAuthData(req));
|
|
260
|
+
} else if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("bearer") === 0) {
|
|
261
|
+
// Token Auth
|
|
262
|
+
user = await bearerAuth(bearerAuthData(req));
|
|
263
|
+
} else if (req.query.apikey) {
|
|
264
|
+
user = await apiKeyAuth(req.query.apikey);
|
|
265
|
+
} else if (req.headers["X-API-Key"] || req.headers["x-api-key"]) {
|
|
266
|
+
// API Key
|
|
267
|
+
user = await apiKeyAuth(req.query.apikey || req.headers["X-API-Key"] || req.headers["x-api-key"])
|
|
268
|
+
} else {
|
|
269
|
+
throw ("Could not find any way to authenticate");
|
|
270
|
+
}
|
|
271
|
+
if (!user) {
|
|
272
|
+
throw ("Could not find user");
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
token: await ensureToken(user._id),
|
|
276
|
+
refresh_token: await ensureRefreshToken(user._id),
|
|
277
|
+
groups: await getGroups(user._id),
|
|
278
|
+
user
|
|
279
279
|
}
|
|
280
|
+
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
const auth = async (req, res) => {
|
|
283
284
|
// Check against model as to whether we're allowed to edit this model
|
|
284
285
|
if (!req.Model) {
|
|
285
286
|
console.error("Model missing");
|
|
286
|
-
|
|
287
|
+
throw new errors.InternalServerError("Model missing", { message: "Model missing" });
|
|
287
288
|
}
|
|
288
289
|
try {
|
|
289
290
|
var method = null;
|
|
@@ -298,12 +299,13 @@ const auth = async (req, res) => {
|
|
|
298
299
|
method = "d";
|
|
299
300
|
} else {
|
|
300
301
|
console.error("Unsupported operation", req.method);
|
|
301
|
-
|
|
302
|
+
throw new errors.InternalServerError("Unsupported operation", { message: "Unsupported operation: " + req.method});
|
|
302
303
|
}
|
|
303
|
-
await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
|
|
304
|
+
return await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
|
|
304
305
|
} catch(err) {
|
|
305
306
|
console.error(err);
|
|
306
|
-
|
|
307
|
+
if (err.code) throw err;
|
|
308
|
+
throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
|
|
307
309
|
}
|
|
308
310
|
};
|
|
309
311
|
|
|
@@ -316,7 +318,8 @@ const bulkAuth = async (req, res,) => {
|
|
|
316
318
|
await check_perms(res.user, res.groups, req.Model, "d");
|
|
317
319
|
} catch (err) {
|
|
318
320
|
console.error(err);
|
|
319
|
-
|
|
321
|
+
if (err.code) throw err;
|
|
322
|
+
throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
|
|
320
323
|
}
|
|
321
324
|
};
|
|
322
325
|
|
|
@@ -326,17 +329,17 @@ const check_perms = async (user, groups, model, method, item_id) => {
|
|
|
326
329
|
//If no perms are set, then this isn't an available model
|
|
327
330
|
if (!perms.admin) {
|
|
328
331
|
console.error("Model permissions not set correctly - add an admin section");
|
|
329
|
-
throw("Model permissions not set correctly - add an admin section");
|
|
332
|
+
throw new errors.InternalServerError("Model permissions not set correctly - add an admin section", { message: "Model permissions not set correctly - add an admin section" });
|
|
330
333
|
}
|
|
331
334
|
//First check if "all" is able to do this. If so, let's get on with it.
|
|
332
|
-
if (perms.all) {
|
|
335
|
+
if (perms.all && perms.all.length) {
|
|
333
336
|
if (perms.all.indexOf(method) !== -1) {
|
|
334
337
|
return true;
|
|
335
338
|
}
|
|
336
339
|
}
|
|
337
340
|
//This isn't an 'all' situation, so let's bail if the user isn't logged in
|
|
338
341
|
if (!user) {
|
|
339
|
-
throw("
|
|
342
|
+
throw new errors.ForbiddenError("User not logged in", { message: "User not logged in" });
|
|
340
343
|
}
|
|
341
344
|
//Let's check perms in this order - admin, user, group, owner
|
|
342
345
|
//Admin check
|
|
@@ -362,16 +365,17 @@ const check_perms = async (user, groups, model, method, item_id) => {
|
|
|
362
365
|
if (item && item._owner_id && item._owner_id.toString() == user._id.toString() && (perms.owner && perms.owner.includes(method))) return true;
|
|
363
366
|
throw ("Authorization failed");
|
|
364
367
|
} catch (err) {
|
|
365
|
-
|
|
368
|
+
if (err.code) throw err;
|
|
369
|
+
throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
|
|
366
370
|
}
|
|
367
371
|
}
|
|
368
372
|
|
|
369
373
|
const admin_only = (req, res, next) => { // Chain after login
|
|
370
374
|
if (!res.user) {
|
|
371
|
-
|
|
375
|
+
throw new errors.ForbiddenError("Unauthorized", { message: "Unauthorized" });
|
|
372
376
|
}
|
|
373
377
|
if (!res.user.admin) {
|
|
374
|
-
|
|
378
|
+
throw new errors.ForbiddenError("Unauthorized", { message: "Unauthorized" });
|
|
375
379
|
}
|
|
376
380
|
next();
|
|
377
381
|
}
|
package/libs/setup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const rand_token = require("rand-token");
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const errors = require("restify-errors");
|
|
3
4
|
const security = require("./security");
|
|
4
5
|
const ObjectID = require('mongodb').ObjectID;
|
|
5
6
|
var User = null;
|
|
@@ -9,15 +10,15 @@ const init = config => {
|
|
|
9
10
|
User = require(path.join(config.model_dir, "user_model"));
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const checkUserDoesNotExist = async (
|
|
13
|
+
const checkUserDoesNotExist = async () => {
|
|
13
14
|
try {
|
|
14
15
|
const count = await User.countDocuments();
|
|
15
16
|
if (count) {
|
|
16
|
-
|
|
17
|
+
throw new errors.ConflictError("Conflict Error", { status: "failed", error: "Cannot setup if user exists" });
|
|
17
18
|
}
|
|
18
19
|
} catch(err) {
|
|
19
20
|
console.error(err);
|
|
20
|
-
|
|
21
|
+
throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
|
|
21
22
|
}
|
|
22
23
|
};
|
|
23
24
|
|
|
@@ -46,7 +47,7 @@ const setup = async (req, res) => {
|
|
|
46
47
|
});
|
|
47
48
|
} catch(err) {
|
|
48
49
|
console.error(err);
|
|
49
|
-
|
|
50
|
+
throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
|
|
@@ -76,7 +77,7 @@ const data_setup = async (req, res) => {
|
|
|
76
77
|
res.send({ status: "success", results });
|
|
77
78
|
} catch(err) {
|
|
78
79
|
console.error(err);
|
|
79
|
-
|
|
80
|
+
throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jxp",
|
|
3
3
|
"description:": "An opinionated RESTful API library based on Mongoose and Restify. Make an API by just writing Mongoose models.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.12.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "libs/jxp.js",
|
|
7
7
|
"scripts": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"jstransformer-markdown-it": "^3.0.0",
|
|
40
40
|
"jxp-helper": "^1.4.0",
|
|
41
41
|
"mkdirp": "^1.0.4",
|
|
42
|
-
"mongoose": "6.7.
|
|
42
|
+
"mongoose": "6.7.5",
|
|
43
43
|
"mongoose-friendly": "^0.1.4",
|
|
44
44
|
"morgan": "^1.10.0",
|
|
45
45
|
"nodemailer": "^6.8.0",
|
|
@@ -49,8 +49,9 @@
|
|
|
49
49
|
"querystring": "^0.2.1",
|
|
50
50
|
"rand-token": "^1.0.1",
|
|
51
51
|
"readline-sync": "^1.4.10",
|
|
52
|
-
"restify": "^
|
|
52
|
+
"restify": "^10.0.0",
|
|
53
53
|
"restify-cors-middleware": "^1.1.1",
|
|
54
|
+
"restify-errors": "^8.0.2",
|
|
54
55
|
"traverse": "^0.6.7",
|
|
55
56
|
"underscore": "^1.13.6",
|
|
56
57
|
"ws": "^8.11.0"
|
package/test/init.js
CHANGED
package/test/setup.test.js
CHANGED
|
@@ -3,14 +3,13 @@ const config = require("config");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
5
|
var model_dir = config.model_dir || path.join(process.cwd(), "./models");
|
|
6
|
-
|
|
6
|
+
require(path.join(model_dir, "test_model"));
|
|
7
7
|
|
|
8
8
|
var chai = require('chai');
|
|
9
9
|
var chaiHttp = require('chai-http');
|
|
10
10
|
var should = chai.should();
|
|
11
11
|
|
|
12
12
|
var init = require("./init");
|
|
13
|
-
|
|
14
13
|
var server = require("../bin/server");
|
|
15
14
|
|
|
16
15
|
chai.use(chaiHttp);
|
|
@@ -27,6 +26,7 @@ const mock_data = {
|
|
|
27
26
|
describe('Setup', () => {
|
|
28
27
|
beforeEach(async function() {
|
|
29
28
|
await init.empty_user_collections();
|
|
29
|
+
console.log("Emptied collections");
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
describe("setup", () => {
|
|
@@ -35,6 +35,7 @@ describe('Setup', () => {
|
|
|
35
35
|
.post("/setup")
|
|
36
36
|
.send({ email: init.email, password: init.password })
|
|
37
37
|
.end((err, res) => {
|
|
38
|
+
// console.log(res);
|
|
38
39
|
res.should.have.status(200);
|
|
39
40
|
res.body.status.should.equal('success');
|
|
40
41
|
res.body.name.should.equal('admin');
|
package/test/test.js
CHANGED
|
@@ -121,6 +121,7 @@ describe('Test', () => {
|
|
|
121
121
|
.get("/api/user")
|
|
122
122
|
.auth(init.email, init.password)
|
|
123
123
|
.end((err, res) => {
|
|
124
|
+
// console.log(res.body);
|
|
124
125
|
res.should.have.status(200);
|
|
125
126
|
res.body.data.should.be.an('array');
|
|
126
127
|
done();
|
|
@@ -159,6 +160,7 @@ describe('Test', () => {
|
|
|
159
160
|
chai.request(server)
|
|
160
161
|
.get(`/api/user`)
|
|
161
162
|
.end((err, res) => {
|
|
163
|
+
// console.log(res.body);
|
|
162
164
|
res.should.have.status(403);
|
|
163
165
|
done();
|
|
164
166
|
});
|
|
@@ -565,7 +567,7 @@ describe('Test', () => {
|
|
|
565
567
|
.get(`/api/test?populate=array_link`)
|
|
566
568
|
.auth(init.email, init.password)
|
|
567
569
|
.end((err, res) => {
|
|
568
|
-
// console.log(res.body
|
|
570
|
+
// console.log(res.body);
|
|
569
571
|
res.should.have.status(200);
|
|
570
572
|
res.body.data[0].should.have.property("array_link");
|
|
571
573
|
res.body.data[0].array_link.should.be.an("array");
|
|
@@ -708,7 +710,7 @@ describe('Test', () => {
|
|
|
708
710
|
var query = [
|
|
709
711
|
{
|
|
710
712
|
$match: {
|
|
711
|
-
"_id": `ObjectId(
|
|
713
|
+
"_id": `ObjectId("${objectid}")`
|
|
712
714
|
}
|
|
713
715
|
},
|
|
714
716
|
{ $group: { _id: null, count: { $sum: 1 } } }
|
|
@@ -719,7 +721,7 @@ describe('Test', () => {
|
|
|
719
721
|
.send({ query })
|
|
720
722
|
.end((err, res) => {
|
|
721
723
|
res.should.have.status(200);
|
|
722
|
-
console.log(res.body);
|
|
724
|
+
// console.log(res.body);
|
|
723
725
|
res.body.data.should.be.an('array');
|
|
724
726
|
res.body.data[0].should.have.property("_id");
|
|
725
727
|
res.body.data[0].should.have.property("count");
|
|
@@ -832,21 +834,21 @@ describe('Test', () => {
|
|
|
832
834
|
});
|
|
833
835
|
});
|
|
834
836
|
});
|
|
835
|
-
it("should $push to an array", done => {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
});
|
|
837
|
+
// it("should $push to an array", done => {
|
|
838
|
+
// chai.request(server)
|
|
839
|
+
// .patch("/api/test/" + post_id)
|
|
840
|
+
// .auth(init.email, init.password)
|
|
841
|
+
// .send({ $push: { shmack: "fah" } })
|
|
842
|
+
// .end((err, res) => {
|
|
843
|
+
// // console.log(res.body.data);
|
|
844
|
+
// res.should.have.status(200);
|
|
845
|
+
// res.body.data.should.be.an('object');
|
|
846
|
+
// res.body.data.should.have.property("shmack")
|
|
847
|
+
// res.body.data.shmack.should.be.an("array");
|
|
848
|
+
// res.body.data.shmack.should.have.length(4);
|
|
849
|
+
// done();
|
|
850
|
+
// });
|
|
851
|
+
// });
|
|
850
852
|
});
|
|
851
853
|
|
|
852
854
|
describe("Models", () => {
|
|
@@ -868,6 +870,7 @@ describe('Test', () => {
|
|
|
868
870
|
.del(`/api/test/${post_id}`)
|
|
869
871
|
.auth(init.email, init.password)
|
|
870
872
|
.end((err, res) => {
|
|
873
|
+
console.log(res.body);
|
|
871
874
|
res.should.have.status(200);
|
|
872
875
|
res.body.status.should.equal('ok');
|
|
873
876
|
done();
|
|
@@ -878,9 +881,10 @@ describe('Test', () => {
|
|
|
878
881
|
.get(`/api/test/${post_id}`)
|
|
879
882
|
.auth(init.email, init.password)
|
|
880
883
|
.end((err, res) => {
|
|
884
|
+
console.log(res.body);
|
|
881
885
|
res.should.have.status(404);
|
|
882
886
|
res.body.message.should.equal('Document is deleted');
|
|
883
|
-
res.body.
|
|
887
|
+
res.body.code.should.equal('NotFound');
|
|
884
888
|
done();
|
|
885
889
|
});
|
|
886
890
|
});
|
|
@@ -982,8 +986,7 @@ describe('Test', () => {
|
|
|
982
986
|
.auth(init.email, init.password)
|
|
983
987
|
.end((err, res) => {
|
|
984
988
|
res.should.have.status(409);
|
|
985
|
-
res.body.message.should.equal(`
|
|
986
|
-
res.body.status.should.equal('error');
|
|
989
|
+
res.body.message.should.equal(`Conflict Error`);
|
|
987
990
|
done();
|
|
988
991
|
});
|
|
989
992
|
});
|
|
@@ -992,7 +995,7 @@ describe('Test', () => {
|
|
|
992
995
|
.del(`/api/link/${link_id}?_cascade=1`)
|
|
993
996
|
.auth(init.email, init.password)
|
|
994
997
|
.end((err, res) => {
|
|
995
|
-
console.log(res.body, res.status);
|
|
998
|
+
// console.log(res.body, res.status);
|
|
996
999
|
res.should.have.status(200);
|
|
997
1000
|
res.body.status.should.equal('ok');
|
|
998
1001
|
done();
|
|
@@ -1004,7 +1007,7 @@ describe('Test', () => {
|
|
|
1004
1007
|
.auth(init.email, init.password)
|
|
1005
1008
|
.end((err, res) => {
|
|
1006
1009
|
res.should.have.status(404);
|
|
1007
|
-
res.body.
|
|
1010
|
+
res.body.message.should.equal('Document is deleted');
|
|
1008
1011
|
done();
|
|
1009
1012
|
});
|
|
1010
1013
|
});
|
|
@@ -1060,8 +1063,7 @@ describe('Test', () => {
|
|
|
1060
1063
|
.auth(init.email, init.password)
|
|
1061
1064
|
.end((err, res) => {
|
|
1062
1065
|
res.should.have.status(409);
|
|
1063
|
-
res.body.message.should.equal(`
|
|
1064
|
-
res.body.status.should.equal('error');
|
|
1066
|
+
res.body.message.should.equal(`Conflict Error`);
|
|
1065
1067
|
done();
|
|
1066
1068
|
});
|
|
1067
1069
|
});
|
|
@@ -1087,7 +1089,6 @@ describe('Test', () => {
|
|
|
1087
1089
|
});
|
|
1088
1090
|
|
|
1089
1091
|
describe("/Filter with +", () => {
|
|
1090
|
-
let plus_user_id = null;
|
|
1091
1092
|
it("it should POST a user with a + in email", (done) => {
|
|
1092
1093
|
var user = {
|
|
1093
1094
|
name: "Plus User",
|
|
@@ -1100,7 +1101,6 @@ describe('Test', () => {
|
|
|
1100
1101
|
.end((err, res) => {
|
|
1101
1102
|
res.should.have.status(200);
|
|
1102
1103
|
res.body.data.should.have.property("_id");
|
|
1103
|
-
plus_user_id = res.body.data._id;
|
|
1104
1104
|
done();
|
|
1105
1105
|
});
|
|
1106
1106
|
});
|