jxp 2.11.2 → 2.12.1
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 +44 -44
- package/libs/login.js +20 -25
- package/libs/schema_description.js +1 -1
- package/libs/security.js +65 -61
- 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
|
|
|
@@ -95,8 +101,7 @@ const actionGet = async (req, res) => {
|
|
|
95
101
|
filters = parseFilter(req.query.filter);
|
|
96
102
|
} catch (err) {
|
|
97
103
|
console.trace(new Date(), err);
|
|
98
|
-
|
|
99
|
-
return;
|
|
104
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
100
105
|
}
|
|
101
106
|
let search = parseSearch(req.query.search);
|
|
102
107
|
for (let i in search) {
|
|
@@ -118,7 +123,7 @@ const actionGet = async (req, res) => {
|
|
|
118
123
|
qcount = req.Model.find({ $text: { $search: req.query.search }});
|
|
119
124
|
}
|
|
120
125
|
if (res.user) {
|
|
121
|
-
q.options({ user: res.user });
|
|
126
|
+
q.options = ({ user: res.user });
|
|
122
127
|
}
|
|
123
128
|
try {
|
|
124
129
|
let count = await req.Model.estimatedDocumentCount();
|
|
@@ -185,7 +190,8 @@ const actionGet = async (req, res) => {
|
|
|
185
190
|
} catch(err) {
|
|
186
191
|
console.error(new Date(), err);
|
|
187
192
|
if (debug) console.timeEnd(opname);
|
|
188
|
-
|
|
193
|
+
if (err.code) throw err;
|
|
194
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
189
195
|
}
|
|
190
196
|
};
|
|
191
197
|
|
|
@@ -199,11 +205,8 @@ const actionGetOne = async (req, res) => {
|
|
|
199
205
|
} catch(err) {
|
|
200
206
|
console.error(new Date(), err);
|
|
201
207
|
if (debug) console.timeEnd(opname);
|
|
202
|
-
if (err.
|
|
203
|
-
|
|
204
|
-
} else {
|
|
205
|
-
res.send(err.code || 500, { status: "error", message: err.toString() });
|
|
206
|
-
}
|
|
208
|
+
if (err.code) throw err;
|
|
209
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
207
210
|
}
|
|
208
211
|
};
|
|
209
212
|
|
|
@@ -233,7 +236,7 @@ const actionPost = async (req, res) => {
|
|
|
233
236
|
} catch (err) {
|
|
234
237
|
console.error(new Date(), err);
|
|
235
238
|
if (debug) console.timeEnd(opname);
|
|
236
|
-
|
|
239
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
237
240
|
}
|
|
238
241
|
};
|
|
239
242
|
|
|
@@ -244,8 +247,7 @@ const actionPut = async (req, res) => {
|
|
|
244
247
|
let item = await req.Model.findById(req.params.item_id);
|
|
245
248
|
if (!item) {
|
|
246
249
|
console.error(new Date(), "Document not found");
|
|
247
|
-
|
|
248
|
-
return;
|
|
250
|
+
throw new errors.NotFoundError("Not Found", { message: "Document not found" });
|
|
249
251
|
}
|
|
250
252
|
_populateItem(item, datamunging.deserialize(req.body));
|
|
251
253
|
_versionItem(item);
|
|
@@ -269,8 +271,7 @@ const actionPut = async (req, res) => {
|
|
|
269
271
|
} catch (err) {
|
|
270
272
|
console.error(new Date(), err);
|
|
271
273
|
if (debug) console.timeEnd(opname);
|
|
272
|
-
|
|
273
|
-
return;
|
|
274
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
274
275
|
}
|
|
275
276
|
};
|
|
276
277
|
|
|
@@ -295,8 +296,7 @@ const actionUpdate = async (req, res) => {
|
|
|
295
296
|
} catch (err) {
|
|
296
297
|
console.error(new Date(), err);
|
|
297
298
|
if (debug) console.timeEnd(opname);
|
|
298
|
-
|
|
299
|
-
return;
|
|
299
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
302
|
|
|
@@ -310,8 +310,7 @@ const actionDelete = async (req, res) => {
|
|
|
310
310
|
let item = await req.Model.findById(req.params.item_id);
|
|
311
311
|
if (!item) {
|
|
312
312
|
console.error(new Date(), "Couldn't find item for delete");
|
|
313
|
-
|
|
314
|
-
return;
|
|
313
|
+
throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for delete" });
|
|
315
314
|
}
|
|
316
315
|
// Get linked models
|
|
317
316
|
const linked_models = [];
|
|
@@ -340,8 +339,7 @@ const actionDelete = async (req, res) => {
|
|
|
340
339
|
await models[linked_model.modelname].updateMany(q, { _deleted: true });
|
|
341
340
|
}
|
|
342
341
|
} else {
|
|
343
|
-
|
|
344
|
-
return;
|
|
342
|
+
throw new errors.ConflictError("Conflict Error", { message: `Parent link item exists in ${linked_model.modelname}/${linked_model.field}` });
|
|
345
343
|
}
|
|
346
344
|
}
|
|
347
345
|
}
|
|
@@ -374,8 +372,8 @@ const actionDelete = async (req, res) => {
|
|
|
374
372
|
} catch(err) {
|
|
375
373
|
console.error(new Date(), err);
|
|
376
374
|
if (debug) console.timeEnd(opname);
|
|
377
|
-
|
|
378
|
-
|
|
375
|
+
if (err.code) throw err;
|
|
376
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
379
377
|
}
|
|
380
378
|
};
|
|
381
379
|
|
|
@@ -394,8 +392,7 @@ const actionCount = async (req, res) => {
|
|
|
394
392
|
filters = parseFilter(req.query.filter);
|
|
395
393
|
} catch (err) {
|
|
396
394
|
console.trace(new Date(), err);
|
|
397
|
-
|
|
398
|
-
return;
|
|
395
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
399
396
|
}
|
|
400
397
|
let search = parseSearch(req.query.search);
|
|
401
398
|
for (let i in search) {
|
|
@@ -411,7 +408,8 @@ const actionCount = async (req, res) => {
|
|
|
411
408
|
} catch(err) {
|
|
412
409
|
console.error(new Date(), err);
|
|
413
410
|
if (debug) console.timeEnd(opname);
|
|
414
|
-
|
|
411
|
+
if (err.code) throw err;
|
|
412
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
415
413
|
}
|
|
416
414
|
};
|
|
417
415
|
|
|
@@ -424,7 +422,8 @@ const actionCall = async (req, res) => {
|
|
|
424
422
|
res.json(result);
|
|
425
423
|
} catch(err) {
|
|
426
424
|
console.error(new Date(), err);
|
|
427
|
-
|
|
425
|
+
if (err.code) throw err;
|
|
426
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
428
427
|
}
|
|
429
428
|
};
|
|
430
429
|
|
|
@@ -432,16 +431,15 @@ const actionCallItem = async (req, res) => {
|
|
|
432
431
|
try {
|
|
433
432
|
const item = req.Model.findById(req.params.item_id);
|
|
434
433
|
if (!item) {
|
|
435
|
-
|
|
436
|
-
return;
|
|
434
|
+
throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for call" });
|
|
437
435
|
}
|
|
438
436
|
req.params.__user = res.user || null;
|
|
439
437
|
const result = await req.Model[req.params.method_name](item);
|
|
440
438
|
res.json(result);
|
|
441
|
-
|
|
442
439
|
} catch(err) {
|
|
443
440
|
console.trace(err);
|
|
444
|
-
|
|
441
|
+
if (err.code) throw err;
|
|
442
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
445
443
|
}
|
|
446
444
|
};
|
|
447
445
|
|
|
@@ -449,7 +447,7 @@ const actionCallItem = async (req, res) => {
|
|
|
449
447
|
const actionQuery = async (req, res) => {
|
|
450
448
|
if (!req.body || !req.body.query || typeof req.body.query !== "object") {
|
|
451
449
|
console.error("query missing or not of type object")
|
|
452
|
-
|
|
450
|
+
throw new errors.InternalServerError("Query missing", { message: "query missing or not of type object" });
|
|
453
451
|
}
|
|
454
452
|
const opname = `query ${req.modelname} ${ops++}`;
|
|
455
453
|
console.time(opname);
|
|
@@ -518,7 +516,8 @@ const actionQuery = async (req, res) => {
|
|
|
518
516
|
} catch(err) {
|
|
519
517
|
console.error(new Date(), err);
|
|
520
518
|
if (debug) console.timeEnd(opname);
|
|
521
|
-
|
|
519
|
+
if (err.code) throw err;
|
|
520
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
522
521
|
}
|
|
523
522
|
};
|
|
524
523
|
|
|
@@ -527,7 +526,7 @@ const actionAggregate = async (req, res) => {
|
|
|
527
526
|
let query = (req.body.query) ? req.body.query : req.body; // Don't require to embed in query anymore
|
|
528
527
|
if (!query || !Array.isArray(query)) {
|
|
529
528
|
console.error("query missing or not of type array")
|
|
530
|
-
|
|
529
|
+
throw new errors.InternalServerError("Query missing", { message: "query missing or not of type array" });
|
|
531
530
|
}
|
|
532
531
|
query = query_manipulation.fix_query(query);
|
|
533
532
|
const opname = `aggregate ${req.modelname} ${ops++}`;
|
|
@@ -545,7 +544,7 @@ const actionAggregate = async (req, res) => {
|
|
|
545
544
|
} catch (err) {
|
|
546
545
|
console.error(new Date(), err);
|
|
547
546
|
if (debug) console.timeEnd(opname);
|
|
548
|
-
|
|
547
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
549
548
|
}
|
|
550
549
|
};
|
|
551
550
|
|
|
@@ -553,7 +552,7 @@ const actionAggregate = async (req, res) => {
|
|
|
553
552
|
const actionBulkWrite = async (req, res) => {
|
|
554
553
|
if (!req.body || !Array.isArray(req.body)) {
|
|
555
554
|
console.error("query missing or not of type array")
|
|
556
|
-
|
|
555
|
+
throw new errors.InternalServerError("Query Missing", { message: "query missing or not of type array" });
|
|
557
556
|
}
|
|
558
557
|
const opname = `bulkwrite ${req.modelname} ${ops++}`;
|
|
559
558
|
console.time(opname);
|
|
@@ -568,7 +567,7 @@ const actionBulkWrite = async (req, res) => {
|
|
|
568
567
|
} catch (err) {
|
|
569
568
|
console.error(new Date(), err);
|
|
570
569
|
if (debug) console.timeEnd(opname);
|
|
571
|
-
|
|
570
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
572
571
|
}
|
|
573
572
|
};
|
|
574
573
|
|
|
@@ -629,11 +628,11 @@ const getOne = async (Model, item_id, params, options) => {
|
|
|
629
628
|
var item = await query.exec();
|
|
630
629
|
if (!item) {
|
|
631
630
|
// console.error("Could not find document");
|
|
632
|
-
|
|
631
|
+
throw new errors.NotFoundError("Could not find document", { message: "Could not find document" });
|
|
633
632
|
}
|
|
634
633
|
if (item._deleted && !params.showDeleted) {
|
|
635
634
|
// console.error("Document is deleted");
|
|
636
|
-
|
|
635
|
+
throw new errors.NotFoundError("Document is deleted", { message: "Document is deleted" });
|
|
637
636
|
}
|
|
638
637
|
item = item.toObject();
|
|
639
638
|
//Don't ever return passwords
|
|
@@ -641,7 +640,8 @@ const getOne = async (Model, item_id, params, options) => {
|
|
|
641
640
|
return item;
|
|
642
641
|
} catch(err) {
|
|
643
642
|
console.error(err);
|
|
644
|
-
|
|
643
|
+
if (err.code) throw err;
|
|
644
|
+
throw new errors.InternalServerError("Internal Error", { message: err.toString() });
|
|
645
645
|
}
|
|
646
646
|
};
|
|
647
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 => {
|
|
@@ -175,7 +177,7 @@ const refreshToken = async user_id => {
|
|
|
175
177
|
|
|
176
178
|
const revokeToken = async user_id => {
|
|
177
179
|
await Token.deleteOne({ user_id, provider });
|
|
178
|
-
return
|
|
180
|
+
return;
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
const generateRefreshToken = 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
|
|
|
@@ -201,7 +203,7 @@ const ensureRefreshToken = async user_id => {
|
|
|
201
203
|
|
|
202
204
|
const revokeRefreshToken = async user_id => {
|
|
203
205
|
await RefreshToken.deleteMany({ user_id });
|
|
204
|
-
return
|
|
206
|
+
return;
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
const refresh = async (req, res) => {
|
|
@@ -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
|
-
throw ("Could not find any way to authenticate");
|
|
267
|
-
}
|
|
268
|
-
if (!user) {
|
|
269
|
-
throw ("Could not find user");
|
|
270
|
-
}
|
|
271
|
-
return {
|
|
272
|
-
token: await ensureToken(user._id),
|
|
273
|
-
refresh_token: await ensureRefreshToken(user._id),
|
|
274
|
-
groups: await getGroups(user._id),
|
|
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");
|
|
279
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
|
+
}
|
|
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,52 +329,53 @@ 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
|
-
return
|
|
337
|
+
return;
|
|
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
|
|
343
346
|
if (user.admin && perms.admin && perms.admin.includes(method)) {
|
|
344
347
|
// console.log("Matched permission 'admin':" + method);
|
|
345
|
-
return
|
|
348
|
+
return;
|
|
346
349
|
}
|
|
347
350
|
//User check
|
|
348
351
|
if (perms.user && perms.user.includes(method)) {
|
|
349
352
|
// console.log("Matched permission 'user':" + method);
|
|
350
|
-
return
|
|
353
|
+
return;
|
|
351
354
|
}
|
|
352
355
|
//Group check
|
|
353
356
|
for (let group of groups) {
|
|
354
357
|
if (perms[group] && perms[group].includes(method) ) {
|
|
355
358
|
// console.log("Matched permission '" + group + "':" + method);
|
|
356
|
-
return
|
|
359
|
+
return;
|
|
357
360
|
}
|
|
358
361
|
}
|
|
359
362
|
//Owner check
|
|
360
363
|
if (!item_id) throw (`Authorization failed - ${method}`);
|
|
361
364
|
const item = await model.findById(item_id);
|
|
362
|
-
if (item && item._owner_id && item._owner_id.toString() == user._id.toString() && (perms.owner && perms.owner.includes(method))) return
|
|
365
|
+
if (item && item._owner_id && item._owner_id.toString() == user._id.toString() && (perms.owner && perms.owner.includes(method))) return;
|
|
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.1",
|
|
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
|
});
|