@saltcorn/server 0.9.0-beta.10 → 0.9.0-beta.11
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/locales/en.json +3 -1
- package/package.json +8 -8
- package/public/saltcorn-common.js +10 -4
- package/routes/actions.js +12 -4
- package/routes/page.js +1 -0
- package/routes/pageedit.js +9 -2
- package/routes/viewedit.js +2 -2
- package/tests/viewedit.test.js +244 -1
- package/wrapper.js +6 -3
package/locales/en.json
CHANGED
|
@@ -1266,5 +1266,7 @@
|
|
|
1266
1266
|
"Initially open": "Initially open",
|
|
1267
1267
|
"Not a valid pack": "Not a valid pack",
|
|
1268
1268
|
"Pack file": "Pack file",
|
|
1269
|
-
"Upload a pack file": "Upload a pack file"
|
|
1269
|
+
"Upload a pack file": "Upload a pack file",
|
|
1270
|
+
"No menu": "No menu",
|
|
1271
|
+
"Omit the menu from this page": "Omit the menu from this page"
|
|
1270
1272
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.11",
|
|
4
4
|
"description": "Server app for Saltcorn, open-source no-code platform",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@saltcorn/base-plugin": "0.9.0-beta.
|
|
10
|
-
"@saltcorn/builder": "0.9.0-beta.
|
|
11
|
-
"@saltcorn/data": "0.9.0-beta.
|
|
12
|
-
"@saltcorn/admin-models": "0.9.0-beta.
|
|
13
|
-
"@saltcorn/filemanager": "0.9.0-beta.
|
|
14
|
-
"@saltcorn/markup": "0.9.0-beta.
|
|
15
|
-
"@saltcorn/sbadmin2": "0.9.0-beta.
|
|
9
|
+
"@saltcorn/base-plugin": "0.9.0-beta.11",
|
|
10
|
+
"@saltcorn/builder": "0.9.0-beta.11",
|
|
11
|
+
"@saltcorn/data": "0.9.0-beta.11",
|
|
12
|
+
"@saltcorn/admin-models": "0.9.0-beta.11",
|
|
13
|
+
"@saltcorn/filemanager": "0.9.0-beta.11",
|
|
14
|
+
"@saltcorn/markup": "0.9.0-beta.11",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.9.0-beta.11",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"adm-zip": "0.5.10",
|
|
@@ -128,10 +128,13 @@ function apply_showif() {
|
|
|
128
128
|
const dynwhere = JSON.parse(
|
|
129
129
|
decodeURIComponent(e.attr("data-fetch-options"))
|
|
130
130
|
);
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
if (window._sc_loglevel > 4) console.log("dynwhere", dynwhere);
|
|
132
|
+
const kvToQs = ([k, v]) => {
|
|
133
|
+
return k === "or" && Array.isArray(v)
|
|
134
|
+
? v.map((v1) => Object.entries(v1).map(kvToQs).join("&")).join("&")
|
|
135
|
+
: `${k}=${v[0] === "$" ? rec[v.substring(1)] : v}`;
|
|
136
|
+
};
|
|
137
|
+
const qss = Object.entries(dynwhere.whereParsed).map(kvToQs);
|
|
135
138
|
if (dynwhere.dereference) {
|
|
136
139
|
if (Array.isArray(dynwhere.dereference))
|
|
137
140
|
qss.push(...dynwhere.dereference.map((d) => `dereference=${d}`));
|
|
@@ -205,6 +208,9 @@ function apply_showif() {
|
|
|
205
208
|
});
|
|
206
209
|
$.ajax(`/api/${dynwhere.table}?${qs}`).then((resp) => {
|
|
207
210
|
if (resp.success) {
|
|
211
|
+
if (window._sc_loglevel > 4)
|
|
212
|
+
console.log("dynwhere fetch", qs, resp.success);
|
|
213
|
+
|
|
208
214
|
activate(resp.success, qs);
|
|
209
215
|
const cacheNow = e.prop("data-fetch-options-cache") || {};
|
|
210
216
|
e.prop("data-fetch-options-cache", {
|
package/routes/actions.js
CHANGED
|
@@ -390,6 +390,16 @@ router.get(
|
|
|
390
390
|
return;
|
|
391
391
|
}
|
|
392
392
|
const action = getState().actions[trigger.action];
|
|
393
|
+
// get table related to trigger
|
|
394
|
+
const table = trigger.table_id
|
|
395
|
+
? Table.findOne({ id: trigger.table_id })
|
|
396
|
+
: null;
|
|
397
|
+
|
|
398
|
+
const subtitle = span(
|
|
399
|
+
{ class: "ms-3" },
|
|
400
|
+
trigger.action,
|
|
401
|
+
table ? ` on ` + a({ href: `/table/${table.name}` }, table.name) : ""
|
|
402
|
+
);
|
|
393
403
|
if (!action) {
|
|
394
404
|
req.flash("warning", req.__("Action not found"));
|
|
395
405
|
res.redirect(`/actions/`);
|
|
@@ -420,6 +430,7 @@ router.get(
|
|
|
420
430
|
type: "card",
|
|
421
431
|
titleAjaxIndicator: true,
|
|
422
432
|
title: req.__("Configure trigger %s", trigger.name),
|
|
433
|
+
subtitle,
|
|
423
434
|
contents: {
|
|
424
435
|
widths: [8, 4],
|
|
425
436
|
besides: [
|
|
@@ -465,10 +476,6 @@ router.get(
|
|
|
465
476
|
req.flash("warning", req.__("Action not configurable"));
|
|
466
477
|
res.redirect(`/actions/`);
|
|
467
478
|
} else {
|
|
468
|
-
// get table related to trigger
|
|
469
|
-
const table = trigger.table_id
|
|
470
|
-
? Table.findOne({ id: trigger.table_id })
|
|
471
|
-
: null;
|
|
472
479
|
// get configuration fields
|
|
473
480
|
const cfgFields = await getActionConfigFields(action, table);
|
|
474
481
|
// create form
|
|
@@ -491,6 +498,7 @@ router.get(
|
|
|
491
498
|
type: "card",
|
|
492
499
|
titleAjaxIndicator: true,
|
|
493
500
|
title: req.__("Configure trigger %s", trigger.name),
|
|
501
|
+
subtitle,
|
|
494
502
|
contents: renderForm(form, req.csrfToken()),
|
|
495
503
|
},
|
|
496
504
|
});
|
package/routes/page.js
CHANGED
package/routes/pageedit.js
CHANGED
|
@@ -92,6 +92,12 @@ const pagePropertiesForm = async (req, isNew) => {
|
|
|
92
92
|
input_type: "select",
|
|
93
93
|
options: roles.map((r) => ({ value: r.id, label: r.role })),
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
name: "no_menu",
|
|
97
|
+
label: req.__("No menu"),
|
|
98
|
+
sublabel: req.__("Omit the menu from this page"),
|
|
99
|
+
type: "Bool",
|
|
100
|
+
},
|
|
95
101
|
],
|
|
96
102
|
});
|
|
97
103
|
return form;
|
|
@@ -315,6 +321,7 @@ router.get(
|
|
|
315
321
|
const form = await pagePropertiesForm(req);
|
|
316
322
|
form.hidden("id");
|
|
317
323
|
form.values = page;
|
|
324
|
+
form.values.no_menu = page.attributes?.no_menu;
|
|
318
325
|
res.sendWrap(
|
|
319
326
|
req.__(`Page attributes`),
|
|
320
327
|
wrap(renderForm(form, req.csrfToken()), false, req, page)
|
|
@@ -360,9 +367,9 @@ router.post(
|
|
|
360
367
|
wrap(renderForm(form, req.csrfToken()), false, req)
|
|
361
368
|
);
|
|
362
369
|
} else {
|
|
363
|
-
const { id, columns, ...pageRow } = form.values;
|
|
370
|
+
const { id, columns, no_menu, ...pageRow } = form.values;
|
|
364
371
|
pageRow.min_role = +pageRow.min_role;
|
|
365
|
-
|
|
372
|
+
pageRow.attributes = { no_menu };
|
|
366
373
|
if (+id) {
|
|
367
374
|
await Page.update(+id, pageRow);
|
|
368
375
|
res.redirect(`/pageedit/`);
|
package/routes/viewedit.js
CHANGED
|
@@ -638,8 +638,8 @@ router.post(
|
|
|
638
638
|
newcfg = {
|
|
639
639
|
...view.configuration,
|
|
640
640
|
[step.contextField]: {
|
|
641
|
-
...view.configuration?.[step.contextField],
|
|
642
|
-
...context,
|
|
641
|
+
...(view.configuration?.[step.contextField] || {}),
|
|
642
|
+
...(context?.[step.contextField] || {}),
|
|
643
643
|
},
|
|
644
644
|
};
|
|
645
645
|
else newcfg = { ...view.configuration, ...context };
|
package/tests/viewedit.test.js
CHANGED
|
@@ -396,7 +396,7 @@ describe("viewedit new Show", () => {
|
|
|
396
396
|
it("delete new view", async () => {
|
|
397
397
|
const loginCookie = await getAdminLoginCookie();
|
|
398
398
|
const app = await getApp({ disableCsrf: true });
|
|
399
|
-
const id =
|
|
399
|
+
const id = View.findOne({ name: "mybook" }).id;
|
|
400
400
|
|
|
401
401
|
await request(app)
|
|
402
402
|
.post("/viewedit/delete/" + id)
|
|
@@ -404,6 +404,249 @@ describe("viewedit new Show", () => {
|
|
|
404
404
|
.expect(toRedirect("/viewedit"));
|
|
405
405
|
});
|
|
406
406
|
});
|
|
407
|
+
|
|
408
|
+
describe("viewedit new Edit", () => {
|
|
409
|
+
// create two edit views for 'books'
|
|
410
|
+
// the first has inputs for all books-fields
|
|
411
|
+
// => 'Fixed and blocked fields (step 2 / max 3)' won't show up
|
|
412
|
+
// the second has no input for 'pages'
|
|
413
|
+
// => 'Fixed and blocked fields (step 2 / max 3)' shows up
|
|
414
|
+
// and the configration gets a fixed property
|
|
415
|
+
const colsWithoutPages = [
|
|
416
|
+
{
|
|
417
|
+
type: "Field",
|
|
418
|
+
fieldview: "edit",
|
|
419
|
+
field_name: "author",
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: "Field",
|
|
423
|
+
fieldview: "select",
|
|
424
|
+
field_name: "publisher",
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
type: "Action",
|
|
428
|
+
rndid: "f61f38",
|
|
429
|
+
minRole: 100,
|
|
430
|
+
action_name: "Save",
|
|
431
|
+
action_style: "btn-primary",
|
|
432
|
+
},
|
|
433
|
+
];
|
|
434
|
+
const colsWithPages = [
|
|
435
|
+
{
|
|
436
|
+
type: "Field",
|
|
437
|
+
fieldview: "edit",
|
|
438
|
+
field_name: "pages",
|
|
439
|
+
},
|
|
440
|
+
...colsWithoutPages,
|
|
441
|
+
];
|
|
442
|
+
|
|
443
|
+
const layoutWithoutPages = {
|
|
444
|
+
above: [
|
|
445
|
+
{
|
|
446
|
+
type: "field",
|
|
447
|
+
fieldview: "edit",
|
|
448
|
+
field_name: "author",
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
{
|
|
452
|
+
type: "field",
|
|
453
|
+
fieldview: "select",
|
|
454
|
+
field_name: "publisher",
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
type: "action",
|
|
458
|
+
rndid: "f61f38",
|
|
459
|
+
minRole: 100,
|
|
460
|
+
action_name: "Save",
|
|
461
|
+
action_style: "btn-primary",
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
};
|
|
465
|
+
const layoutWithPages = {
|
|
466
|
+
above: [
|
|
467
|
+
{
|
|
468
|
+
type: "field",
|
|
469
|
+
fieldview: "edit",
|
|
470
|
+
field_name: "pages",
|
|
471
|
+
},
|
|
472
|
+
...layoutWithoutPages.above,
|
|
473
|
+
],
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
it("submits new view", async () => {
|
|
477
|
+
const loginCookie = await getAdminLoginCookie();
|
|
478
|
+
const app = await getApp({ disableCsrf: true });
|
|
479
|
+
// edit_mybook
|
|
480
|
+
await request(app)
|
|
481
|
+
.post("/viewedit/save")
|
|
482
|
+
.send("viewtemplate=Edit")
|
|
483
|
+
.send("table_name=books")
|
|
484
|
+
.send("name=edit_mybook")
|
|
485
|
+
.send("min_role=100")
|
|
486
|
+
.set("Cookie", loginCookie)
|
|
487
|
+
.expect(toRedirect("/viewedit/config/edit_mybook"));
|
|
488
|
+
// edit_mybook_without_pages
|
|
489
|
+
await request(app)
|
|
490
|
+
.post("/viewedit/save")
|
|
491
|
+
.send("viewtemplate=Edit")
|
|
492
|
+
.send("table_name=books")
|
|
493
|
+
.send("name=edit_mybook_without_pages")
|
|
494
|
+
.send("min_role=100")
|
|
495
|
+
.set("Cookie", loginCookie)
|
|
496
|
+
.expect(toRedirect("/viewedit/config/edit_mybook_without_pages"));
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("saves new view layout", async () => {
|
|
500
|
+
const loginCookie = await getAdminLoginCookie();
|
|
501
|
+
const table = Table.findOne({ name: "books" });
|
|
502
|
+
const app = await getApp({ disableCsrf: true });
|
|
503
|
+
// edit_mybook
|
|
504
|
+
await request(app)
|
|
505
|
+
.post("/viewedit/config/edit_mybook")
|
|
506
|
+
.send(
|
|
507
|
+
"contextEnc=" +
|
|
508
|
+
encodeURIComponent(
|
|
509
|
+
JSON.stringify({
|
|
510
|
+
table_id: table.id,
|
|
511
|
+
viewname: "edit_mybook",
|
|
512
|
+
})
|
|
513
|
+
)
|
|
514
|
+
)
|
|
515
|
+
.send("stepName=Layout")
|
|
516
|
+
.send("columns=" + encodeURIComponent(JSON.stringify(colsWithPages)))
|
|
517
|
+
.send("layout=" + encodeURIComponent(JSON.stringify(layoutWithPages)))
|
|
518
|
+
.set("Cookie", loginCookie)
|
|
519
|
+
.expect(toInclude("Edit options (step 3 / 3)"));
|
|
520
|
+
// edit_mybook_without_pages
|
|
521
|
+
await request(app)
|
|
522
|
+
.post("/viewedit/config/edit_mybook_without_pages")
|
|
523
|
+
.send(
|
|
524
|
+
"contextEnc=" +
|
|
525
|
+
encodeURIComponent(
|
|
526
|
+
JSON.stringify({
|
|
527
|
+
table_id: table.id,
|
|
528
|
+
viewname: "edit_mybook_without_pages",
|
|
529
|
+
})
|
|
530
|
+
)
|
|
531
|
+
)
|
|
532
|
+
.send("stepName=Layout")
|
|
533
|
+
.send("columns=" + encodeURIComponent(JSON.stringify(colsWithoutPages)))
|
|
534
|
+
.send("layout=" + encodeURIComponent(JSON.stringify(layoutWithoutPages)))
|
|
535
|
+
.set("Cookie", loginCookie)
|
|
536
|
+
.expect(toInclude("Fixed and blocked fields (step 2 / max 3)"));
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it("saves new view fixed fields", async () => {
|
|
540
|
+
const loginCookie = await getAdminLoginCookie();
|
|
541
|
+
const table = Table.findOne({ name: "books" });
|
|
542
|
+
const app = await getApp({ disableCsrf: true });
|
|
543
|
+
// only edit_mybook_without_pages
|
|
544
|
+
await request(app)
|
|
545
|
+
.post("/viewedit/config/edit_mybook_without_pages")
|
|
546
|
+
.send(
|
|
547
|
+
"contextEnc=" +
|
|
548
|
+
encodeURIComponent(
|
|
549
|
+
JSON.stringify({
|
|
550
|
+
table_id: table.id,
|
|
551
|
+
viewname: "edit_mybook_without_pages",
|
|
552
|
+
layout: layoutWithoutPages,
|
|
553
|
+
columns: colsWithoutPages,
|
|
554
|
+
})
|
|
555
|
+
)
|
|
556
|
+
)
|
|
557
|
+
.send("stepName=Fixed+and+blocked+fields")
|
|
558
|
+
.send("pages=2")
|
|
559
|
+
.set("Cookie", loginCookie)
|
|
560
|
+
.expect(toInclude("Edit options (step 3 / 3)"));
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("saves view when done", async () => {
|
|
564
|
+
const loginCookie = await getAdminLoginCookie();
|
|
565
|
+
const table = Table.findOne({ name: "books" });
|
|
566
|
+
const app = await getApp({ disableCsrf: true });
|
|
567
|
+
// edit_mybook
|
|
568
|
+
await request(app)
|
|
569
|
+
.post("/viewedit/config/edit_mybook")
|
|
570
|
+
.send(
|
|
571
|
+
"contextEnc=" +
|
|
572
|
+
encodeURIComponent(
|
|
573
|
+
JSON.stringify({
|
|
574
|
+
table_id: table.id,
|
|
575
|
+
viewname: "edit_mybook",
|
|
576
|
+
layout: layoutWithPages,
|
|
577
|
+
columns: colsWithPages,
|
|
578
|
+
})
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
.send("stepName=Edit+options")
|
|
582
|
+
.send("destination_type=View")
|
|
583
|
+
.send("view_when_done=authorlist")
|
|
584
|
+
.send("auto_save=on")
|
|
585
|
+
.send("split_paste=on")
|
|
586
|
+
.set("Cookie", loginCookie)
|
|
587
|
+
.expect(toRedirect("/viewedit"));
|
|
588
|
+
const viewWithPages = View.findOne({ name: "edit_mybook" });
|
|
589
|
+
expect(viewWithPages.configuration.layout).toEqual(layoutWithPages);
|
|
590
|
+
expect(viewWithPages.configuration.columns).toEqual(colsWithPages);
|
|
591
|
+
// edit_mybook_without_pages
|
|
592
|
+
await request(app)
|
|
593
|
+
.post("/viewedit/config/edit_mybook_without_pages")
|
|
594
|
+
.send(
|
|
595
|
+
"contextEnc=" +
|
|
596
|
+
encodeURIComponent(
|
|
597
|
+
JSON.stringify({
|
|
598
|
+
table_id: table.id,
|
|
599
|
+
viewname: "edit_mybook_without_pages",
|
|
600
|
+
layout: layoutWithoutPages,
|
|
601
|
+
columns: colsWithoutPages,
|
|
602
|
+
fixed: {
|
|
603
|
+
pages: 22,
|
|
604
|
+
_block_pages: false,
|
|
605
|
+
},
|
|
606
|
+
})
|
|
607
|
+
)
|
|
608
|
+
)
|
|
609
|
+
.send("stepName=Edit+options")
|
|
610
|
+
.send("destination_type=View")
|
|
611
|
+
.send("view_when_done=authorlist")
|
|
612
|
+
.send("auto_save=on")
|
|
613
|
+
.send("split_paste=on")
|
|
614
|
+
.set("Cookie", loginCookie)
|
|
615
|
+
.expect(toRedirect("/viewedit"));
|
|
616
|
+
|
|
617
|
+
const viewWithoutPages = View.findOne({
|
|
618
|
+
name: "edit_mybook_without_pages",
|
|
619
|
+
});
|
|
620
|
+
expect(viewWithoutPages.configuration.fixed).toEqual({
|
|
621
|
+
pages: 22,
|
|
622
|
+
_block_pages: false,
|
|
623
|
+
});
|
|
624
|
+
expect(viewWithoutPages.configuration.layout).toEqual(layoutWithoutPages);
|
|
625
|
+
expect(viewWithoutPages.configuration.columns).toEqual(colsWithoutPages);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it("deletes new view", async () => {
|
|
629
|
+
const loginCookie = await getAdminLoginCookie();
|
|
630
|
+
const app = await getApp({ disableCsrf: true });
|
|
631
|
+
const idA = View.findOne({ name: "edit_mybook" }).id;
|
|
632
|
+
// edit_mybook
|
|
633
|
+
await request(app)
|
|
634
|
+
.post("/viewedit/delete/" + idA)
|
|
635
|
+
.set("Cookie", loginCookie)
|
|
636
|
+
.expect(toRedirect("/viewedit"));
|
|
637
|
+
// edit_mybook_without_pages
|
|
638
|
+
const idB = View.findOne({ name: "edit_mybook_without_pages" }).id;
|
|
639
|
+
await request(app)
|
|
640
|
+
.post("/viewedit/delete/" + idB)
|
|
641
|
+
.set("Cookie", loginCookie)
|
|
642
|
+
.expect(toRedirect("/viewedit"));
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
describe("viewedit new Edit with fixed fields", () => {
|
|
647
|
+
it("submit new view", async () => {});
|
|
648
|
+
});
|
|
649
|
+
|
|
407
650
|
describe("Library", () => {
|
|
408
651
|
it("should save new from builder", async () => {
|
|
409
652
|
const loginCookie = await getAdminLoginCookie();
|
package/wrapper.js
CHANGED
|
@@ -187,7 +187,9 @@ const get_headers = (req, version_tag, description, extras = []) => {
|
|
|
187
187
|
: [];
|
|
188
188
|
const stdHeaders = [
|
|
189
189
|
{
|
|
190
|
-
headerTag: `<script>var
|
|
190
|
+
headerTag: `<script>var _sc_loglevel = ${
|
|
191
|
+
state.logLevel
|
|
192
|
+
}, _sc_globalCsrf = "${req.csrfToken()}", _sc_version_tag = "${version_tag}";</script>`,
|
|
191
193
|
},
|
|
192
194
|
{ css: `/static_assets/${version_tag}/saltcorn.css` },
|
|
193
195
|
{ script: `/static_assets/${version_tag}/saltcorn-common.js` },
|
|
@@ -309,6 +311,7 @@ module.exports = (version_tag) =>
|
|
|
309
311
|
const alerts = getFlashes(req);
|
|
310
312
|
const state = getState();
|
|
311
313
|
const layout = state.getLayout(req.user);
|
|
314
|
+
const no_menu = opts.no_menu;
|
|
312
315
|
|
|
313
316
|
if (req.xhr) {
|
|
314
317
|
const renderToHtml = layout.renderBody
|
|
@@ -335,8 +338,8 @@ module.exports = (version_tag) =>
|
|
|
335
338
|
res.send(
|
|
336
339
|
layout.wrap({
|
|
337
340
|
title,
|
|
338
|
-
brand: get_brand(state),
|
|
339
|
-
menu: get_menu(req),
|
|
341
|
+
brand: no_menu ? undefined : get_brand(state),
|
|
342
|
+
menu: no_menu ? undefined : get_menu(req),
|
|
340
343
|
currentUrl,
|
|
341
344
|
originalUrl: req.originalUrl,
|
|
342
345
|
|