jxp 2.12.1 → 2.12.3
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/bin/setup.js +1 -1
- package/libs/docs.js +8 -8
- package/libs/groups.js +12 -12
- package/libs/jxp.js +41 -36
- package/libs/login.js +13 -13
- package/libs/security.js +14 -13
- package/libs/setup.js +5 -4
- package/models/test_model.js +9 -0
- package/package.json +20 -17
- package/test/test.js +33 -7
package/bin/setup.js
CHANGED
package/libs/docs.js
CHANGED
|
@@ -33,7 +33,7 @@ class Docs {
|
|
|
33
33
|
res.end();
|
|
34
34
|
} catch(err) {
|
|
35
35
|
console.error(err);
|
|
36
|
-
return errors.InternalServerError(
|
|
36
|
+
return new errors.InternalServerError(err.toString());
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -43,20 +43,20 @@ class Docs {
|
|
|
43
43
|
res.send(models);
|
|
44
44
|
} catch (err) {
|
|
45
45
|
console.error(err);
|
|
46
|
-
return errors.InternalServerError(
|
|
46
|
+
return new errors.InternalServerError(err.toString());
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
metaModel(req, res, next) {
|
|
51
51
|
try {
|
|
52
52
|
if (!req.Model) {
|
|
53
|
-
return errors.NotFoundError(
|
|
53
|
+
return new errors.NotFoundError("Model not found")
|
|
54
54
|
}
|
|
55
55
|
res.send(req.Model.schema.paths);
|
|
56
56
|
next();
|
|
57
57
|
} catch (err) {
|
|
58
58
|
console.error(err);
|
|
59
|
-
return errors.InternalServerError(
|
|
59
|
+
return new errors.InternalServerError(err.toString());
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -65,7 +65,7 @@ class Docs {
|
|
|
65
65
|
res.send(this.models);
|
|
66
66
|
next();
|
|
67
67
|
} catch(err) {
|
|
68
|
-
errors.InternalServerError(
|
|
68
|
+
return new errors.InternalServerError(err.toString());
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -74,7 +74,7 @@ class Docs {
|
|
|
74
74
|
this.renderTemplate(res, "index", {});
|
|
75
75
|
next();
|
|
76
76
|
} catch(err) {
|
|
77
|
-
errors.InternalServerError(
|
|
77
|
+
return new errors.InternalServerError(err.toString());
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -84,7 +84,7 @@ class Docs {
|
|
|
84
84
|
const md_contents = md.render(body.toString()).body;
|
|
85
85
|
this.renderTemplate(res, "md", { md_contents });
|
|
86
86
|
} catch(err) {
|
|
87
|
-
errors.InternalServerError(
|
|
87
|
+
return new errors.InternalServerError(err.toString());
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -98,7 +98,7 @@ class Docs {
|
|
|
98
98
|
this.renderTemplate(res, "model", { model, fields, perms });
|
|
99
99
|
next();
|
|
100
100
|
} catch(err) {
|
|
101
|
-
errors.InternalServerError(
|
|
101
|
+
return new errors.InternalServerError(err.toString());
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
package/libs/groups.js
CHANGED
|
@@ -12,11 +12,11 @@ const actionPut = async (req, res) => {
|
|
|
12
12
|
const group = req.body.group;
|
|
13
13
|
if (!group) {
|
|
14
14
|
console.error("Group required");
|
|
15
|
-
return errors.BadRequestError("Group required");
|
|
15
|
+
return new errors.BadRequestError("Group required");
|
|
16
16
|
}
|
|
17
17
|
if (!user_id) {
|
|
18
18
|
console.error("user_id required");
|
|
19
|
-
return errors.BadRequestError("user_id
|
|
19
|
+
return new errors.BadRequestError("user_id is required");
|
|
20
20
|
}
|
|
21
21
|
try {
|
|
22
22
|
let userGroup = await Groups.findOne({ user_id: user_id });
|
|
@@ -39,7 +39,7 @@ const actionPut = async (req, res) => {
|
|
|
39
39
|
res.send(await userGroup.save());
|
|
40
40
|
} catch(err) {
|
|
41
41
|
console.error(err);
|
|
42
|
-
return errors.InternalServerError(
|
|
42
|
+
return new errors.InternalServerError(err.toString());
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -48,11 +48,11 @@ const actionPost = async (req, res) => {
|
|
|
48
48
|
const group = req.body.group;
|
|
49
49
|
if (!group) {
|
|
50
50
|
console.error("Group required");
|
|
51
|
-
return errors.BadRequestError("Group required");
|
|
51
|
+
return new errors.BadRequestError("Group required");
|
|
52
52
|
}
|
|
53
53
|
if (!user_id) {
|
|
54
54
|
console.error("user_id required");
|
|
55
|
-
return errors.BadRequestError("user_id required"
|
|
55
|
+
return new errors.BadRequestError("user_id required");
|
|
56
56
|
}
|
|
57
57
|
try {
|
|
58
58
|
let userGroup = await Groups.findOne({ user_id: user_id });
|
|
@@ -75,7 +75,7 @@ const actionPost = async (req, res) => {
|
|
|
75
75
|
res.send(await userGroup.save());
|
|
76
76
|
} catch (err) {
|
|
77
77
|
console.error(err);
|
|
78
|
-
return errors.InternalServerError(
|
|
78
|
+
return new errors.InternalServerError(err.toString());
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
|
|
@@ -83,7 +83,7 @@ const actionGet = async (req, res) => {
|
|
|
83
83
|
var user_id = req.params.user_id;
|
|
84
84
|
if (!user_id) {
|
|
85
85
|
console.error("user_id required");
|
|
86
|
-
return errors.BadRequestError("user_id required"
|
|
86
|
+
return new errors.BadRequestError("user_id required");
|
|
87
87
|
}
|
|
88
88
|
try {
|
|
89
89
|
let userGroup = await Groups.findOne({ user_id });
|
|
@@ -94,7 +94,7 @@ const actionGet = async (req, res) => {
|
|
|
94
94
|
res.send(userGroup);
|
|
95
95
|
} catch (err) {
|
|
96
96
|
console.error(err);
|
|
97
|
-
return errors.InternalServerError(
|
|
97
|
+
return new errors.InternalServerError(err.toString());
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
|
|
@@ -102,16 +102,16 @@ const actionDelete = async (req, res) => {
|
|
|
102
102
|
const user_id = req.params.user_id;
|
|
103
103
|
const group = req.query.group;
|
|
104
104
|
if (!group) {
|
|
105
|
-
return errors.BadRequestError("Group required");
|
|
105
|
+
return new errors.BadRequestError("Group required");
|
|
106
106
|
}
|
|
107
107
|
if (!user_id) {
|
|
108
108
|
console.error("user_id required");
|
|
109
|
-
return errors.BadRequestError("user_id required"
|
|
109
|
+
return new errors.BadRequestError("user_id required");
|
|
110
110
|
}
|
|
111
111
|
try {
|
|
112
112
|
let userGroup = await Groups.findOne({ user_id });
|
|
113
113
|
if (!userGroup) {
|
|
114
|
-
return errors.BadRequestError("User not found");
|
|
114
|
+
return new errors.BadRequestError("User not found");
|
|
115
115
|
}
|
|
116
116
|
let i = userGroup.groups.indexOf(group);
|
|
117
117
|
if (i > -1) {
|
|
@@ -120,7 +120,7 @@ const actionDelete = async (req, res) => {
|
|
|
120
120
|
res.send(await userGroup.save());
|
|
121
121
|
} catch (err) {
|
|
122
122
|
console.error(err);
|
|
123
|
-
return errors.InternalServerError(
|
|
123
|
+
return new errors.InternalServerError(err.toString());
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
package/libs/jxp.js
CHANGED
|
@@ -13,6 +13,9 @@ const morgan = require("morgan");
|
|
|
13
13
|
const ws = require("./ws");
|
|
14
14
|
const modeldir = require("./modeldir");
|
|
15
15
|
const query_manipulation = require("./query_manipulation");
|
|
16
|
+
const corsMiddleware = require('restify-cors-middleware2');
|
|
17
|
+
const json2csv = require('json2csv').parse;
|
|
18
|
+
global.JXPSchema = require("./schema");
|
|
16
19
|
|
|
17
20
|
var models = {};
|
|
18
21
|
|
|
@@ -30,7 +33,7 @@ const middlewareModel = (req, res, next) => {
|
|
|
30
33
|
return next();
|
|
31
34
|
} catch (err) {
|
|
32
35
|
console.error(new Date, err);
|
|
33
|
-
throw new errors.NotFoundError(
|
|
36
|
+
throw new errors.NotFoundError(`Model ${modelname} not found`);
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
|
|
@@ -56,16 +59,15 @@ const outputJSON = (req, res, next) => {
|
|
|
56
59
|
next();
|
|
57
60
|
} catch (err) {
|
|
58
61
|
console.error(new Date(), err);
|
|
59
|
-
throw new errors.InternalServerError(
|
|
62
|
+
throw new errors.InternalServerError(err.toString());
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
// Outputs whatever is in res.result as CSV
|
|
64
67
|
const outputCSV = (req, res, next) => {
|
|
65
|
-
const json2csv = require('json2csv').parse;
|
|
66
68
|
const opts = { "flatten": true };
|
|
67
69
|
if (!res.result.data) {
|
|
68
|
-
throw new errors.InternalServerError("Error generating
|
|
70
|
+
throw new errors.InternalServerError("Error generating CSV");
|
|
69
71
|
}
|
|
70
72
|
try {
|
|
71
73
|
const data = res.result.data.map(row => row._doc);
|
|
@@ -81,7 +83,7 @@ const outputCSV = (req, res, next) => {
|
|
|
81
83
|
next();
|
|
82
84
|
} catch (err) {
|
|
83
85
|
console.error(err);
|
|
84
|
-
throw new errors.InternalServerError(
|
|
86
|
+
throw new errors.InternalServerError(err.toString());
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -101,7 +103,7 @@ const actionGet = async (req, res) => {
|
|
|
101
103
|
filters = parseFilter(req.query.filter);
|
|
102
104
|
} catch (err) {
|
|
103
105
|
console.trace(new Date(), err);
|
|
104
|
-
throw new errors.InternalServerError(
|
|
106
|
+
throw new errors.InternalServerError(err.toString());
|
|
105
107
|
}
|
|
106
108
|
let search = parseSearch(req.query.search);
|
|
107
109
|
for (let i in search) {
|
|
@@ -191,7 +193,7 @@ const actionGet = async (req, res) => {
|
|
|
191
193
|
console.error(new Date(), err);
|
|
192
194
|
if (debug) console.timeEnd(opname);
|
|
193
195
|
if (err.code) throw err;
|
|
194
|
-
throw new errors.InternalServerError(
|
|
196
|
+
throw new errors.InternalServerError(err.toString());
|
|
195
197
|
}
|
|
196
198
|
};
|
|
197
199
|
|
|
@@ -206,7 +208,7 @@ const actionGetOne = async (req, res) => {
|
|
|
206
208
|
console.error(new Date(), err);
|
|
207
209
|
if (debug) console.timeEnd(opname);
|
|
208
210
|
if (err.code) throw err;
|
|
209
|
-
throw new errors.InternalServerError(
|
|
211
|
+
throw new errors.InternalServerError(err.toString());
|
|
210
212
|
}
|
|
211
213
|
};
|
|
212
214
|
|
|
@@ -236,7 +238,8 @@ const actionPost = async (req, res) => {
|
|
|
236
238
|
} catch (err) {
|
|
237
239
|
console.error(new Date(), err);
|
|
238
240
|
if (debug) console.timeEnd(opname);
|
|
239
|
-
|
|
241
|
+
if (err.code) throw err;
|
|
242
|
+
throw new errors.InternalServerError(err.toString());
|
|
240
243
|
}
|
|
241
244
|
};
|
|
242
245
|
|
|
@@ -247,7 +250,7 @@ const actionPut = async (req, res) => {
|
|
|
247
250
|
let item = await req.Model.findById(req.params.item_id);
|
|
248
251
|
if (!item) {
|
|
249
252
|
console.error(new Date(), "Document not found");
|
|
250
|
-
throw new errors.NotFoundError(
|
|
253
|
+
throw new errors.NotFoundError(`Document ${req.params.item_id} not found on ${req.modelname}`);
|
|
251
254
|
}
|
|
252
255
|
_populateItem(item, datamunging.deserialize(req.body));
|
|
253
256
|
_versionItem(item);
|
|
@@ -271,7 +274,8 @@ const actionPut = async (req, res) => {
|
|
|
271
274
|
} catch (err) {
|
|
272
275
|
console.error(new Date(), err);
|
|
273
276
|
if (debug) console.timeEnd(opname);
|
|
274
|
-
|
|
277
|
+
if (err.code) throw err;
|
|
278
|
+
throw new errors.InternalServerError(err.toString());
|
|
275
279
|
}
|
|
276
280
|
};
|
|
277
281
|
|
|
@@ -289,14 +293,15 @@ const actionUpdate = async (req, res) => {
|
|
|
289
293
|
}
|
|
290
294
|
res.json({
|
|
291
295
|
status: "ok",
|
|
292
|
-
message: req.modelname + "
|
|
296
|
+
message: req.modelname + " updated",
|
|
293
297
|
data
|
|
294
298
|
});
|
|
295
299
|
if (debug) console.timeEnd(opname);
|
|
296
300
|
} catch (err) {
|
|
297
301
|
console.error(new Date(), err);
|
|
298
302
|
if (debug) console.timeEnd(opname);
|
|
299
|
-
|
|
303
|
+
if (err.code) throw err;
|
|
304
|
+
throw new errors.InternalServerError(err.toString());
|
|
300
305
|
}
|
|
301
306
|
};
|
|
302
307
|
|
|
@@ -309,8 +314,7 @@ const actionDelete = async (req, res) => {
|
|
|
309
314
|
try {
|
|
310
315
|
let item = await req.Model.findById(req.params.item_id);
|
|
311
316
|
if (!item) {
|
|
312
|
-
|
|
313
|
-
throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for delete" });
|
|
317
|
+
throw new errors.NotFoundError(`Couldn't find item ${req.params.item_id} for delete on ${req.modelname}`);
|
|
314
318
|
}
|
|
315
319
|
// Get linked models
|
|
316
320
|
const linked_models = [];
|
|
@@ -339,7 +343,7 @@ const actionDelete = async (req, res) => {
|
|
|
339
343
|
await models[linked_model.modelname].updateMany(q, { _deleted: true });
|
|
340
344
|
}
|
|
341
345
|
} else {
|
|
342
|
-
throw new errors.ConflictError(
|
|
346
|
+
throw new errors.ConflictError(`Parent link item exists in ${linked_model.modelname}/${linked_model.field}`);
|
|
343
347
|
}
|
|
344
348
|
}
|
|
345
349
|
}
|
|
@@ -373,7 +377,7 @@ const actionDelete = async (req, res) => {
|
|
|
373
377
|
console.error(new Date(), err);
|
|
374
378
|
if (debug) console.timeEnd(opname);
|
|
375
379
|
if (err.code) throw err;
|
|
376
|
-
throw new errors.InternalServerError(
|
|
380
|
+
throw new errors.InternalServerError(err.toString());
|
|
377
381
|
}
|
|
378
382
|
};
|
|
379
383
|
|
|
@@ -392,7 +396,7 @@ const actionCount = async (req, res) => {
|
|
|
392
396
|
filters = parseFilter(req.query.filter);
|
|
393
397
|
} catch (err) {
|
|
394
398
|
console.trace(new Date(), err);
|
|
395
|
-
throw new errors.InternalServerError(
|
|
399
|
+
throw new errors.InternalServerError(err.toString());
|
|
396
400
|
}
|
|
397
401
|
let search = parseSearch(req.query.search);
|
|
398
402
|
for (let i in search) {
|
|
@@ -409,7 +413,7 @@ const actionCount = async (req, res) => {
|
|
|
409
413
|
console.error(new Date(), err);
|
|
410
414
|
if (debug) console.timeEnd(opname);
|
|
411
415
|
if (err.code) throw err;
|
|
412
|
-
throw new errors.InternalServerError(
|
|
416
|
+
throw new errors.InternalServerError(err.toString());
|
|
413
417
|
}
|
|
414
418
|
};
|
|
415
419
|
|
|
@@ -423,7 +427,7 @@ const actionCall = async (req, res) => {
|
|
|
423
427
|
} catch(err) {
|
|
424
428
|
console.error(new Date(), err);
|
|
425
429
|
if (err.code) throw err;
|
|
426
|
-
throw new errors.InternalServerError(
|
|
430
|
+
throw new errors.InternalServerError(err.toString());
|
|
427
431
|
}
|
|
428
432
|
};
|
|
429
433
|
|
|
@@ -431,7 +435,7 @@ const actionCallItem = async (req, res) => {
|
|
|
431
435
|
try {
|
|
432
436
|
const item = req.Model.findById(req.params.item_id);
|
|
433
437
|
if (!item) {
|
|
434
|
-
throw new errors.NotFoundError(
|
|
438
|
+
throw new errors.NotFoundError(`Couldn't find item ${req.params.item_id} on ${req.modelname} for call`);
|
|
435
439
|
}
|
|
436
440
|
req.params.__user = res.user || null;
|
|
437
441
|
const result = await req.Model[req.params.method_name](item);
|
|
@@ -439,15 +443,14 @@ const actionCallItem = async (req, res) => {
|
|
|
439
443
|
} catch(err) {
|
|
440
444
|
console.trace(err);
|
|
441
445
|
if (err.code) throw err;
|
|
442
|
-
throw new errors.InternalServerError(
|
|
446
|
+
throw new errors.InternalServerError(err.toString());
|
|
443
447
|
}
|
|
444
448
|
};
|
|
445
449
|
|
|
446
450
|
// Actions (verbs)
|
|
447
451
|
const actionQuery = async (req, res) => {
|
|
448
452
|
if (!req.body || !req.body.query || typeof req.body.query !== "object") {
|
|
449
|
-
|
|
450
|
-
throw new errors.InternalServerError("Query missing", { message: "query missing or not of type object" });
|
|
453
|
+
throw new errors.BadRequestError("Query missing or not of type object");
|
|
451
454
|
}
|
|
452
455
|
const opname = `query ${req.modelname} ${ops++}`;
|
|
453
456
|
console.time(opname);
|
|
@@ -517,7 +520,7 @@ const actionQuery = async (req, res) => {
|
|
|
517
520
|
console.error(new Date(), err);
|
|
518
521
|
if (debug) console.timeEnd(opname);
|
|
519
522
|
if (err.code) throw err;
|
|
520
|
-
throw new errors.InternalServerError(
|
|
523
|
+
throw new errors.InternalServerError(err.toString());
|
|
521
524
|
}
|
|
522
525
|
};
|
|
523
526
|
|
|
@@ -526,7 +529,7 @@ const actionAggregate = async (req, res) => {
|
|
|
526
529
|
let query = (req.body.query) ? req.body.query : req.body; // Don't require to embed in query anymore
|
|
527
530
|
if (!query || !Array.isArray(query)) {
|
|
528
531
|
console.error("query missing or not of type array")
|
|
529
|
-
throw new errors.
|
|
532
|
+
throw new errors.BadRequestError("Query missing or not of type array");
|
|
530
533
|
}
|
|
531
534
|
query = query_manipulation.fix_query(query);
|
|
532
535
|
const opname = `aggregate ${req.modelname} ${ops++}`;
|
|
@@ -544,7 +547,7 @@ const actionAggregate = async (req, res) => {
|
|
|
544
547
|
} catch (err) {
|
|
545
548
|
console.error(new Date(), err);
|
|
546
549
|
if (debug) console.timeEnd(opname);
|
|
547
|
-
throw new errors.InternalServerError(
|
|
550
|
+
throw new errors.InternalServerError(err.toString());
|
|
548
551
|
}
|
|
549
552
|
};
|
|
550
553
|
|
|
@@ -552,7 +555,7 @@ const actionAggregate = async (req, res) => {
|
|
|
552
555
|
const actionBulkWrite = async (req, res) => {
|
|
553
556
|
if (!req.body || !Array.isArray(req.body)) {
|
|
554
557
|
console.error("query missing or not of type array")
|
|
555
|
-
throw new errors.
|
|
558
|
+
throw new errors.BadRequestError("Query missing or not of type array");
|
|
556
559
|
}
|
|
557
560
|
const opname = `bulkwrite ${req.modelname} ${ops++}`;
|
|
558
561
|
console.time(opname);
|
|
@@ -567,7 +570,7 @@ const actionBulkWrite = async (req, res) => {
|
|
|
567
570
|
} catch (err) {
|
|
568
571
|
console.error(new Date(), err);
|
|
569
572
|
if (debug) console.timeEnd(opname);
|
|
570
|
-
throw new errors.InternalServerError(
|
|
573
|
+
throw new errors.InternalServerError(err.toString());
|
|
571
574
|
}
|
|
572
575
|
};
|
|
573
576
|
|
|
@@ -628,11 +631,11 @@ const getOne = async (Model, item_id, params, options) => {
|
|
|
628
631
|
var item = await query.exec();
|
|
629
632
|
if (!item) {
|
|
630
633
|
// console.error("Could not find document");
|
|
631
|
-
throw new errors.NotFoundError(
|
|
634
|
+
throw new errors.NotFoundError(`Could not find document ${item_id} on ${Model.modelName}`);
|
|
632
635
|
}
|
|
633
636
|
if (item._deleted && !params.showDeleted) {
|
|
634
637
|
// console.error("Document is deleted");
|
|
635
|
-
throw new errors.NotFoundError(
|
|
638
|
+
throw new errors.NotFoundError(`Document ${item_id} is deleted on ${Model.modelName}`);
|
|
636
639
|
}
|
|
637
640
|
item = item.toObject();
|
|
638
641
|
//Don't ever return passwords
|
|
@@ -641,7 +644,7 @@ const getOne = async (Model, item_id, params, options) => {
|
|
|
641
644
|
} catch(err) {
|
|
642
645
|
console.error(err);
|
|
643
646
|
if (err.code) throw err;
|
|
644
|
-
throw new errors.InternalServerError(
|
|
647
|
+
throw new errors.InternalServerError(err.toString());
|
|
645
648
|
}
|
|
646
649
|
};
|
|
647
650
|
|
|
@@ -762,8 +765,6 @@ const changeUrlParams = (req, key, val) => {
|
|
|
762
765
|
return req.config.url + req.path() + "?" + querystring.stringify(q);
|
|
763
766
|
};
|
|
764
767
|
|
|
765
|
-
global.JXPSchema = require("./schema");
|
|
766
|
-
|
|
767
768
|
const JXP = function(options) {
|
|
768
769
|
const server = restify.createServer();
|
|
769
770
|
const model_dir = options.model_dir || modeldir.findModelDir(path.dirname(process.argv[1]));
|
|
@@ -872,8 +873,6 @@ const JXP = function(options) {
|
|
|
872
873
|
server.use(morgan("combined", { stream: accessLogStream }));
|
|
873
874
|
|
|
874
875
|
// CORS
|
|
875
|
-
const corsMiddleware = require('restify-cors-middleware');
|
|
876
|
-
|
|
877
876
|
const cors = corsMiddleware({
|
|
878
877
|
preflightMaxAge: 5, //Optional
|
|
879
878
|
origins: ['*'],
|
|
@@ -894,6 +893,12 @@ const JXP = function(options) {
|
|
|
894
893
|
next();
|
|
895
894
|
});
|
|
896
895
|
|
|
896
|
+
// Set req.username = "anonymous" if not logged in
|
|
897
|
+
server.use((req, res, next) => {
|
|
898
|
+
if (!req.username) req.username = "anonymous";
|
|
899
|
+
next();
|
|
900
|
+
});
|
|
901
|
+
|
|
897
902
|
// Define our endpoints
|
|
898
903
|
|
|
899
904
|
/* Our API endpoints */
|
package/libs/login.js
CHANGED
|
@@ -29,11 +29,11 @@ const recover = async (req, res) => {
|
|
|
29
29
|
const email = req.body.email;
|
|
30
30
|
if (!email) {
|
|
31
31
|
console.error("Missing email parameter");
|
|
32
|
-
throw new errors.BadRequestError("
|
|
32
|
+
throw new errors.BadRequestError("Missing email parameter");
|
|
33
33
|
}
|
|
34
34
|
const user = await User.findOne({ email });
|
|
35
35
|
if (!user) {
|
|
36
|
-
throw new errors.NotFoundError(
|
|
36
|
+
throw new errors.NotFoundError(`User with email ${email} Not Found`);
|
|
37
37
|
}
|
|
38
38
|
const result = await security.generateApiKey(user._id);
|
|
39
39
|
const token = jwt.sign({ apikey: result.apikey, email: user.email, id: user._id }, req.config.shared_secret, { expiresIn: "2d" });
|
|
@@ -56,26 +56,26 @@ const recover = async (req, res) => {
|
|
|
56
56
|
res.send({ status: "ok", message: "Sent recovery email" });
|
|
57
57
|
} catch (err) {
|
|
58
58
|
if (err.code) throw err;
|
|
59
|
-
throw new errors.UnauthorizedError(
|
|
59
|
+
throw new errors.UnauthorizedError(err.toString());
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const logout = async (req, res) => {
|
|
64
64
|
try {
|
|
65
|
-
if (!res.user) throw new errors.ForbiddenError("
|
|
65
|
+
if (!res.user) throw new errors.ForbiddenError("You don't seem to be logged in");
|
|
66
66
|
await security.revokeToken(res.user._id);
|
|
67
67
|
res.send({ status: "ok", message: "User logged out" });
|
|
68
68
|
} catch(err) {
|
|
69
69
|
console.error(err);
|
|
70
70
|
if (err.code) throw err;
|
|
71
|
-
throw new errors.InternalServerError(
|
|
71
|
+
throw new errors.InternalServerError(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
|
-
throw new errors.InternalServerError(
|
|
78
|
+
throw new errors.InternalServerError(`oAuth ${req.params.provider} config not defined`);
|
|
79
79
|
}
|
|
80
80
|
const state = Math.random().toString(36).substring(7);
|
|
81
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`;
|
|
@@ -148,7 +148,7 @@ const login = async (req, res) => {
|
|
|
148
148
|
}
|
|
149
149
|
if ((!password) || (!email)) {
|
|
150
150
|
console.error(new Date(), "Missing email or password parameters");
|
|
151
|
-
return errors.ForbiddenError(
|
|
151
|
+
return new errors.ForbiddenError("Missing email or password parameters");
|
|
152
152
|
}
|
|
153
153
|
try {
|
|
154
154
|
const user = await User.findOne({ email });
|
|
@@ -171,23 +171,23 @@ const login = async (req, res) => {
|
|
|
171
171
|
} catch (err) {
|
|
172
172
|
console.error(new Date(), `Authentication failed`, ip, err);
|
|
173
173
|
if (err.code) throw err;
|
|
174
|
-
return errors.ForbiddenError(
|
|
174
|
+
return new errors.ForbiddenError(err.toString());
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
const getJWT = async (req, res) => {
|
|
179
179
|
var user = null;
|
|
180
180
|
if (!res.user.admin) {
|
|
181
|
-
throw new errors.UnauthorizedError("Unauthorized"
|
|
181
|
+
throw new errors.UnauthorizedError("Unauthorized");
|
|
182
182
|
}
|
|
183
183
|
var email = req.params.email || req.body.email;
|
|
184
184
|
if (!email) {
|
|
185
|
-
throw new errors.BadRequestError("
|
|
185
|
+
throw new errors.BadRequestError("Email required");
|
|
186
186
|
}
|
|
187
187
|
try {
|
|
188
188
|
const result = await User.findOne({ email: email });
|
|
189
189
|
if (!result || !result._id) {
|
|
190
|
-
throw new errors.NotFoundError("
|
|
190
|
+
throw new errors.NotFoundError("User not found");
|
|
191
191
|
}
|
|
192
192
|
user = result;
|
|
193
193
|
try {
|
|
@@ -197,11 +197,11 @@ const getJWT = async (req, res) => {
|
|
|
197
197
|
});
|
|
198
198
|
res.send({ email: user.email, token: token });
|
|
199
199
|
} catch (err) {
|
|
200
|
-
throw new errors.UnauthorizedError("Unauthorized"
|
|
200
|
+
throw new errors.UnauthorizedError("Unauthorized");
|
|
201
201
|
}
|
|
202
202
|
} catch (err) {
|
|
203
203
|
if (err.code) throw err;
|
|
204
|
-
throw new errors.InternalServerError(
|
|
204
|
+
throw new errors.InternalServerError(err.toString());
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
package/libs/security.js
CHANGED
|
@@ -229,7 +229,7 @@ const refresh = async (req, res) => {
|
|
|
229
229
|
} catch (err) {
|
|
230
230
|
console.error(err);
|
|
231
231
|
if (err.code) throw err;
|
|
232
|
-
throw new errors.ForbiddenError(
|
|
232
|
+
throw new errors.ForbiddenError(err.toString());
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -245,7 +245,7 @@ const login = async (req, res) => {
|
|
|
245
245
|
} catch(err) {
|
|
246
246
|
console.error(err);
|
|
247
247
|
if (err.code) throw err;
|
|
248
|
-
throw new errors.ForbiddenError(
|
|
248
|
+
throw new errors.ForbiddenError(err.toString());
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
251
|
|
|
@@ -275,6 +275,7 @@ const authenticate = async req => {
|
|
|
275
275
|
token: await ensureToken(user._id),
|
|
276
276
|
refresh_token: await ensureRefreshToken(user._id),
|
|
277
277
|
groups: await getGroups(user._id),
|
|
278
|
+
username: user.email,
|
|
278
279
|
user
|
|
279
280
|
}
|
|
280
281
|
|
|
@@ -283,8 +284,8 @@ const authenticate = async req => {
|
|
|
283
284
|
const auth = async (req, res) => {
|
|
284
285
|
// Check against model as to whether we're allowed to edit this model
|
|
285
286
|
if (!req.Model) {
|
|
286
|
-
console.error("Model missing");
|
|
287
|
-
throw new errors.
|
|
287
|
+
// console.error("Model missing");
|
|
288
|
+
throw new errors.BadRequestError("Model missing");
|
|
288
289
|
}
|
|
289
290
|
try {
|
|
290
291
|
var method = null;
|
|
@@ -298,14 +299,14 @@ const auth = async (req, res) => {
|
|
|
298
299
|
} else if (req.method == "DELETE") {
|
|
299
300
|
method = "d";
|
|
300
301
|
} else {
|
|
301
|
-
console.error("Unsupported operation", req.method);
|
|
302
|
-
throw new errors.InternalServerError(
|
|
302
|
+
// console.error("Unsupported operation", req.method);
|
|
303
|
+
throw new errors.InternalServerError(`Unsupported operation: ${req.method}`);
|
|
303
304
|
}
|
|
304
305
|
return await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
|
|
305
306
|
} catch(err) {
|
|
306
307
|
console.error(err);
|
|
307
308
|
if (err.code) throw err;
|
|
308
|
-
throw new errors.ForbiddenError(
|
|
309
|
+
throw new errors.ForbiddenError(err.toString());
|
|
309
310
|
}
|
|
310
311
|
};
|
|
311
312
|
|
|
@@ -319,7 +320,7 @@ const bulkAuth = async (req, res,) => {
|
|
|
319
320
|
} catch (err) {
|
|
320
321
|
console.error(err);
|
|
321
322
|
if (err.code) throw err;
|
|
322
|
-
throw new errors.ForbiddenError(
|
|
323
|
+
throw new errors.ForbiddenError(err.toString());
|
|
323
324
|
}
|
|
324
325
|
};
|
|
325
326
|
|
|
@@ -329,7 +330,7 @@ const check_perms = async (user, groups, model, method, item_id) => {
|
|
|
329
330
|
//If no perms are set, then this isn't an available model
|
|
330
331
|
if (!perms.admin) {
|
|
331
332
|
console.error("Model permissions not set correctly - add an admin section");
|
|
332
|
-
throw new errors.InternalServerError("Model permissions not set correctly - add an admin section"
|
|
333
|
+
throw new errors.InternalServerError("Model permissions not set correctly - add an admin section");
|
|
333
334
|
}
|
|
334
335
|
//First check if "all" is able to do this. If so, let's get on with it.
|
|
335
336
|
if (perms.all && perms.all.length) {
|
|
@@ -339,7 +340,7 @@ const check_perms = async (user, groups, model, method, item_id) => {
|
|
|
339
340
|
}
|
|
340
341
|
//This isn't an 'all' situation, so let's bail if the user isn't logged in
|
|
341
342
|
if (!user) {
|
|
342
|
-
throw new errors.ForbiddenError("User not logged in"
|
|
343
|
+
throw new errors.ForbiddenError("User not logged in");
|
|
343
344
|
}
|
|
344
345
|
//Let's check perms in this order - admin, user, group, owner
|
|
345
346
|
//Admin check
|
|
@@ -366,16 +367,16 @@ const check_perms = async (user, groups, model, method, item_id) => {
|
|
|
366
367
|
throw ("Authorization failed");
|
|
367
368
|
} catch (err) {
|
|
368
369
|
if (err.code) throw err;
|
|
369
|
-
throw new errors.ForbiddenError(
|
|
370
|
+
throw new errors.ForbiddenError(err.toString());
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
373
|
|
|
373
374
|
const admin_only = (req, res, next) => { // Chain after login
|
|
374
375
|
if (!res.user) {
|
|
375
|
-
throw new errors.ForbiddenError("
|
|
376
|
+
throw new errors.ForbiddenError("User not logged in");
|
|
376
377
|
}
|
|
377
378
|
if (!res.user.admin) {
|
|
378
|
-
throw new errors.ForbiddenError("
|
|
379
|
+
throw new errors.ForbiddenError("User not admin");
|
|
379
380
|
}
|
|
380
381
|
next();
|
|
381
382
|
}
|
package/libs/setup.js
CHANGED
|
@@ -14,11 +14,12 @@ const checkUserDoesNotExist = async () => {
|
|
|
14
14
|
try {
|
|
15
15
|
const count = await User.countDocuments();
|
|
16
16
|
if (count) {
|
|
17
|
-
throw new errors.ConflictError("
|
|
17
|
+
throw new errors.ConflictError("Cannot setup if user exists");
|
|
18
18
|
}
|
|
19
19
|
} catch(err) {
|
|
20
20
|
console.error(err);
|
|
21
|
-
|
|
21
|
+
if (err.code) throw err;
|
|
22
|
+
throw new errors.InternalServerError(err.toString());
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
25
|
|
|
@@ -47,7 +48,7 @@ const setup = async (req, res) => {
|
|
|
47
48
|
});
|
|
48
49
|
} catch(err) {
|
|
49
50
|
console.error(err);
|
|
50
|
-
throw new errors.InternalServerError(
|
|
51
|
+
throw new errors.InternalServerError(err.toString());
|
|
51
52
|
}
|
|
52
53
|
};
|
|
53
54
|
|
|
@@ -77,7 +78,7 @@ const data_setup = async (req, res) => {
|
|
|
77
78
|
res.send({ status: "success", results });
|
|
78
79
|
} catch(err) {
|
|
79
80
|
console.error(err);
|
|
80
|
-
throw new errors.InternalServerError(
|
|
81
|
+
throw new errors.InternalServerError(err.toString());
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
package/models/test_model.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* global JXPSchema ObjectId Mixed */
|
|
2
|
+
const errors = require("restify-errors");
|
|
2
3
|
|
|
3
4
|
const TestSchema = new JXPSchema({
|
|
4
5
|
foo: String, // A normal string
|
|
@@ -33,6 +34,14 @@ TestSchema.statics.test = function() {
|
|
|
33
34
|
return "Testing OKAY!";
|
|
34
35
|
};
|
|
35
36
|
|
|
37
|
+
TestSchema.pre("save", function(next) {
|
|
38
|
+
// If we have the setting "error" set to true, we will throw an error
|
|
39
|
+
if (this.bar == "Throw an error") {
|
|
40
|
+
throw new errors.ImATeapotError("I'm a teapot");
|
|
41
|
+
}
|
|
42
|
+
next();
|
|
43
|
+
});
|
|
44
|
+
|
|
36
45
|
// Finally, we export our model. Make sure to change the name!
|
|
37
46
|
const Test = JXPSchema.model('Test', TestSchema);
|
|
38
47
|
module.exports = Test;
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
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.12.
|
|
4
|
+
"version": "2.12.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "libs/jxp.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"start": "node bin/server.js",
|
|
9
|
-
"test": "mocha --timeout
|
|
10
|
-
"wstest": "mocha test/wstest"
|
|
9
|
+
"test": "mocha --timeout 1000 -b --exit",
|
|
10
|
+
"wstest": "mocha test/wstest",
|
|
11
|
+
"setup": "node bin/setup.js"
|
|
11
12
|
},
|
|
12
13
|
"bin": {
|
|
14
|
+
"jxp": "bin/setup.js",
|
|
13
15
|
"jxp-setup": "bin/setup.js",
|
|
14
16
|
"jxp-add-admin": "bin/init_db.js"
|
|
15
17
|
},
|
|
@@ -27,40 +29,41 @@
|
|
|
27
29
|
"url": "https://github.com/j-norwood-young/jexpress-2/issues"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
|
-
"axios": "^1.2.
|
|
32
|
+
"axios": "^1.2.3",
|
|
31
33
|
"bcryptjs": "^2.4.3",
|
|
32
|
-
"commander": "^
|
|
33
|
-
"config": "^3.3.
|
|
34
|
+
"commander": "^10.0.0",
|
|
35
|
+
"config": "^3.3.9",
|
|
34
36
|
"dotenv": "^16.0.3",
|
|
35
|
-
"glob": "8.0
|
|
37
|
+
"glob": "8.1.0",
|
|
36
38
|
"js-yaml": "4.1.0",
|
|
37
39
|
"json2csv": "^5.0.7",
|
|
38
|
-
"jsonwebtoken": "^
|
|
40
|
+
"jsonwebtoken": "^9.0.0",
|
|
39
41
|
"jstransformer-markdown-it": "^3.0.0",
|
|
40
42
|
"jxp-helper": "^1.4.0",
|
|
41
|
-
"mkdirp": "^1.
|
|
42
|
-
"
|
|
43
|
+
"mkdirp": "^2.1.3",
|
|
44
|
+
"moment": "^2.29.4",
|
|
45
|
+
"mongoose": "6.8.4",
|
|
43
46
|
"mongoose-friendly": "^0.1.4",
|
|
44
47
|
"morgan": "^1.10.0",
|
|
45
|
-
"nodemailer": "^6.
|
|
48
|
+
"nodemailer": "^6.9.0",
|
|
46
49
|
"nodemailer-smtp-transport": "^2.7.4",
|
|
47
50
|
"path": "^0.12.7",
|
|
48
51
|
"pug": "^3.0.2",
|
|
49
52
|
"querystring": "^0.2.1",
|
|
50
53
|
"rand-token": "^1.0.1",
|
|
51
54
|
"readline-sync": "^1.4.10",
|
|
52
|
-
"restify": "^
|
|
53
|
-
"restify-cors-
|
|
55
|
+
"restify": "^11.0.0",
|
|
56
|
+
"restify-cors-middleware2": "^2.2.0",
|
|
54
57
|
"restify-errors": "^8.0.2",
|
|
55
58
|
"traverse": "^0.6.7",
|
|
56
59
|
"underscore": "^1.13.6",
|
|
57
|
-
"ws": "^8.
|
|
60
|
+
"ws": "^8.12.0"
|
|
58
61
|
},
|
|
59
62
|
"devDependencies": {
|
|
60
63
|
"chai": "^4.3.7",
|
|
61
64
|
"chai-http": "^4.3.0",
|
|
62
|
-
"mocha": "^10.
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
+
"mocha": "^10.2.0",
|
|
66
|
+
"moment": "^2.29.4",
|
|
67
|
+
"should": "^13.2.3"
|
|
65
68
|
}
|
|
66
69
|
}
|
package/test/test.js
CHANGED
|
@@ -15,11 +15,17 @@ var server = require("../bin/server");
|
|
|
15
15
|
|
|
16
16
|
chai.use(chaiHttp);
|
|
17
17
|
|
|
18
|
+
const pause = ms => new Promise(res => setTimeout(res, ms));
|
|
19
|
+
|
|
18
20
|
describe('Test', () => {
|
|
19
21
|
before(async function() {
|
|
20
22
|
await init.init();
|
|
21
23
|
})
|
|
22
24
|
|
|
25
|
+
beforeEach(async function() {
|
|
26
|
+
await pause(0);
|
|
27
|
+
})
|
|
28
|
+
|
|
23
29
|
|
|
24
30
|
var apikey = null;
|
|
25
31
|
var token = null;
|
|
@@ -438,7 +444,7 @@ describe('Test', () => {
|
|
|
438
444
|
.get(`/api/test/${post_id}?populate=link`)
|
|
439
445
|
.auth(init.email, init.password)
|
|
440
446
|
.end((err, res) => {
|
|
441
|
-
|
|
447
|
+
console.log(res.body);
|
|
442
448
|
res.should.have.status(200);
|
|
443
449
|
res.body.should.have.property("data");
|
|
444
450
|
res.body.data.should.have.property("link")
|
|
@@ -870,7 +876,7 @@ describe('Test', () => {
|
|
|
870
876
|
.del(`/api/test/${post_id}`)
|
|
871
877
|
.auth(init.email, init.password)
|
|
872
878
|
.end((err, res) => {
|
|
873
|
-
console.log(res.body);
|
|
879
|
+
// console.log(res.body);
|
|
874
880
|
res.should.have.status(200);
|
|
875
881
|
res.body.status.should.equal('ok');
|
|
876
882
|
done();
|
|
@@ -881,9 +887,9 @@ describe('Test', () => {
|
|
|
881
887
|
.get(`/api/test/${post_id}`)
|
|
882
888
|
.auth(init.email, init.password)
|
|
883
889
|
.end((err, res) => {
|
|
884
|
-
console.log(res.body);
|
|
890
|
+
// console.log(res.body);
|
|
885
891
|
res.should.have.status(404);
|
|
886
|
-
res.body.message.should.equal(
|
|
892
|
+
res.body.message.should.equal(`Document ${post_id} is deleted on Test`);
|
|
887
893
|
res.body.code.should.equal('NotFound');
|
|
888
894
|
done();
|
|
889
895
|
});
|
|
@@ -986,7 +992,8 @@ describe('Test', () => {
|
|
|
986
992
|
.auth(init.email, init.password)
|
|
987
993
|
.end((err, res) => {
|
|
988
994
|
res.should.have.status(409);
|
|
989
|
-
res.body.message
|
|
995
|
+
// console.log(res.body.message);
|
|
996
|
+
res.body.message.should.equal(`Parent link item exists in test/link_id`);
|
|
990
997
|
done();
|
|
991
998
|
});
|
|
992
999
|
});
|
|
@@ -1007,7 +1014,7 @@ describe('Test', () => {
|
|
|
1007
1014
|
.auth(init.email, init.password)
|
|
1008
1015
|
.end((err, res) => {
|
|
1009
1016
|
res.should.have.status(404);
|
|
1010
|
-
res.body.message.should.equal(
|
|
1017
|
+
res.body.message.should.equal(`Document ${test_with_links_id} is deleted on Test`);
|
|
1011
1018
|
done();
|
|
1012
1019
|
});
|
|
1013
1020
|
});
|
|
@@ -1063,7 +1070,7 @@ describe('Test', () => {
|
|
|
1063
1070
|
.auth(init.email, init.password)
|
|
1064
1071
|
.end((err, res) => {
|
|
1065
1072
|
res.should.have.status(409);
|
|
1066
|
-
res.body.message.should.equal(`
|
|
1073
|
+
res.body.message.should.equal(`Parent link item exists in test/link_id`);
|
|
1067
1074
|
done();
|
|
1068
1075
|
});
|
|
1069
1076
|
});
|
|
@@ -1115,4 +1122,23 @@ describe('Test', () => {
|
|
|
1115
1122
|
});
|
|
1116
1123
|
});
|
|
1117
1124
|
});
|
|
1125
|
+
|
|
1126
|
+
describe("Error Handling", () => {
|
|
1127
|
+
it ("should get an error", (done) => {
|
|
1128
|
+
chai.request(server)
|
|
1129
|
+
.post("/api/test")
|
|
1130
|
+
.auth(init.email, init.password)
|
|
1131
|
+
.send({
|
|
1132
|
+
error: true,
|
|
1133
|
+
bar: "Throw an error"
|
|
1134
|
+
})
|
|
1135
|
+
.end((err, res) => {
|
|
1136
|
+
// console.log(res.body);
|
|
1137
|
+
console.log(res.headers);
|
|
1138
|
+
res.should.have.status(418);
|
|
1139
|
+
res.body.message.should.equal(`I'm a teapot`);
|
|
1140
|
+
done();
|
|
1141
|
+
});
|
|
1142
|
+
})
|
|
1143
|
+
});
|
|
1118
1144
|
});
|