@saltcorn/server 0.7.3 → 0.7.4-beta.2
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 +2 -1
- package/auth/routes.js +80 -92
- package/errors.js +51 -48
- package/locales/en.json +47 -2
- package/locales/it.json +2 -1
- package/locales/ru.json +42 -6
- package/locales/zh.json +1 -1
- package/markup/admin.js +15 -1
- package/markup/plugin-store.js +5 -5
- package/package.json +7 -7
- package/public/jquery-menu-editor.min.js +1 -1
- package/public/saltcorn-builder.css +75 -0
- package/public/saltcorn-common.js +24 -9
- package/public/saltcorn.css +28 -1
- package/public/saltcorn.js +9 -7
- package/routes/actions.js +2 -39
- package/routes/admin.js +375 -90
- package/routes/api.js +9 -1
- package/routes/common_lists.js +419 -0
- package/routes/fields.js +34 -19
- package/routes/homepage.js +60 -60
- package/routes/index.js +4 -0
- package/routes/menu.js +65 -4
- package/routes/packs.js +4 -4
- package/routes/page.js +5 -1
- package/routes/pageedit.js +13 -98
- package/routes/plugins.js +116 -118
- package/routes/settings.js +3 -3
- package/routes/tables.js +158 -193
- package/routes/tag_entries.js +173 -0
- package/routes/tags.js +266 -0
- package/routes/tenant.js +27 -27
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +22 -132
- package/serve.js +54 -38
- package/tests/admin.test.js +1 -1
- package/tests/api.test.js +17 -0
- package/tests/clientjs.test.js +11 -1
- package/tests/plugins.test.js +1 -1
- package/tests/viewedit.test.js +1 -1
- package/wrapper.js +57 -55
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,7 +48,6 @@ const {
|
|
|
42
48
|
select,
|
|
43
49
|
option,
|
|
44
50
|
fieldset,
|
|
45
|
-
legend,
|
|
46
51
|
ul,
|
|
47
52
|
li,
|
|
48
53
|
ol,
|
|
@@ -63,6 +68,7 @@ const {
|
|
|
63
68
|
restore,
|
|
64
69
|
auto_backup_now,
|
|
65
70
|
} = require("@saltcorn/admin-models/models/backup");
|
|
71
|
+
const Snapshot = require("@saltcorn/admin-models/models/snapshot");
|
|
66
72
|
const {
|
|
67
73
|
runConfigurationCheck,
|
|
68
74
|
} = require("@saltcorn/admin-models/models/config-check");
|
|
@@ -89,6 +95,7 @@ const moment = require("moment");
|
|
|
89
95
|
const View = require("@saltcorn/data/models/view");
|
|
90
96
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
91
97
|
const os = require("os");
|
|
98
|
+
const Page = require("@saltcorn/data/models/page");
|
|
92
99
|
|
|
93
100
|
/**
|
|
94
101
|
* @type {object}
|
|
@@ -118,6 +125,7 @@ const site_id_form = (req) =>
|
|
|
118
125
|
"page_custom_html",
|
|
119
126
|
"development_mode",
|
|
120
127
|
"log_sql",
|
|
128
|
+
"log_level",
|
|
121
129
|
"plugins_store_endpoint",
|
|
122
130
|
"packs_store_endpoint",
|
|
123
131
|
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
@@ -146,7 +154,7 @@ const email_form = async (req) => {
|
|
|
146
154
|
return form;
|
|
147
155
|
};
|
|
148
156
|
|
|
149
|
-
const app_files_table = (
|
|
157
|
+
const app_files_table = (files, req) =>
|
|
150
158
|
mkTable(
|
|
151
159
|
[
|
|
152
160
|
{
|
|
@@ -160,7 +168,7 @@ const app_files_table = (file, req) =>
|
|
|
160
168
|
key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
|
|
161
169
|
},
|
|
162
170
|
],
|
|
163
|
-
|
|
171
|
+
files
|
|
164
172
|
);
|
|
165
173
|
|
|
166
174
|
/**
|
|
@@ -337,6 +345,9 @@ router.get(
|
|
|
337
345
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
338
346
|
"auto_backup_expire_days"
|
|
339
347
|
);
|
|
348
|
+
const aSnapshotForm = snapshotForm(req);
|
|
349
|
+
aSnapshotForm.values.snapshots_enabled =
|
|
350
|
+
getState().getConfig("snapshots_enabled");
|
|
340
351
|
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
341
352
|
|
|
342
353
|
send_admin_page({
|
|
@@ -354,7 +365,7 @@ router.get(
|
|
|
354
365
|
post_btn(
|
|
355
366
|
"/admin/backup",
|
|
356
367
|
i({ class: "fas fa-download me-2" }) +
|
|
357
|
-
|
|
368
|
+
req.__("Download a backup"),
|
|
358
369
|
req.csrfToken(),
|
|
359
370
|
{
|
|
360
371
|
btnClass: "btn-outline-primary",
|
|
@@ -373,22 +384,38 @@ router.get(
|
|
|
373
384
|
},
|
|
374
385
|
isRoot
|
|
375
386
|
? {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
),
|
|
384
|
-
script(
|
|
385
|
-
domReady(
|
|
386
|
-
`$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
|
|
387
|
-
)
|
|
388
|
-
)
|
|
387
|
+
type: "card",
|
|
388
|
+
title: req.__("Automated backup"),
|
|
389
|
+
contents: div(
|
|
390
|
+
renderForm(backupForm, req.csrfToken()),
|
|
391
|
+
a(
|
|
392
|
+
{ href: "/admin/auto-backup-list" },
|
|
393
|
+
"Restore/download automated backups »"
|
|
389
394
|
),
|
|
390
|
-
|
|
395
|
+
script(
|
|
396
|
+
domReady(
|
|
397
|
+
`$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
),
|
|
401
|
+
}
|
|
391
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
|
+
},
|
|
392
419
|
],
|
|
393
420
|
},
|
|
394
421
|
});
|
|
@@ -464,6 +491,103 @@ router.get(
|
|
|
464
491
|
})
|
|
465
492
|
);
|
|
466
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
|
+
);
|
|
467
591
|
router.get(
|
|
468
592
|
"/auto-backup-download/:filename",
|
|
469
593
|
isAdmin,
|
|
@@ -540,6 +664,43 @@ const autoBackupForm = (req) =>
|
|
|
540
664
|
],
|
|
541
665
|
});
|
|
542
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
|
+
);
|
|
543
704
|
router.post(
|
|
544
705
|
"/set-auto-backup",
|
|
545
706
|
isAdmin,
|
|
@@ -579,6 +740,22 @@ router.post(
|
|
|
579
740
|
})
|
|
580
741
|
);
|
|
581
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
|
+
|
|
582
759
|
/**
|
|
583
760
|
* @name get/system
|
|
584
761
|
* @function
|
|
@@ -654,47 +831,47 @@ router.get(
|
|
|
654
831
|
th(req.__("Saltcorn version")),
|
|
655
832
|
td(
|
|
656
833
|
packagejson.version +
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
834
|
+
(isRoot && can_update
|
|
835
|
+
? post_btn(
|
|
836
|
+
"/admin/upgrade",
|
|
837
|
+
req.__("Upgrade"),
|
|
838
|
+
req.csrfToken(),
|
|
839
|
+
{
|
|
840
|
+
btnClass: "btn-primary btn-sm",
|
|
841
|
+
formClass: "d-inline",
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
: isRoot && is_latest
|
|
668
845
|
? span(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
846
|
+
{ class: "badge bg-primary ms-2" },
|
|
847
|
+
req.__("Latest")
|
|
848
|
+
) +
|
|
849
|
+
post_btn(
|
|
850
|
+
"/admin/check-for-upgrade",
|
|
851
|
+
req.__("Check for updates"),
|
|
852
|
+
req.csrfToken(),
|
|
853
|
+
{
|
|
854
|
+
btnClass: "btn-primary btn-sm px-1 py-0",
|
|
855
|
+
formClass: "d-inline",
|
|
856
|
+
}
|
|
857
|
+
)
|
|
681
858
|
: "")
|
|
682
859
|
)
|
|
683
860
|
),
|
|
684
861
|
git_commit &&
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
)
|
|
862
|
+
tr(
|
|
863
|
+
th(req.__("git commit")),
|
|
864
|
+
td(
|
|
865
|
+
a(
|
|
866
|
+
{
|
|
867
|
+
href:
|
|
868
|
+
"https://github.com/saltcorn/saltcorn/commit/" +
|
|
869
|
+
git_commit,
|
|
870
|
+
},
|
|
871
|
+
git_commit.substring(0, 6)
|
|
696
872
|
)
|
|
697
|
-
)
|
|
873
|
+
)
|
|
874
|
+
),
|
|
698
875
|
tr(th(req.__("Node.js version")), td(process.version)),
|
|
699
876
|
tr(
|
|
700
877
|
th(req.__("Database")),
|
|
@@ -804,7 +981,7 @@ router.post(
|
|
|
804
981
|
res.attachment(fileName);
|
|
805
982
|
const file = fs.createReadStream(fileName);
|
|
806
983
|
file.on("end", function () {
|
|
807
|
-
fs.unlink(fileName, function () {});
|
|
984
|
+
fs.unlink(fileName, function () { });
|
|
808
985
|
});
|
|
809
986
|
file.pipe(res);
|
|
810
987
|
})
|
|
@@ -827,7 +1004,7 @@ router.post(
|
|
|
827
1004
|
);
|
|
828
1005
|
if (err) req.flash("error", err);
|
|
829
1006
|
else req.flash("success", req.__("Successfully restored backup"));
|
|
830
|
-
fs.unlink(newPath, function () {});
|
|
1007
|
+
fs.unlink(newPath, function () { });
|
|
831
1008
|
res.redirect(`/admin`);
|
|
832
1009
|
})
|
|
833
1010
|
);
|
|
@@ -902,7 +1079,7 @@ const clearAllForm = (req) =>
|
|
|
902
1079
|
{
|
|
903
1080
|
type: "Bool",
|
|
904
1081
|
name: "plugins",
|
|
905
|
-
label: req.__("
|
|
1082
|
+
label: req.__("Modules"),
|
|
906
1083
|
default: true,
|
|
907
1084
|
},
|
|
908
1085
|
],
|
|
@@ -972,8 +1149,8 @@ router.post(
|
|
|
972
1149
|
req.__(
|
|
973
1150
|
"LetsEncrypt SSL enabled. Restart for changes to take effect."
|
|
974
1151
|
) +
|
|
975
|
-
|
|
976
|
-
|
|
1152
|
+
" " +
|
|
1153
|
+
a({ href: "/admin/system" }, req.__("Restart here"))
|
|
977
1154
|
);
|
|
978
1155
|
res.redirect("/useradmin/ssl");
|
|
979
1156
|
} catch (e) {
|
|
@@ -1044,13 +1221,13 @@ router.get(
|
|
|
1044
1221
|
contents: div(
|
|
1045
1222
|
pass
|
|
1046
1223
|
? div(
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
)
|
|
1224
|
+
{ class: "alert alert-success", role: "alert" },
|
|
1225
|
+
i({ class: "fas fa-check-circle fa-lg me-2" }),
|
|
1226
|
+
h5(
|
|
1227
|
+
{ class: "d-inline" },
|
|
1228
|
+
req.__("No errors detected during configuration check")
|
|
1053
1229
|
)
|
|
1230
|
+
)
|
|
1054
1231
|
: errors.map(mkError)
|
|
1055
1232
|
),
|
|
1056
1233
|
},
|
|
@@ -1064,19 +1241,66 @@ router.get(
|
|
|
1064
1241
|
})
|
|
1065
1242
|
);
|
|
1066
1243
|
|
|
1244
|
+
const buildDialogScript = () => {
|
|
1245
|
+
return `<script>
|
|
1246
|
+
function swapEntryInputs(activeTab, activeInput, disabledTab, disabledInput) {
|
|
1247
|
+
activeTab.addClass("active");
|
|
1248
|
+
activeInput.removeClass("d-none");
|
|
1249
|
+
activeInput.addClass("d-block");
|
|
1250
|
+
activeInput.attr("name", "entryPoint");
|
|
1251
|
+
disabledTab.removeClass("active");
|
|
1252
|
+
disabledInput.removeClass("d-block");
|
|
1253
|
+
disabledInput.addClass("d-none");
|
|
1254
|
+
disabledInput.removeAttr("name");
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function showEntrySelect(type) {
|
|
1258
|
+
const viewNavLin = $("#viewNavLinkID");
|
|
1259
|
+
const pageNavLink = $("#pageNavLinkID");
|
|
1260
|
+
const viewInp = $("#viewInputID");
|
|
1261
|
+
const pageInp = $("#pageInputID");
|
|
1262
|
+
if (type === "page") {
|
|
1263
|
+
swapEntryInputs(pageNavLink, pageInp, viewNavLin, viewInp);
|
|
1264
|
+
}
|
|
1265
|
+
else if (type === "view") {
|
|
1266
|
+
swapEntryInputs(viewNavLin, viewInp, pageNavLink, pageInp);
|
|
1267
|
+
}
|
|
1268
|
+
$("#entryPointTypeID").attr("value", type);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
function handleMessages() {
|
|
1272
|
+
notifyAlert("This is still under development and might run longer.")
|
|
1273
|
+
${
|
|
1274
|
+
getState().getConfig("apple_team_id") &&
|
|
1275
|
+
getState().getConfig("apple_team_id") !== "null"
|
|
1276
|
+
? ""
|
|
1277
|
+
: `
|
|
1278
|
+
if ($("#iOSCheckboxId")[0].checked) {
|
|
1279
|
+
notifyAlert(
|
|
1280
|
+
"No 'Apple Team ID' is configured, I will try to build a project for the iOS simulator."
|
|
1281
|
+
);
|
|
1282
|
+
}`
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
</script>`;
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1067
1288
|
router.get(
|
|
1068
1289
|
"/build-mobile-app",
|
|
1069
1290
|
isAdmin,
|
|
1070
1291
|
error_catcher(async (req, res) => {
|
|
1071
|
-
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
1072
1292
|
const views = await View.find();
|
|
1073
|
-
const
|
|
1074
|
-
"This is still under development and might run longer.";
|
|
1293
|
+
const pages = await Page.find();
|
|
1075
1294
|
|
|
1076
1295
|
send_admin_page({
|
|
1077
1296
|
res,
|
|
1078
1297
|
req,
|
|
1079
1298
|
active_sub: "Mobile app",
|
|
1299
|
+
headers: [
|
|
1300
|
+
{
|
|
1301
|
+
headerTag: buildDialogScript(),
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1080
1304
|
contents: {
|
|
1081
1305
|
above: [
|
|
1082
1306
|
{
|
|
@@ -1094,34 +1318,77 @@ router.get(
|
|
|
1094
1318
|
name: "_csrf",
|
|
1095
1319
|
value: req.csrfToken(),
|
|
1096
1320
|
}),
|
|
1321
|
+
input({
|
|
1322
|
+
type: "hidden",
|
|
1323
|
+
name: "entryPointType",
|
|
1324
|
+
value: "view",
|
|
1325
|
+
id: "entryPointTypeID",
|
|
1326
|
+
}),
|
|
1097
1327
|
div(
|
|
1098
1328
|
{ class: "container ps-2" },
|
|
1099
1329
|
div(
|
|
1100
1330
|
{ class: "row pb-2" },
|
|
1101
|
-
div({ class: "col-sm-4 fw-bold" }, "Entry
|
|
1102
|
-
div({ class: "col-sm-4 fw-bold" }, "Platform"),
|
|
1331
|
+
div({ class: "col-sm-4 fw-bold" }, req.__("Entry point")),
|
|
1332
|
+
div({ class: "col-sm-4 fw-bold" }, req.__("Platform")),
|
|
1103
1333
|
div(
|
|
1104
1334
|
{
|
|
1105
1335
|
class: "col-sm-1 fw-bold d-flex justify-content-center",
|
|
1106
1336
|
},
|
|
1107
|
-
"docker"
|
|
1337
|
+
req.__("docker")
|
|
1108
1338
|
)
|
|
1109
1339
|
),
|
|
1110
1340
|
div(
|
|
1111
1341
|
{ class: "row" },
|
|
1112
1342
|
div(
|
|
1113
1343
|
{ class: "col-sm-4" },
|
|
1344
|
+
// 'view/page' tabs
|
|
1345
|
+
ul(
|
|
1346
|
+
{ class: "nav nav-pills" },
|
|
1347
|
+
li(
|
|
1348
|
+
{
|
|
1349
|
+
class: "nav-item",
|
|
1350
|
+
onClick: "showEntrySelect('view')",
|
|
1351
|
+
},
|
|
1352
|
+
div(
|
|
1353
|
+
{ class: "nav-link active", id: "viewNavLinkID" },
|
|
1354
|
+
req.__("View")
|
|
1355
|
+
)
|
|
1356
|
+
),
|
|
1357
|
+
li(
|
|
1358
|
+
{
|
|
1359
|
+
class: "nav-item",
|
|
1360
|
+
onClick: "showEntrySelect('page')",
|
|
1361
|
+
},
|
|
1362
|
+
div(
|
|
1363
|
+
{ class: "nav-link", id: "pageNavLinkID" },
|
|
1364
|
+
req.__("Page")
|
|
1365
|
+
)
|
|
1366
|
+
)
|
|
1367
|
+
),
|
|
1368
|
+
// select entry-view
|
|
1114
1369
|
select(
|
|
1115
1370
|
{
|
|
1116
1371
|
class: "form-control",
|
|
1117
|
-
name: "
|
|
1118
|
-
id: "
|
|
1372
|
+
name: "entryPoint",
|
|
1373
|
+
id: "viewInputID",
|
|
1119
1374
|
},
|
|
1120
1375
|
views
|
|
1121
1376
|
.map((view) =>
|
|
1122
1377
|
option({ value: view.name }, view.name)
|
|
1123
1378
|
)
|
|
1124
1379
|
.join(",")
|
|
1380
|
+
),
|
|
1381
|
+
// select entry-page
|
|
1382
|
+
select(
|
|
1383
|
+
{
|
|
1384
|
+
class: "form-control d-none",
|
|
1385
|
+
id: "pageInputID",
|
|
1386
|
+
},
|
|
1387
|
+
pages
|
|
1388
|
+
.map((page) =>
|
|
1389
|
+
option({ value: page.name }, page.name)
|
|
1390
|
+
)
|
|
1391
|
+
.join(",")
|
|
1125
1392
|
)
|
|
1126
1393
|
),
|
|
1127
1394
|
div(
|
|
@@ -1131,7 +1398,7 @@ router.get(
|
|
|
1131
1398
|
{ class: "container ps-0" },
|
|
1132
1399
|
div(
|
|
1133
1400
|
{ class: "row" },
|
|
1134
|
-
div({ class: "col-sm-8" }, "android"),
|
|
1401
|
+
div({ class: "col-sm-8" }, req.__("android")),
|
|
1135
1402
|
div(
|
|
1136
1403
|
{ class: "col-sm" },
|
|
1137
1404
|
input({
|
|
@@ -1144,7 +1411,7 @@ router.get(
|
|
|
1144
1411
|
),
|
|
1145
1412
|
div(
|
|
1146
1413
|
{ class: "row" },
|
|
1147
|
-
div({ class: "col-sm-8" }, "iOS"),
|
|
1414
|
+
div({ class: "col-sm-8" }, req.__("iOS")),
|
|
1148
1415
|
div(
|
|
1149
1416
|
{ class: "col-sm" },
|
|
1150
1417
|
input({
|
|
@@ -1176,7 +1443,7 @@ router.get(
|
|
|
1176
1443
|
for: "appNameInputId",
|
|
1177
1444
|
class: "form-label fw-bold",
|
|
1178
1445
|
},
|
|
1179
|
-
"App file"
|
|
1446
|
+
req.__("App file")
|
|
1180
1447
|
),
|
|
1181
1448
|
input({
|
|
1182
1449
|
type: "text",
|
|
@@ -1196,14 +1463,14 @@ router.get(
|
|
|
1196
1463
|
for: "serverURLInputId",
|
|
1197
1464
|
class: "form-label fw-bold",
|
|
1198
1465
|
},
|
|
1199
|
-
"Server URL"
|
|
1466
|
+
req.__("Server URL")
|
|
1200
1467
|
),
|
|
1201
1468
|
input({
|
|
1202
1469
|
type: "text",
|
|
1203
1470
|
class: "form-control",
|
|
1204
1471
|
name: "serverURL",
|
|
1205
1472
|
id: "serverURLInputId",
|
|
1206
|
-
placeholder:
|
|
1473
|
+
placeholder: getState().getConfig("base_url") || "",
|
|
1207
1474
|
})
|
|
1208
1475
|
)
|
|
1209
1476
|
)
|
|
@@ -1211,12 +1478,12 @@ router.get(
|
|
|
1211
1478
|
button(
|
|
1212
1479
|
{
|
|
1213
1480
|
type: "submit",
|
|
1214
|
-
onClick: `
|
|
1481
|
+
onClick: `handleMessages(); press_store_button(this);`,
|
|
1215
1482
|
class: "btn btn-warning",
|
|
1216
1483
|
},
|
|
1217
1484
|
i({ class: "fas fa-hammer pe-2" }),
|
|
1218
1485
|
|
|
1219
|
-
"Build mobile app"
|
|
1486
|
+
req.__("Build mobile app")
|
|
1220
1487
|
)
|
|
1221
1488
|
)
|
|
1222
1489
|
),
|
|
@@ -1232,7 +1499,8 @@ router.post(
|
|
|
1232
1499
|
isAdmin,
|
|
1233
1500
|
error_catcher(async (req, res) => {
|
|
1234
1501
|
let {
|
|
1235
|
-
|
|
1502
|
+
entryPoint,
|
|
1503
|
+
entryPointType,
|
|
1236
1504
|
androidPlatform,
|
|
1237
1505
|
iOSPlatform,
|
|
1238
1506
|
useDocker,
|
|
@@ -1250,12 +1518,20 @@ router.post(
|
|
|
1250
1518
|
req.flash("error", req.__("Only the android build supports docker."));
|
|
1251
1519
|
return res.redirect("/admin/build-mobile-app");
|
|
1252
1520
|
}
|
|
1253
|
-
if (
|
|
1521
|
+
if (!serverURL || serverURL.length == 0) {
|
|
1522
|
+
serverURL = getState().getConfig("base_url") || "";
|
|
1523
|
+
}
|
|
1524
|
+
if (!serverURL.startsWith("http")) {
|
|
1525
|
+
req.flash("error", req.__("Please enter a valid server URL."));
|
|
1526
|
+
return res.redirect("/admin/build-mobile-app");
|
|
1527
|
+
}
|
|
1254
1528
|
const appOut = path.join(__dirname, "..", "mobile-app-out");
|
|
1255
1529
|
const spawnParams = [
|
|
1256
1530
|
"build-app",
|
|
1257
|
-
"-
|
|
1258
|
-
|
|
1531
|
+
"-e",
|
|
1532
|
+
entryPoint,
|
|
1533
|
+
"-t",
|
|
1534
|
+
entryPointType,
|
|
1259
1535
|
"-c",
|
|
1260
1536
|
appOut,
|
|
1261
1537
|
"-b",
|
|
@@ -1263,7 +1539,13 @@ router.post(
|
|
|
1263
1539
|
];
|
|
1264
1540
|
if (useDocker) spawnParams.push("-d");
|
|
1265
1541
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
1266
|
-
if (iOSPlatform)
|
|
1542
|
+
if (iOSPlatform) {
|
|
1543
|
+
spawnParams.push("-p", "ios");
|
|
1544
|
+
const teamId = getState().getConfig("apple_team_id");
|
|
1545
|
+
if (!teamId || teamId === "null") {
|
|
1546
|
+
spawnParams.push("--buildForEmulator");
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1267
1549
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1268
1550
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1269
1551
|
const child = spawn("saltcorn", spawnParams, {
|
|
@@ -1281,10 +1563,13 @@ router.post(
|
|
|
1281
1563
|
});
|
|
1282
1564
|
child.on("exit", async function (exitCode, signal) {
|
|
1283
1565
|
if (exitCode === 0) {
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1566
|
+
const files = await Promise.all(
|
|
1567
|
+
fs
|
|
1568
|
+
.readdirSync(appOut)
|
|
1569
|
+
.map(
|
|
1570
|
+
async (outFile) =>
|
|
1571
|
+
await File.from_existing_file(appOut, outFile, req.user.id)
|
|
1572
|
+
)
|
|
1288
1573
|
);
|
|
1289
1574
|
res.sendWrap(req.__(`Admin`), {
|
|
1290
1575
|
above: [
|
|
@@ -1293,7 +1578,7 @@ router.post(
|
|
|
1293
1578
|
title: req.__("Build Result"),
|
|
1294
1579
|
contents: div("The build was successfully"),
|
|
1295
1580
|
},
|
|
1296
|
-
app_files_table(
|
|
1581
|
+
files.length > 0 ? app_files_table(files, req) : "",
|
|
1297
1582
|
],
|
|
1298
1583
|
});
|
|
1299
1584
|
} else
|