@saltcorn/server 0.7.3-beta.6 → 0.7.4-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/admin.js +9 -5
- package/auth/routes.js +19 -9
- package/errors.js +51 -48
- package/locales/en.json +12 -1
- package/locales/zh.json +187 -187
- package/markup/admin.js +4 -3
- package/package.json +7 -7
- package/public/jquery-menu-editor.min.js +11 -1
- package/public/saltcorn-common.js +26 -10
- package/public/saltcorn.css +17 -0
- package/public/saltcorn.js +5 -1
- package/routes/admin.js +296 -23
- package/routes/api.js +9 -1
- package/routes/eventlog.js +24 -22
- package/routes/files.js +5 -5
- package/routes/infoarch.js +6 -3
- package/routes/page.js +5 -1
- package/routes/pageedit.js +9 -1
- package/routes/plugins.js +77 -11
- package/routes/search.js +4 -2
- package/routes/tables.js +4 -3
- package/routes/tenant.js +4 -2
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +16 -3
- package/serve.js +54 -38
- package/tests/api.test.js +17 -0
- package/tests/clientjs.test.js +11 -1
package/routes/admin.js
CHANGED
|
@@ -18,7 +18,13 @@ const { spawn } = require("child_process");
|
|
|
18
18
|
const User = require("@saltcorn/data/models/user");
|
|
19
19
|
const path = require("path");
|
|
20
20
|
const { getAllTenants } = require("@saltcorn/admin-models/models/tenant");
|
|
21
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
post_btn,
|
|
23
|
+
renderForm,
|
|
24
|
+
mkTable,
|
|
25
|
+
link,
|
|
26
|
+
localeDateTime,
|
|
27
|
+
} = require("@saltcorn/markup");
|
|
22
28
|
const {
|
|
23
29
|
div,
|
|
24
30
|
a,
|
|
@@ -42,10 +48,11 @@ const {
|
|
|
42
48
|
select,
|
|
43
49
|
option,
|
|
44
50
|
fieldset,
|
|
45
|
-
legend,
|
|
46
51
|
ul,
|
|
47
52
|
li,
|
|
48
53
|
ol,
|
|
54
|
+
script,
|
|
55
|
+
domReady,
|
|
49
56
|
} = require("@saltcorn/markup/tags");
|
|
50
57
|
const db = require("@saltcorn/data/db");
|
|
51
58
|
const {
|
|
@@ -61,6 +68,7 @@ const {
|
|
|
61
68
|
restore,
|
|
62
69
|
auto_backup_now,
|
|
63
70
|
} = require("@saltcorn/admin-models/models/backup");
|
|
71
|
+
const Snapshot = require("@saltcorn/admin-models/models/snapshot");
|
|
64
72
|
const {
|
|
65
73
|
runConfigurationCheck,
|
|
66
74
|
} = require("@saltcorn/admin-models/models/config-check");
|
|
@@ -86,6 +94,8 @@ const {
|
|
|
86
94
|
const moment = require("moment");
|
|
87
95
|
const View = require("@saltcorn/data/models/view");
|
|
88
96
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
97
|
+
const os = require("os");
|
|
98
|
+
const Page = require("@saltcorn/data/models/page");
|
|
89
99
|
|
|
90
100
|
/**
|
|
91
101
|
* @type {object}
|
|
@@ -115,6 +125,7 @@ const site_id_form = (req) =>
|
|
|
115
125
|
"page_custom_html",
|
|
116
126
|
"development_mode",
|
|
117
127
|
"log_sql",
|
|
128
|
+
"log_level",
|
|
118
129
|
"plugins_store_endpoint",
|
|
119
130
|
"packs_store_endpoint",
|
|
120
131
|
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
@@ -140,10 +151,6 @@ const email_form = async (req) => {
|
|
|
140
151
|
],
|
|
141
152
|
action: "/admin/email",
|
|
142
153
|
});
|
|
143
|
-
form.submitButtonClass = "btn-outline-primary";
|
|
144
|
-
form.submitLabel = req.__("Save");
|
|
145
|
-
form.onChange =
|
|
146
|
-
"remove_outline(this);$('#testemail').attr('href','#').removeClass('btn-primary').addClass('btn-outline-primary')";
|
|
147
154
|
return form;
|
|
148
155
|
};
|
|
149
156
|
|
|
@@ -214,8 +221,10 @@ router.post(
|
|
|
214
221
|
flash_restart_if_required(form, req);
|
|
215
222
|
await save_config_from_form(form);
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
224
|
+
if (!req.xhr) {
|
|
225
|
+
req.flash("success", req.__("Site identity settings updated"));
|
|
226
|
+
res.redirect("/admin");
|
|
227
|
+
} else res.json({ success: "ok" });
|
|
219
228
|
}
|
|
220
229
|
})
|
|
221
230
|
);
|
|
@@ -308,7 +317,8 @@ router.post(
|
|
|
308
317
|
} else {
|
|
309
318
|
await save_config_from_form(form);
|
|
310
319
|
req.flash("success", req.__("Email settings updated"));
|
|
311
|
-
res.redirect("/admin/email");
|
|
320
|
+
if (!req.xhr) res.redirect("/admin/email");
|
|
321
|
+
else res.json({ success: "ok" });
|
|
312
322
|
}
|
|
313
323
|
})
|
|
314
324
|
);
|
|
@@ -335,6 +345,9 @@ router.get(
|
|
|
335
345
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
336
346
|
"auto_backup_expire_days"
|
|
337
347
|
);
|
|
348
|
+
const aSnapshotForm = snapshotForm(req);
|
|
349
|
+
aSnapshotForm.values.snapshots_enabled =
|
|
350
|
+
getState().getConfig("snapshots_enabled");
|
|
338
351
|
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
339
352
|
|
|
340
353
|
send_admin_page({
|
|
@@ -378,10 +391,31 @@ router.get(
|
|
|
378
391
|
a(
|
|
379
392
|
{ href: "/admin/auto-backup-list" },
|
|
380
393
|
"Restore/download automated backups »"
|
|
394
|
+
),
|
|
395
|
+
script(
|
|
396
|
+
domReady(
|
|
397
|
+
`$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
|
|
398
|
+
)
|
|
381
399
|
)
|
|
382
400
|
),
|
|
383
401
|
}
|
|
384
402
|
: { type: "blank", contents: "" },
|
|
403
|
+
{
|
|
404
|
+
type: "card",
|
|
405
|
+
title: req.__("Snapshots"),
|
|
406
|
+
contents: div(
|
|
407
|
+
p(
|
|
408
|
+
i(
|
|
409
|
+
"Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns)."
|
|
410
|
+
)
|
|
411
|
+
),
|
|
412
|
+
renderForm(aSnapshotForm, req.csrfToken()),
|
|
413
|
+
a(
|
|
414
|
+
{ href: "/admin/snapshot-list" },
|
|
415
|
+
"List/download snapshots »"
|
|
416
|
+
)
|
|
417
|
+
),
|
|
418
|
+
},
|
|
385
419
|
],
|
|
386
420
|
},
|
|
387
421
|
});
|
|
@@ -457,6 +491,103 @@ router.get(
|
|
|
457
491
|
})
|
|
458
492
|
);
|
|
459
493
|
|
|
494
|
+
router.get(
|
|
495
|
+
"/snapshot-list",
|
|
496
|
+
isAdmin,
|
|
497
|
+
error_catcher(async (req, res) => {
|
|
498
|
+
const snaps = await Snapshot.find();
|
|
499
|
+
send_admin_page({
|
|
500
|
+
res,
|
|
501
|
+
req,
|
|
502
|
+
active_sub: "Backup",
|
|
503
|
+
contents: {
|
|
504
|
+
above: [
|
|
505
|
+
{
|
|
506
|
+
type: "card",
|
|
507
|
+
title: req.__("Download snapshots"),
|
|
508
|
+
contents: div(
|
|
509
|
+
ul(
|
|
510
|
+
snaps.map((snap) =>
|
|
511
|
+
li(
|
|
512
|
+
a(
|
|
513
|
+
{
|
|
514
|
+
href: `/admin/snapshot-download/${encodeURIComponent(
|
|
515
|
+
snap.id
|
|
516
|
+
)}`,
|
|
517
|
+
target: "_blank",
|
|
518
|
+
},
|
|
519
|
+
`${localeDateTime(snap.created)} (${moment(
|
|
520
|
+
snap.created
|
|
521
|
+
).fromNow()})`
|
|
522
|
+
)
|
|
523
|
+
)
|
|
524
|
+
)
|
|
525
|
+
)
|
|
526
|
+
),
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
})
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
router.get(
|
|
535
|
+
"/snapshot-download/:id",
|
|
536
|
+
isAdmin,
|
|
537
|
+
error_catcher(async (req, res) => {
|
|
538
|
+
const { id } = req.params;
|
|
539
|
+
const snap = await Snapshot.findOne({ id });
|
|
540
|
+
res.send(snap.pack);
|
|
541
|
+
})
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
router.get(
|
|
545
|
+
"/snapshot-restore/:type/:name",
|
|
546
|
+
isAdmin,
|
|
547
|
+
error_catcher(async (req, res) => {
|
|
548
|
+
const { type, name } = req.params;
|
|
549
|
+
const snaps = await Snapshot.entity_history(type, name);
|
|
550
|
+
res.send(
|
|
551
|
+
mkTable(
|
|
552
|
+
[
|
|
553
|
+
{
|
|
554
|
+
label: "When",
|
|
555
|
+
key: (r) =>
|
|
556
|
+
`${localeDateTime(r.created)} (${moment(r.created).fromNow()})`,
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
{
|
|
560
|
+
label: req.__("Restore"),
|
|
561
|
+
key: (r) =>
|
|
562
|
+
post_btn(
|
|
563
|
+
`/admin/snapshot-restore/${type}/${name}/${r.id}`,
|
|
564
|
+
req.__("Restore"),
|
|
565
|
+
req.csrfToken()
|
|
566
|
+
),
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
snaps
|
|
570
|
+
)
|
|
571
|
+
);
|
|
572
|
+
})
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
router.post(
|
|
576
|
+
"/snapshot-restore/:type/:name/:id",
|
|
577
|
+
isAdmin,
|
|
578
|
+
error_catcher(async (req, res) => {
|
|
579
|
+
const { type, name, id } = req.params;
|
|
580
|
+
const snap = await Snapshot.findOne({ id });
|
|
581
|
+
await snap.restore_entity(type, name);
|
|
582
|
+
req.flash(
|
|
583
|
+
"success",
|
|
584
|
+
`${type} ${name} restored to snapshot saved ${moment(
|
|
585
|
+
snap.created
|
|
586
|
+
).fromNow()}`
|
|
587
|
+
);
|
|
588
|
+
res.redirect(`/${type}edit`);
|
|
589
|
+
})
|
|
590
|
+
);
|
|
460
591
|
router.get(
|
|
461
592
|
"/auto-backup-download/:filename",
|
|
462
593
|
isAdmin,
|
|
@@ -483,9 +614,8 @@ router.get(
|
|
|
483
614
|
const autoBackupForm = (req) =>
|
|
484
615
|
new Form({
|
|
485
616
|
action: "/admin/set-auto-backup",
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
submitLabel: "Save settings",
|
|
617
|
+
onChange: `saveAndContinue(this);$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`,
|
|
618
|
+
noSubmitButton: true,
|
|
489
619
|
additionalButtons: [
|
|
490
620
|
{
|
|
491
621
|
label: "Backup now",
|
|
@@ -534,6 +664,43 @@ const autoBackupForm = (req) =>
|
|
|
534
664
|
],
|
|
535
665
|
});
|
|
536
666
|
|
|
667
|
+
const snapshotForm = (req) =>
|
|
668
|
+
new Form({
|
|
669
|
+
action: "/admin/set-snapshot",
|
|
670
|
+
onChange: `saveAndContinue(this);`,
|
|
671
|
+
noSubmitButton: true,
|
|
672
|
+
additionalButtons: [
|
|
673
|
+
{
|
|
674
|
+
label: "Snapshot now",
|
|
675
|
+
id: "btnSnapNow",
|
|
676
|
+
class: "btn btn-outline-secondary",
|
|
677
|
+
onclick: "ajax_post('/admin/snapshot-now')",
|
|
678
|
+
},
|
|
679
|
+
],
|
|
680
|
+
fields: [
|
|
681
|
+
{
|
|
682
|
+
type: "Bool",
|
|
683
|
+
label: req.__("Periodic snapshots enabled"),
|
|
684
|
+
name: "snapshots_enabled",
|
|
685
|
+
sublabel: req.__(
|
|
686
|
+
"Snapshot will be made every hour if there are changes"
|
|
687
|
+
),
|
|
688
|
+
},
|
|
689
|
+
],
|
|
690
|
+
});
|
|
691
|
+
router.post(
|
|
692
|
+
"/set-snapshot",
|
|
693
|
+
isAdmin,
|
|
694
|
+
error_catcher(async (req, res) => {
|
|
695
|
+
const form = await snapshotForm(req);
|
|
696
|
+
form.validate(req.body);
|
|
697
|
+
|
|
698
|
+
await save_config_from_form(form);
|
|
699
|
+
req.flash("success", req.__("Snapshot settings updated"));
|
|
700
|
+
if (!req.xhr) res.redirect("/admin/backup");
|
|
701
|
+
else res.json({ success: "ok" });
|
|
702
|
+
})
|
|
703
|
+
);
|
|
537
704
|
router.post(
|
|
538
705
|
"/set-auto-backup",
|
|
539
706
|
isAdmin,
|
|
@@ -554,7 +721,8 @@ router.post(
|
|
|
554
721
|
} else {
|
|
555
722
|
await save_config_from_form(form);
|
|
556
723
|
req.flash("success", req.__("Backup settings updated"));
|
|
557
|
-
res.redirect("/admin/backup");
|
|
724
|
+
if (!req.xhr) res.redirect("/admin/backup");
|
|
725
|
+
else res.json({ success: "ok" });
|
|
558
726
|
}
|
|
559
727
|
})
|
|
560
728
|
);
|
|
@@ -572,6 +740,22 @@ router.post(
|
|
|
572
740
|
})
|
|
573
741
|
);
|
|
574
742
|
|
|
743
|
+
router.post(
|
|
744
|
+
"/snapshot-now",
|
|
745
|
+
isAdmin,
|
|
746
|
+
error_catcher(async (req, res) => {
|
|
747
|
+
try {
|
|
748
|
+
const taken = await Snapshot.take_if_changed();
|
|
749
|
+
if (taken) req.flash("success", req.__("Snapshot successful"));
|
|
750
|
+
else
|
|
751
|
+
req.flash("success", req.__("No changes detected, snapshot skipped"));
|
|
752
|
+
} catch (e) {
|
|
753
|
+
req.flash("error", e.message);
|
|
754
|
+
}
|
|
755
|
+
res.json({ reload_page: true });
|
|
756
|
+
})
|
|
757
|
+
);
|
|
758
|
+
|
|
575
759
|
/**
|
|
576
760
|
* @name get/system
|
|
577
761
|
* @function
|
|
@@ -1057,12 +1241,39 @@ router.get(
|
|
|
1057
1241
|
})
|
|
1058
1242
|
);
|
|
1059
1243
|
|
|
1244
|
+
const dialogScript = `<script>
|
|
1245
|
+
function swapEntryInputs(activeTab, activeInput, disabledTab, disabledInput) {
|
|
1246
|
+
activeTab.addClass("active");
|
|
1247
|
+
activeInput.removeClass("d-none");
|
|
1248
|
+
activeInput.addClass("d-block");
|
|
1249
|
+
activeInput.attr("name", "entryPoint");
|
|
1250
|
+
disabledTab.removeClass("active");
|
|
1251
|
+
disabledInput.removeClass("d-block");
|
|
1252
|
+
disabledInput.addClass("d-none");
|
|
1253
|
+
disabledInput.removeAttr("name");
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
function showEntrySelect(type) {
|
|
1257
|
+
const viewNavLin = $("#viewNavLinkID");
|
|
1258
|
+
const pageNavLink = $("#pageNavLinkID");
|
|
1259
|
+
const viewInp = $("#viewInputID");
|
|
1260
|
+
const pageInp = $("#pageInputID");
|
|
1261
|
+
if (type === "page") {
|
|
1262
|
+
swapEntryInputs(pageNavLink, pageInp, viewNavLin, viewInp);
|
|
1263
|
+
}
|
|
1264
|
+
else if (type === "view") {
|
|
1265
|
+
swapEntryInputs(viewNavLin, viewInp, pageNavLink, pageInp);
|
|
1266
|
+
}
|
|
1267
|
+
$("#entryPointTypeID").attr("value", type);
|
|
1268
|
+
}
|
|
1269
|
+
</script>`;
|
|
1270
|
+
|
|
1060
1271
|
router.get(
|
|
1061
1272
|
"/build-mobile-app",
|
|
1062
1273
|
isAdmin,
|
|
1063
1274
|
error_catcher(async (req, res) => {
|
|
1064
|
-
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
1065
1275
|
const views = await View.find();
|
|
1276
|
+
const pages = await Page.find();
|
|
1066
1277
|
const execBuildMsg =
|
|
1067
1278
|
"This is still under development and might run longer.";
|
|
1068
1279
|
|
|
@@ -1070,6 +1281,11 @@ router.get(
|
|
|
1070
1281
|
res,
|
|
1071
1282
|
req,
|
|
1072
1283
|
active_sub: "Mobile app",
|
|
1284
|
+
headers: [
|
|
1285
|
+
{
|
|
1286
|
+
headerTag: dialogScript,
|
|
1287
|
+
},
|
|
1288
|
+
],
|
|
1073
1289
|
contents: {
|
|
1074
1290
|
above: [
|
|
1075
1291
|
{
|
|
@@ -1087,11 +1303,17 @@ router.get(
|
|
|
1087
1303
|
name: "_csrf",
|
|
1088
1304
|
value: req.csrfToken(),
|
|
1089
1305
|
}),
|
|
1306
|
+
input({
|
|
1307
|
+
type: "hidden",
|
|
1308
|
+
name: "entryPointType",
|
|
1309
|
+
value: "view",
|
|
1310
|
+
id: "entryPointTypeID",
|
|
1311
|
+
}),
|
|
1090
1312
|
div(
|
|
1091
1313
|
{ class: "container ps-2" },
|
|
1092
1314
|
div(
|
|
1093
1315
|
{ class: "row pb-2" },
|
|
1094
|
-
div({ class: "col-sm-4 fw-bold" }, "Entry
|
|
1316
|
+
div({ class: "col-sm-4 fw-bold" }, "Entry point"),
|
|
1095
1317
|
div({ class: "col-sm-4 fw-bold" }, "Platform"),
|
|
1096
1318
|
div(
|
|
1097
1319
|
{
|
|
@@ -1104,17 +1326,54 @@ router.get(
|
|
|
1104
1326
|
{ class: "row" },
|
|
1105
1327
|
div(
|
|
1106
1328
|
{ class: "col-sm-4" },
|
|
1329
|
+
// 'view/page' tabs
|
|
1330
|
+
ul(
|
|
1331
|
+
{ class: "nav nav-pills" },
|
|
1332
|
+
li(
|
|
1333
|
+
{
|
|
1334
|
+
class: "nav-item",
|
|
1335
|
+
onClick: "showEntrySelect('view')",
|
|
1336
|
+
},
|
|
1337
|
+
div(
|
|
1338
|
+
{ class: "nav-link active", id: "viewNavLinkID" },
|
|
1339
|
+
"View"
|
|
1340
|
+
)
|
|
1341
|
+
),
|
|
1342
|
+
li(
|
|
1343
|
+
{
|
|
1344
|
+
class: "nav-item",
|
|
1345
|
+
onClick: "showEntrySelect('page')",
|
|
1346
|
+
},
|
|
1347
|
+
div(
|
|
1348
|
+
{ class: "nav-link", id: "pageNavLinkID" },
|
|
1349
|
+
"Page"
|
|
1350
|
+
)
|
|
1351
|
+
)
|
|
1352
|
+
),
|
|
1353
|
+
// select entry-view
|
|
1107
1354
|
select(
|
|
1108
1355
|
{
|
|
1109
1356
|
class: "form-control",
|
|
1110
|
-
name: "
|
|
1111
|
-
id: "
|
|
1357
|
+
name: "entryPoint",
|
|
1358
|
+
id: "viewInputID",
|
|
1112
1359
|
},
|
|
1113
1360
|
views
|
|
1114
1361
|
.map((view) =>
|
|
1115
1362
|
option({ value: view.name }, view.name)
|
|
1116
1363
|
)
|
|
1117
1364
|
.join(",")
|
|
1365
|
+
),
|
|
1366
|
+
// select entry-page
|
|
1367
|
+
select(
|
|
1368
|
+
{
|
|
1369
|
+
class: "form-control d-none",
|
|
1370
|
+
id: "pageInputID",
|
|
1371
|
+
},
|
|
1372
|
+
pages
|
|
1373
|
+
.map((page) =>
|
|
1374
|
+
option({ value: page.name }, page.name)
|
|
1375
|
+
)
|
|
1376
|
+
.join(",")
|
|
1118
1377
|
)
|
|
1119
1378
|
),
|
|
1120
1379
|
div(
|
|
@@ -1196,7 +1455,7 @@ router.get(
|
|
|
1196
1455
|
class: "form-control",
|
|
1197
1456
|
name: "serverURL",
|
|
1198
1457
|
id: "serverURLInputId",
|
|
1199
|
-
placeholder:
|
|
1458
|
+
placeholder: getState().getConfig("base_url") || "",
|
|
1200
1459
|
})
|
|
1201
1460
|
)
|
|
1202
1461
|
)
|
|
@@ -1225,7 +1484,8 @@ router.post(
|
|
|
1225
1484
|
isAdmin,
|
|
1226
1485
|
error_catcher(async (req, res) => {
|
|
1227
1486
|
let {
|
|
1228
|
-
|
|
1487
|
+
entryPoint,
|
|
1488
|
+
entryPointType,
|
|
1229
1489
|
androidPlatform,
|
|
1230
1490
|
iOSPlatform,
|
|
1231
1491
|
useDocker,
|
|
@@ -1244,15 +1504,24 @@ router.post(
|
|
|
1244
1504
|
return res.redirect("/admin/build-mobile-app");
|
|
1245
1505
|
}
|
|
1246
1506
|
if (appFile && !appFile.endsWith(".apk")) appFile = `${appFile}.apk`;
|
|
1507
|
+
if (!serverURL || serverURL.length == 0) {
|
|
1508
|
+
serverURL = getState().getConfig("base_url") || "";
|
|
1509
|
+
}
|
|
1510
|
+
if (!serverURL.startsWith("http")) {
|
|
1511
|
+
req.flash("error", req.__("Please enter a valid server URL."));
|
|
1512
|
+
return res.redirect("/admin/build-mobile-app");
|
|
1513
|
+
}
|
|
1247
1514
|
const appOut = path.join(__dirname, "..", "mobile-app-out");
|
|
1248
1515
|
const spawnParams = [
|
|
1249
1516
|
"build-app",
|
|
1250
|
-
"-
|
|
1251
|
-
|
|
1517
|
+
"-e",
|
|
1518
|
+
entryPoint,
|
|
1519
|
+
"-t",
|
|
1520
|
+
entryPointType,
|
|
1252
1521
|
"-c",
|
|
1253
1522
|
appOut,
|
|
1254
1523
|
"-b",
|
|
1255
|
-
|
|
1524
|
+
`${os.userInfo().homedir}/mobile_app_build`,
|
|
1256
1525
|
];
|
|
1257
1526
|
if (useDocker) spawnParams.push("-d");
|
|
1258
1527
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
@@ -1260,7 +1529,7 @@ router.post(
|
|
|
1260
1529
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1261
1530
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1262
1531
|
const child = spawn("saltcorn", spawnParams, {
|
|
1263
|
-
stdio: ["ignore", "pipe",
|
|
1532
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1264
1533
|
cwd: ".",
|
|
1265
1534
|
});
|
|
1266
1535
|
const childOutputs = [];
|
|
@@ -1268,6 +1537,10 @@ router.post(
|
|
|
1268
1537
|
// console.log(data.toString());
|
|
1269
1538
|
childOutputs.push(data.toString());
|
|
1270
1539
|
});
|
|
1540
|
+
child.stderr.on("data", (data) => {
|
|
1541
|
+
// console.log(data.toString());
|
|
1542
|
+
childOutputs.push(data.toString());
|
|
1543
|
+
});
|
|
1271
1544
|
child.on("exit", async function (exitCode, signal) {
|
|
1272
1545
|
if (exitCode === 0) {
|
|
1273
1546
|
const file = await File.from_existing_file(
|
package/routes/api.js
CHANGED
|
@@ -112,6 +112,13 @@ function accessAllowed(req, user, trigger) {
|
|
|
112
112
|
return role <= trigger.min_role;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
const getFlashes = (req) =>
|
|
116
|
+
["error", "success", "danger", "warning", "information"]
|
|
117
|
+
.map((type) => {
|
|
118
|
+
return { type, msg: req.flash(type) };
|
|
119
|
+
})
|
|
120
|
+
.filter((a) => a.msg && a.msg.length && a.msg.length > 0);
|
|
121
|
+
|
|
115
122
|
router.post(
|
|
116
123
|
"/viewQuery/:viewName/:queryName",
|
|
117
124
|
error_catcher(async (req, res, next) => {
|
|
@@ -134,7 +141,7 @@ router.post(
|
|
|
134
141
|
if (queries[queryName]) {
|
|
135
142
|
const { args } = req.body;
|
|
136
143
|
const resp = await queries[queryName](...args, true);
|
|
137
|
-
res.json({ success: resp });
|
|
144
|
+
res.json({ success: resp, alerts: getFlashes(req) });
|
|
138
145
|
} else {
|
|
139
146
|
res.status(404).json({ error: req.__("Not found") });
|
|
140
147
|
}
|
|
@@ -235,6 +242,7 @@ router.get(
|
|
|
235
242
|
rows = await table.getJoinedRows(joinOpts);
|
|
236
243
|
} else if (req_query && req_query !== {}) {
|
|
237
244
|
const tbl_fields = await table.getFields();
|
|
245
|
+
readState(req_query, tbl_fields, req);
|
|
238
246
|
const qstate = await stateFieldsToWhere({
|
|
239
247
|
fields: tbl_fields,
|
|
240
248
|
approximate: !!approximate,
|
package/routes/eventlog.js
CHANGED
|
@@ -73,8 +73,9 @@ const logSettingsForm = async (req) => {
|
|
|
73
73
|
fields.push({
|
|
74
74
|
name: w + "_channel",
|
|
75
75
|
label: w + " channel",
|
|
76
|
-
sublabel:
|
|
77
|
-
|
|
76
|
+
sublabel: req.__(
|
|
77
|
+
"Channels to create events for. Separate by comma; leave blank for all"
|
|
78
|
+
),
|
|
78
79
|
type: "String",
|
|
79
80
|
showIf: { [w]: true },
|
|
80
81
|
});
|
|
@@ -82,8 +83,8 @@ const logSettingsForm = async (req) => {
|
|
|
82
83
|
return new Form({
|
|
83
84
|
action: "/eventlog/settings",
|
|
84
85
|
blurb: req.__("Which events should be logged?"),
|
|
85
|
-
|
|
86
|
-
onChange: "
|
|
86
|
+
noSubmitButton: true,
|
|
87
|
+
onChange: "saveAndContinue(this)",
|
|
87
88
|
fields,
|
|
88
89
|
});
|
|
89
90
|
};
|
|
@@ -169,23 +170,23 @@ router.get(
|
|
|
169
170
|
* @returns {Form}
|
|
170
171
|
*/
|
|
171
172
|
const customEventForm = async (req) => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
173
|
+
return new Form({
|
|
174
|
+
action: "/eventlog/custom/new",
|
|
175
|
+
submitButtonClass: "btn-outline-primary",
|
|
176
|
+
onChange: "remove_outline(this)",
|
|
177
|
+
fields: [
|
|
178
|
+
{
|
|
179
|
+
name: "name",
|
|
180
|
+
label: req.__("Event Name"),
|
|
181
|
+
type: "String",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "hasChannel",
|
|
185
|
+
label: req.__("Has channels?"),
|
|
186
|
+
type: "Bool",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
});
|
|
189
190
|
};
|
|
190
191
|
/**
|
|
191
192
|
* @name get/custom/new
|
|
@@ -297,7 +298,8 @@ router.post(
|
|
|
297
298
|
} else {
|
|
298
299
|
await getState().setConfig("event_log_settings", form.values);
|
|
299
300
|
|
|
300
|
-
res.redirect(`/eventlog/settings`);
|
|
301
|
+
if (!req.xhr) res.redirect(`/eventlog/settings`);
|
|
302
|
+
else res.json({ success: "ok" });
|
|
301
303
|
}
|
|
302
304
|
})
|
|
303
305
|
);
|
package/routes/files.js
CHANGED
|
@@ -378,9 +378,6 @@ const storage_form = async (req) => {
|
|
|
378
378
|
],
|
|
379
379
|
action: "/files/storage",
|
|
380
380
|
});
|
|
381
|
-
form.submitButtonClass = "btn-outline-primary";
|
|
382
|
-
form.submitLabel = req.__("Save");
|
|
383
|
-
form.onChange = "remove_outline(this)";
|
|
384
381
|
return form;
|
|
385
382
|
};
|
|
386
383
|
|
|
@@ -431,8 +428,11 @@ router.post(
|
|
|
431
428
|
});
|
|
432
429
|
} else {
|
|
433
430
|
await save_config_from_form(form);
|
|
434
|
-
|
|
435
|
-
|
|
431
|
+
|
|
432
|
+
if (!req.xhr) {
|
|
433
|
+
req.flash("success", req.__("Storage settings updated"));
|
|
434
|
+
res.redirect("/files/storage");
|
|
435
|
+
} else res.json({ success: "ok" });
|
|
436
436
|
}
|
|
437
437
|
})
|
|
438
438
|
);
|
package/routes/infoarch.js
CHANGED
|
@@ -48,8 +48,8 @@ router.get(
|
|
|
48
48
|
const languageForm = (req) =>
|
|
49
49
|
new Form({
|
|
50
50
|
action: "/site-structure/localizer/save-lang",
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
onChange: "saveAndContinue(this)",
|
|
52
|
+
noSubmitButton: true,
|
|
53
53
|
fields: [
|
|
54
54
|
{
|
|
55
55
|
name: "name",
|
|
@@ -270,7 +270,10 @@ router.post(
|
|
|
270
270
|
...cfgLangs,
|
|
271
271
|
[lang.locale]: lang,
|
|
272
272
|
});
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
if (!req.xhr)
|
|
275
|
+
res.redirect(`/site-structure/localizer/edit/${lang.locale}`);
|
|
276
|
+
else res.json({ success: "ok" });
|
|
274
277
|
}
|
|
275
278
|
})
|
|
276
279
|
);
|
package/routes/page.js
CHANGED
|
@@ -36,6 +36,8 @@ router.get(
|
|
|
36
36
|
"/:pagename",
|
|
37
37
|
error_catcher(async (req, res) => {
|
|
38
38
|
const { pagename } = req.params;
|
|
39
|
+
const state = getState();
|
|
40
|
+
state.log(3, `Route /page/${pagename} user=${req.user?.id}`);
|
|
39
41
|
|
|
40
42
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
41
43
|
const db_page = await Page.findOne({ name: pagename });
|
|
@@ -56,10 +58,12 @@ router.get(
|
|
|
56
58
|
contents,
|
|
57
59
|
})
|
|
58
60
|
);
|
|
59
|
-
} else
|
|
61
|
+
} else {
|
|
62
|
+
state.log(2, `Page $pagename} not found or not authorized`);
|
|
60
63
|
res
|
|
61
64
|
.status(404)
|
|
62
65
|
.sendWrap(`${pagename} page`, req.__("Page %s not found", pagename));
|
|
66
|
+
}
|
|
63
67
|
})
|
|
64
68
|
);
|
|
65
69
|
|