jxp 4.0.0 → 4.1.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/.env.sample +12 -0
- package/dist/libs/bulkwrite_guard.d.ts +1 -0
- package/dist/libs/bulkwrite_guard.d.ts.map +1 -1
- package/dist/libs/bulkwrite_guard.js +43 -0
- package/dist/libs/bulkwrite_guard.js.map +1 -1
- package/dist/libs/docs-auth.d.ts +25 -0
- package/dist/libs/docs-auth.d.ts.map +1 -0
- package/dist/libs/docs-auth.js +205 -0
- package/dist/libs/docs-auth.js.map +1 -0
- package/dist/libs/docs.js +26 -5
- package/dist/libs/docs.js.map +1 -1
- package/dist/libs/jxp.d.ts.map +1 -1
- package/dist/libs/jxp.js +118 -53
- package/dist/libs/jxp.js.map +1 -1
- package/dist/libs/load-config.d.ts.map +1 -1
- package/dist/libs/load-config.js +11 -0
- package/dist/libs/load-config.js.map +1 -1
- package/dist/libs/login_rate_limit.d.ts +16 -0
- package/dist/libs/login_rate_limit.d.ts.map +1 -0
- package/dist/libs/login_rate_limit.js +49 -0
- package/dist/libs/login_rate_limit.js.map +1 -0
- package/dist/libs/query_limits.js +9 -3
- package/dist/libs/query_limits.js.map +1 -1
- package/dist/libs/request_log.d.ts +44 -0
- package/dist/libs/request_log.d.ts.map +1 -0
- package/dist/libs/request_log.js +229 -0
- package/dist/libs/request_log.js.map +1 -0
- package/dist/libs/security.js +18 -9
- package/dist/libs/security.js.map +1 -1
- package/dist/models/test_model.js +1 -1
- package/dist/models/test_model.js.map +1 -1
- package/dist/types/jxp-config.d.ts +17 -0
- package/dist/types/jxp-config.d.ts.map +1 -1
- package/docs/bulk_writes.md +9 -2
- package/docs/changelog.md +76 -1
- package/docs/configuration.md +30 -0
- package/package.json +2 -2
- package/templates/assets/api-console.js +19 -1
- package/templates/assets/docs-login.js +66 -0
- package/templates/assets/docs.css +17 -0
- package/templates/index.pug +9 -4
- package/templates/layout.pug +8 -4
- package/templates/login.pug +39 -0
- package/templates/navbar.pug +18 -7
package/dist/libs/jxp.js
CHANGED
|
@@ -8,6 +8,8 @@ const login = require("./login");
|
|
|
8
8
|
const groups = require("./groups");
|
|
9
9
|
const setup = require("./setup");
|
|
10
10
|
const Docs = require("./docs");
|
|
11
|
+
const docsAuth = require("./docs-auth");
|
|
12
|
+
const loginRateLimit = require("./login_rate_limit");
|
|
11
13
|
const querystring = require("node:querystring");
|
|
12
14
|
const fs = require("fs");
|
|
13
15
|
const morgan = require("morgan");
|
|
@@ -25,6 +27,7 @@ const call_guard = require("./call_guard");
|
|
|
25
27
|
const response_sanitize = require("./response_sanitize");
|
|
26
28
|
const link_index = require("./link_index");
|
|
27
29
|
const { safeErrorMessage } = require("./safe_error");
|
|
30
|
+
const { logRequestError, logAndThrow } = require("./request_log");
|
|
28
31
|
const schemaModule = require("./schema");
|
|
29
32
|
global.JXPSchema = schemaModule.default || schemaModule;
|
|
30
33
|
var models = {};
|
|
@@ -37,10 +40,12 @@ function getStripFields(req) {
|
|
|
37
40
|
function getSecurityOpts(req) {
|
|
38
41
|
return req.config?.security || {};
|
|
39
42
|
}
|
|
40
|
-
function advancedQueryAllowed(Model, kind) {
|
|
43
|
+
function advancedQueryAllowed(Model, kind, options) {
|
|
41
44
|
const opts = Model.schema.opts;
|
|
42
45
|
const aq = opts?.advanced_queries;
|
|
43
46
|
if (kind === "bulkwrite") {
|
|
47
|
+
if (options?.isAdmin)
|
|
48
|
+
return true;
|
|
44
49
|
return aq?.bulkwrite === true;
|
|
45
50
|
}
|
|
46
51
|
if (kind === "aggregate") {
|
|
@@ -48,13 +53,33 @@ function advancedQueryAllowed(Model, kind) {
|
|
|
48
53
|
}
|
|
49
54
|
return aq?.query !== false;
|
|
50
55
|
}
|
|
56
|
+
const middlewareBulkWriteAllowed = (req, res, next) => {
|
|
57
|
+
if (!advancedQueryAllowed(req.Model, "bulkwrite", { isAdmin: !!res.user?.admin })) {
|
|
58
|
+
const err = new errors.ForbiddenError(`POST /bulkwrite is disabled for model ${req.modelname}`);
|
|
59
|
+
logRequestError(req, res, err, "bulkwrite disabled");
|
|
60
|
+
return next(err);
|
|
61
|
+
}
|
|
62
|
+
next();
|
|
63
|
+
};
|
|
64
|
+
const middlewareAdvancedQueryAllowed = (kind) => {
|
|
65
|
+
return (req, res, next) => {
|
|
66
|
+
if (!advancedQueryAllowed(req.Model, kind)) {
|
|
67
|
+
const err = new errors.ForbiddenError(`POST /${kind} is disabled for model ${req.modelname}`);
|
|
68
|
+
logRequestError(req, res, err, `${kind} disabled`);
|
|
69
|
+
return next(err);
|
|
70
|
+
}
|
|
71
|
+
next();
|
|
72
|
+
};
|
|
73
|
+
};
|
|
51
74
|
// Middleware
|
|
52
75
|
const middlewareModel = (req, res, next) => {
|
|
53
76
|
const modelname = req.params.modelname;
|
|
54
77
|
req.modelname = modelname;
|
|
55
78
|
req.Model = models[modelname];
|
|
56
79
|
if (!req.Model) {
|
|
57
|
-
|
|
80
|
+
const err = new errors.NotFoundError(`Model ${modelname} not found`);
|
|
81
|
+
logRequestError(req, res, err, "model");
|
|
82
|
+
return next(err);
|
|
58
83
|
}
|
|
59
84
|
return next();
|
|
60
85
|
};
|
|
@@ -62,7 +87,9 @@ const middlewarePasswords = (req, res, next) => {
|
|
|
62
87
|
if (req.body && req.body.password) {
|
|
63
88
|
if (req.query.password_override) {
|
|
64
89
|
if (!res.user?.admin) {
|
|
65
|
-
|
|
90
|
+
const err = new errors.ForbiddenError("password_override requires admin");
|
|
91
|
+
logRequestError(req, res, err, "password_override");
|
|
92
|
+
return next(err);
|
|
66
93
|
}
|
|
67
94
|
}
|
|
68
95
|
else {
|
|
@@ -94,7 +121,7 @@ const outputJSON = async (req, res) => {
|
|
|
94
121
|
res.send(res.result);
|
|
95
122
|
}
|
|
96
123
|
catch (err) {
|
|
97
|
-
|
|
124
|
+
logRequestError(req, res, err, "outputJSON");
|
|
98
125
|
throw new errors.InternalServerError(safeErrorMessage(err));
|
|
99
126
|
}
|
|
100
127
|
};
|
|
@@ -118,7 +145,7 @@ const outputCSV = (req, res, next) => {
|
|
|
118
145
|
next();
|
|
119
146
|
}
|
|
120
147
|
catch (err) {
|
|
121
|
-
|
|
148
|
+
logRequestError(req, res, err, "outputCSV");
|
|
122
149
|
throw new errors.InternalServerError(safeErrorMessage(err));
|
|
123
150
|
}
|
|
124
151
|
};
|
|
@@ -132,8 +159,7 @@ const actionGet = async (req, res) => {
|
|
|
132
159
|
filters = query_sanitize.sanitizeFilter(filters, getSecurityOpts(req));
|
|
133
160
|
}
|
|
134
161
|
catch (err) {
|
|
135
|
-
|
|
136
|
-
// Preserve BadRequestError, convert others to InternalServerError
|
|
162
|
+
logRequestError(req, res, err, "filter");
|
|
137
163
|
if (err instanceof errors.BadRequestError) {
|
|
138
164
|
throw err;
|
|
139
165
|
}
|
|
@@ -179,7 +205,7 @@ const actionGet = async (req, res) => {
|
|
|
179
205
|
if (count >= 0) {
|
|
180
206
|
result.count = count;
|
|
181
207
|
}
|
|
182
|
-
const effectiveLimit = query_limits.enforceListLimit(req, estimatedCount);
|
|
208
|
+
const effectiveLimit = query_limits.enforceListLimit(req, estimatedCount, res);
|
|
183
209
|
query_limits.applyListPagination(q, result, req, effectiveLimit, count >= 0 ? count : 0, changeUrlParams);
|
|
184
210
|
if (req.query.sort) {
|
|
185
211
|
q.sort(req.query.sort);
|
|
@@ -224,10 +250,12 @@ const actionGet = async (req, res) => {
|
|
|
224
250
|
console.timeEnd(opname);
|
|
225
251
|
}
|
|
226
252
|
catch (err) {
|
|
227
|
-
console.error(new Date(), err);
|
|
228
253
|
if (debug)
|
|
229
254
|
console.timeEnd(opname);
|
|
230
|
-
|
|
255
|
+
if (!(err instanceof errors.BadRequestError) &&
|
|
256
|
+
!(err instanceof errors.ForbiddenError)) {
|
|
257
|
+
logRequestError(req, res, err, "get");
|
|
258
|
+
}
|
|
231
259
|
if (err instanceof errors.BadRequestError) {
|
|
232
260
|
throw err;
|
|
233
261
|
}
|
|
@@ -249,7 +277,7 @@ const actionGetOne = async (req, res) => {
|
|
|
249
277
|
console.timeEnd(opname);
|
|
250
278
|
}
|
|
251
279
|
catch (err) {
|
|
252
|
-
|
|
280
|
+
logRequestError(req, res, err, "getOne");
|
|
253
281
|
if (debug)
|
|
254
282
|
console.timeEnd(opname);
|
|
255
283
|
if (err.code)
|
|
@@ -284,7 +312,7 @@ const actionPost = async (req, res) => {
|
|
|
284
312
|
console.timeEnd(opname);
|
|
285
313
|
}
|
|
286
314
|
catch (err) {
|
|
287
|
-
|
|
315
|
+
logRequestError(req, res, err, "post");
|
|
288
316
|
if (debug)
|
|
289
317
|
console.timeEnd(opname);
|
|
290
318
|
if (err.code)
|
|
@@ -298,8 +326,7 @@ const actionPut = async (req, res) => {
|
|
|
298
326
|
try {
|
|
299
327
|
let item = await req.Model.findById(req.params.item_id);
|
|
300
328
|
if (!item) {
|
|
301
|
-
|
|
302
|
-
throw new errors.NotFoundError(`Document ${req.params.item_id} not found on ${req.modelname}`);
|
|
329
|
+
logAndThrow(req, res, new errors.NotFoundError(`Document ${req.params.item_id} not found on ${req.modelname}`), "put");
|
|
303
330
|
}
|
|
304
331
|
_populateItem(item, datamunging.deserialize(req.body));
|
|
305
332
|
_versionItem(item);
|
|
@@ -324,7 +351,7 @@ const actionPut = async (req, res) => {
|
|
|
324
351
|
console.timeEnd(opname);
|
|
325
352
|
}
|
|
326
353
|
catch (err) {
|
|
327
|
-
|
|
354
|
+
logRequestError(req, res, err, "put");
|
|
328
355
|
if (debug)
|
|
329
356
|
console.timeEnd(opname);
|
|
330
357
|
if (err.code)
|
|
@@ -364,7 +391,7 @@ const actionUpdate = async (req, res) => {
|
|
|
364
391
|
console.timeEnd(opname);
|
|
365
392
|
}
|
|
366
393
|
catch (err) {
|
|
367
|
-
|
|
394
|
+
logRequestError(req, res, err, "update");
|
|
368
395
|
if (debug)
|
|
369
396
|
console.timeEnd(opname);
|
|
370
397
|
if (err.code)
|
|
@@ -398,7 +425,7 @@ const actionDelete = async (req, res) => {
|
|
|
398
425
|
}
|
|
399
426
|
}
|
|
400
427
|
else {
|
|
401
|
-
|
|
428
|
+
logAndThrow(req, res, new errors.ConflictError(`Parent link item exists in ${linked_model.modelname}/${linked_model.field}`), "delete");
|
|
402
429
|
}
|
|
403
430
|
}
|
|
404
431
|
});
|
|
@@ -427,7 +454,9 @@ const actionDelete = async (req, res) => {
|
|
|
427
454
|
console.timeEnd(opname);
|
|
428
455
|
}
|
|
429
456
|
catch (err) {
|
|
430
|
-
|
|
457
|
+
if (!(err instanceof errors.ConflictError)) {
|
|
458
|
+
logRequestError(req, res, err, "delete");
|
|
459
|
+
}
|
|
431
460
|
if (debug)
|
|
432
461
|
console.timeEnd(opname);
|
|
433
462
|
if (err.code)
|
|
@@ -444,7 +473,7 @@ const actionCount = async (req, res) => {
|
|
|
444
473
|
filters = query_sanitize.sanitizeFilter(filters, getSecurityOpts(req));
|
|
445
474
|
}
|
|
446
475
|
catch (err) {
|
|
447
|
-
|
|
476
|
+
logRequestError(req, res, err, "filter");
|
|
448
477
|
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
449
478
|
throw err;
|
|
450
479
|
}
|
|
@@ -464,7 +493,7 @@ const actionCount = async (req, res) => {
|
|
|
464
493
|
console.timeEnd(opname);
|
|
465
494
|
}
|
|
466
495
|
catch (err) {
|
|
467
|
-
|
|
496
|
+
logRequestError(req, res, err, "count");
|
|
468
497
|
if (debug)
|
|
469
498
|
console.timeEnd(opname);
|
|
470
499
|
if (err.code)
|
|
@@ -481,7 +510,7 @@ const actionCall = async (req, res) => {
|
|
|
481
510
|
res.json(result);
|
|
482
511
|
}
|
|
483
512
|
catch (err) {
|
|
484
|
-
|
|
513
|
+
logRequestError(req, res, err, "call");
|
|
485
514
|
if (err.code)
|
|
486
515
|
throw err;
|
|
487
516
|
if (err instanceof errors.ForbiddenError || err instanceof errors.NotFoundError) {
|
|
@@ -506,7 +535,7 @@ const actionCallItem = async (req, res) => {
|
|
|
506
535
|
res.json(result);
|
|
507
536
|
}
|
|
508
537
|
catch (err) {
|
|
509
|
-
|
|
538
|
+
logRequestError(req, res, err, "call");
|
|
510
539
|
if (err.code)
|
|
511
540
|
throw err;
|
|
512
541
|
if (err instanceof errors.ForbiddenError || err instanceof errors.NotFoundError) {
|
|
@@ -517,15 +546,22 @@ const actionCallItem = async (req, res) => {
|
|
|
517
546
|
};
|
|
518
547
|
// Actions (verbs)
|
|
519
548
|
const actionQuery = async (req, res) => {
|
|
520
|
-
if (!advancedQueryAllowed(req.Model, "query")) {
|
|
521
|
-
throw new errors.ForbiddenError(`POST /query is disabled for model ${req.modelname}`);
|
|
522
|
-
}
|
|
523
549
|
if (!req.body || !req.body.query || typeof req.body.query !== "object") {
|
|
524
|
-
|
|
550
|
+
logAndThrow(req, res, new errors.BadRequestError("Query missing or not of type object"), "query");
|
|
525
551
|
}
|
|
526
552
|
const opname = `query ${req.modelname} ${ops++}`;
|
|
527
553
|
console.time(opname);
|
|
528
|
-
|
|
554
|
+
let sanitizedQuery;
|
|
555
|
+
try {
|
|
556
|
+
sanitizedQuery = query_sanitize.sanitizeFilter(req.body.query, getSecurityOpts(req));
|
|
557
|
+
}
|
|
558
|
+
catch (err) {
|
|
559
|
+
logRequestError(req, res, err, "query_sanitize");
|
|
560
|
+
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
561
|
+
throw err;
|
|
562
|
+
}
|
|
563
|
+
throw new errors.InternalServerError(safeErrorMessage(err));
|
|
564
|
+
}
|
|
529
565
|
let query = [sanitizedQuery];
|
|
530
566
|
let checkDeleted = { "$or": [{ _deleted: false }, { _deleted: null }] };
|
|
531
567
|
if (!req.query.showDeleted) {
|
|
@@ -543,7 +579,7 @@ const actionQuery = async (req, res) => {
|
|
|
543
579
|
result.count = count;
|
|
544
580
|
}
|
|
545
581
|
const estimatedCount = await req.Model.estimatedDocumentCount();
|
|
546
|
-
const effectiveLimit = query_limits.enforceListLimit(req, estimatedCount);
|
|
582
|
+
const effectiveLimit = query_limits.enforceListLimit(req, estimatedCount, res);
|
|
547
583
|
query_limits.applyListPagination(q, result, req, effectiveLimit, count >= 0 ? count : 0, changeUrlParams);
|
|
548
584
|
if (req.query.sort) {
|
|
549
585
|
q.sort(req.query.sort);
|
|
@@ -585,9 +621,12 @@ const actionQuery = async (req, res) => {
|
|
|
585
621
|
res.json(result);
|
|
586
622
|
}
|
|
587
623
|
catch (err) {
|
|
588
|
-
console.error(new Date(), err);
|
|
589
624
|
if (debug)
|
|
590
625
|
console.timeEnd(opname);
|
|
626
|
+
if (!(err instanceof errors.BadRequestError) &&
|
|
627
|
+
!(err instanceof errors.ForbiddenError)) {
|
|
628
|
+
logRequestError(req, res, err, "query");
|
|
629
|
+
}
|
|
591
630
|
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
592
631
|
throw err;
|
|
593
632
|
}
|
|
@@ -598,19 +637,24 @@ const actionQuery = async (req, res) => {
|
|
|
598
637
|
};
|
|
599
638
|
// Actions (verbs)
|
|
600
639
|
const actionAggregate = async (req, res) => {
|
|
601
|
-
|
|
602
|
-
throw new errors.ForbiddenError(`POST /aggregate is disabled for model ${req.modelname}`);
|
|
603
|
-
}
|
|
604
|
-
let query = (req.body.query) ? req.body.query : req.body; // Don't require to embed in query anymore
|
|
640
|
+
let query = req.body?.query ? req.body.query : req.body;
|
|
605
641
|
if (!query || !Array.isArray(query)) {
|
|
606
|
-
|
|
607
|
-
throw new errors.BadRequestError("Query missing or not of type array");
|
|
642
|
+
logAndThrow(req, res, new errors.BadRequestError("Query missing or not of type array"), "aggregate");
|
|
608
643
|
}
|
|
609
644
|
query = query_manipulation.fix_query(query);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
645
|
+
try {
|
|
646
|
+
aggregate_guard.validatePipeline(query, {
|
|
647
|
+
aggregate_stages_allow: getSecurityOpts(req).aggregate_stages_allow,
|
|
648
|
+
isAdmin: res.user?.admin,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
catch (err) {
|
|
652
|
+
logRequestError(req, res, err, "aggregate_guard");
|
|
653
|
+
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
654
|
+
throw err;
|
|
655
|
+
}
|
|
656
|
+
throw new errors.InternalServerError(safeErrorMessage(err));
|
|
657
|
+
}
|
|
614
658
|
const opname = `aggregate ${req.modelname} ${ops++}`;
|
|
615
659
|
console.time(opname);
|
|
616
660
|
try {
|
|
@@ -628,9 +672,12 @@ const actionAggregate = async (req, res) => {
|
|
|
628
672
|
res.json(result);
|
|
629
673
|
}
|
|
630
674
|
catch (err) {
|
|
631
|
-
console.error(new Date(), err);
|
|
632
675
|
if (debug)
|
|
633
676
|
console.timeEnd(opname);
|
|
677
|
+
if (!(err instanceof errors.BadRequestError) &&
|
|
678
|
+
!(err instanceof errors.ForbiddenError)) {
|
|
679
|
+
logRequestError(req, res, err, "aggregate");
|
|
680
|
+
}
|
|
634
681
|
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
635
682
|
throw err;
|
|
636
683
|
}
|
|
@@ -639,12 +686,10 @@ const actionAggregate = async (req, res) => {
|
|
|
639
686
|
};
|
|
640
687
|
// Actions (verbs)
|
|
641
688
|
const actionBulkWrite = async (req, res) => {
|
|
642
|
-
if (!advancedQueryAllowed(req.Model, "bulkwrite")) {
|
|
643
|
-
throw new errors.ForbiddenError(`POST /bulkwrite is disabled for model ${req.modelname}`);
|
|
644
|
-
}
|
|
645
689
|
if (!req.body || !Array.isArray(req.body)) {
|
|
646
|
-
|
|
647
|
-
|
|
690
|
+
const err = new errors.BadRequestError("Query missing or not of type array");
|
|
691
|
+
logRequestError(req, res, err, "bulkwrite");
|
|
692
|
+
throw err;
|
|
648
693
|
}
|
|
649
694
|
const opname = `bulkwrite ${req.modelname} ${ops++}`;
|
|
650
695
|
console.time(opname);
|
|
@@ -662,7 +707,7 @@ const actionBulkWrite = async (req, res) => {
|
|
|
662
707
|
res.json(result);
|
|
663
708
|
}
|
|
664
709
|
catch (err) {
|
|
665
|
-
|
|
710
|
+
logRequestError(req, res, err, "bulkwrite");
|
|
666
711
|
if (debug)
|
|
667
712
|
console.timeEnd(opname);
|
|
668
713
|
if (err instanceof errors.BadRequestError || err instanceof errors.ForbiddenError) {
|
|
@@ -734,7 +779,6 @@ const getOne = async (Model, item_id, params, options) => {
|
|
|
734
779
|
return response_sanitize.sanitizeDocument(item);
|
|
735
780
|
}
|
|
736
781
|
catch (err) {
|
|
737
|
-
console.error(err);
|
|
738
782
|
if (err.code)
|
|
739
783
|
throw err;
|
|
740
784
|
throw new errors.InternalServerError(safeErrorMessage(err));
|
|
@@ -1053,6 +1097,10 @@ const JXP = function (options) {
|
|
|
1053
1097
|
ws.init({ models });
|
|
1054
1098
|
cache.init(config);
|
|
1055
1099
|
const docs = new Docs({ config, models });
|
|
1100
|
+
docsAuth.init(config);
|
|
1101
|
+
docsAuth.logDocsAccessMode(config);
|
|
1102
|
+
const loginThrottle = loginRateLimit.createLoginThrottle(config);
|
|
1103
|
+
loginRateLimit.logLoginRateLimit(config);
|
|
1056
1104
|
// Set up our API server
|
|
1057
1105
|
// Rate limitting
|
|
1058
1106
|
if (config.throttle) {
|
|
@@ -1108,9 +1156,9 @@ const JXP = function (options) {
|
|
|
1108
1156
|
// CSV endpoints
|
|
1109
1157
|
server.get("/csv/:modelname", middlewareModel, security.login, security.auth, config.pre_hooks.get, actionGet, outputCSV);
|
|
1110
1158
|
// Query endpoints
|
|
1111
|
-
server.post("/query/:modelname", middlewareModel, security.login, security.auth, config.pre_hooks.get, actionQuery);
|
|
1112
|
-
server.post("/aggregate/:modelname", middlewareModel, security.login, security.auth, config.pre_hooks.get, actionAggregate);
|
|
1113
|
-
server.post("/bulkwrite/:modelname", middlewareModel, security.login, security.bulkAuth, config.pre_hooks.get, actionBulkWrite);
|
|
1159
|
+
server.post("/query/:modelname", middlewareModel, security.login, security.auth, middlewareAdvancedQueryAllowed("query"), config.pre_hooks.get, actionQuery);
|
|
1160
|
+
server.post("/aggregate/:modelname", middlewareModel, security.login, security.auth, middlewareAdvancedQueryAllowed("aggregate"), config.pre_hooks.get, actionAggregate);
|
|
1161
|
+
server.post("/bulkwrite/:modelname", middlewareModel, security.login, middlewareBulkWriteAllowed, security.bulkAuth, config.pre_hooks.get, actionBulkWrite);
|
|
1114
1162
|
server.post("/update/:modelname/:item_id", middlewareModel, security.login, security.auth, middlewarePasswords, middlewareCheckAdmin, config.pre_hooks.update, actionUpdate, cache.clearAll);
|
|
1115
1163
|
/* Batch routes - ROLLED BACK FOR NOW */
|
|
1116
1164
|
// server.post('/batch/create/:modelname', middlewareModel, security.login, security.auth, actionBatch);
|
|
@@ -1125,7 +1173,14 @@ const JXP = function (options) {
|
|
|
1125
1173
|
server.get("/logout", security.login, login.logout);
|
|
1126
1174
|
server.get("/login/oauth/:provider", login.oauth);
|
|
1127
1175
|
server.get("/login/oauth/callback/:provider", login.oauth_callback);
|
|
1128
|
-
|
|
1176
|
+
const loginChain = [
|
|
1177
|
+
...(loginThrottle ? [loginThrottle] : []),
|
|
1178
|
+
config.pre_hooks.login,
|
|
1179
|
+
login.login,
|
|
1180
|
+
config.post_hooks.login,
|
|
1181
|
+
outputJSON,
|
|
1182
|
+
];
|
|
1183
|
+
server.post("/login", ...loginChain);
|
|
1129
1184
|
server.post("/refresh", security.refresh);
|
|
1130
1185
|
server.post("/login/refresh", security.refresh);
|
|
1131
1186
|
/* Groups */
|
|
@@ -1137,10 +1192,16 @@ const JXP = function (options) {
|
|
|
1137
1192
|
server.get("/model/:modelname", middlewareModel, docs.metaModel.bind(docs));
|
|
1138
1193
|
server.get("/model", docs.metaModels.bind(docs));
|
|
1139
1194
|
// server.get("/docs/_design", docs.dbDiagram.bind(docs));
|
|
1195
|
+
server.get("/docs/login", async (req, res) => {
|
|
1196
|
+
await docsAuth.loginPage(req, res, docs.renderLogin.bind(docs));
|
|
1197
|
+
});
|
|
1198
|
+
server.post("/docs/session", ...(loginThrottle ? [loginThrottle] : []), docsAuth.establishSession);
|
|
1199
|
+
server.get("/docs/session", docsAuth.getSession);
|
|
1200
|
+
server.post("/docs/logout", docsAuth.logout);
|
|
1140
1201
|
server.get("/docs/assets/:file", docs.serveAsset.bind(docs));
|
|
1141
|
-
server.get("/docs/api", docs.apiIndex.bind(docs));
|
|
1202
|
+
server.get("/docs/api", docsAuth.docsAccessMiddleware, docs.apiIndex.bind(docs));
|
|
1142
1203
|
server.get("/docs/md/:md_doc", docs.md.bind(docs));
|
|
1143
|
-
server.get("/docs/model/:modelname", docs.model.bind(docs));
|
|
1204
|
+
server.get("/docs/model/:modelname", docsAuth.docsAccessMiddleware, docs.model.bind(docs));
|
|
1144
1205
|
server.get("/", docs.frontPage.bind(docs));
|
|
1145
1206
|
/* Setup */
|
|
1146
1207
|
server.get("/setup", setup.checkUserDoesNotExist, setup.setup);
|
|
@@ -1148,6 +1209,10 @@ const JXP = function (options) {
|
|
|
1148
1209
|
server.post("/setup/data", setup.checkUserDoesNotExist, setup.data_setup);
|
|
1149
1210
|
/* Websocket */
|
|
1150
1211
|
server.on("upgrade", ws.upgrade);
|
|
1212
|
+
server.on("restError", (req, res, err, callback) => {
|
|
1213
|
+
logRequestError(req, res, err);
|
|
1214
|
+
return callback();
|
|
1215
|
+
});
|
|
1151
1216
|
/* Cache */
|
|
1152
1217
|
server.get("/cache/stats", security.login, security.admin_only, cache.stats, outputJSON);
|
|
1153
1218
|
server.get("/cache/clear", security.login, security.admin_only, cache.clearAll, outputJSON);
|