@saltcorn/server 0.7.3-beta.7 → 0.7.4-beta.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/auth/admin.js +9 -5
- package/auth/routes.js +16 -6
- package/errors.js +51 -48
- package/locales/en.json +29 -1
- package/locales/it.json +2 -1
- package/locales/ru.json +42 -6
- package/locales/zh.json +1 -1
- package/markup/admin.js +4 -3
- 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 +26 -10
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +5 -1
- package/routes/admin.js +387 -96
- package/routes/api.js +9 -1
- package/routes/eventlog.js +24 -22
- package/routes/fields.js +11 -13
- package/routes/files.js +5 -5
- package/routes/homepage.js +60 -60
- package/routes/infoarch.js +6 -3
- package/routes/menu.js +65 -4
- package/routes/packs.js +4 -4
- package/routes/page.js +5 -1
- package/routes/pageedit.js +9 -1
- package/routes/plugins.js +187 -123
- package/routes/search.js +4 -2
- package/routes/settings.js +3 -3
- package/routes/tables.js +191 -190
- package/routes/tenant.js +31 -29
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +78 -70
- 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,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");
|
|
@@ -87,6 +95,7 @@ const moment = require("moment");
|
|
|
87
95
|
const View = require("@saltcorn/data/models/view");
|
|
88
96
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
89
97
|
const os = require("os");
|
|
98
|
+
const Page = require("@saltcorn/data/models/page");
|
|
90
99
|
|
|
91
100
|
/**
|
|
92
101
|
* @type {object}
|
|
@@ -116,6 +125,7 @@ const site_id_form = (req) =>
|
|
|
116
125
|
"page_custom_html",
|
|
117
126
|
"development_mode",
|
|
118
127
|
"log_sql",
|
|
128
|
+
"log_level",
|
|
119
129
|
"plugins_store_endpoint",
|
|
120
130
|
"packs_store_endpoint",
|
|
121
131
|
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
@@ -141,14 +151,10 @@ const email_form = async (req) => {
|
|
|
141
151
|
],
|
|
142
152
|
action: "/admin/email",
|
|
143
153
|
});
|
|
144
|
-
form.submitButtonClass = "btn-outline-primary";
|
|
145
|
-
form.submitLabel = req.__("Save");
|
|
146
|
-
form.onChange =
|
|
147
|
-
"remove_outline(this);$('#testemail').attr('href','#').removeClass('btn-primary').addClass('btn-outline-primary')";
|
|
148
154
|
return form;
|
|
149
155
|
};
|
|
150
156
|
|
|
151
|
-
const app_files_table = (
|
|
157
|
+
const app_files_table = (files, req) =>
|
|
152
158
|
mkTable(
|
|
153
159
|
[
|
|
154
160
|
{
|
|
@@ -162,7 +168,7 @@ const app_files_table = (file, req) =>
|
|
|
162
168
|
key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
|
|
163
169
|
},
|
|
164
170
|
],
|
|
165
|
-
|
|
171
|
+
files
|
|
166
172
|
);
|
|
167
173
|
|
|
168
174
|
/**
|
|
@@ -215,8 +221,10 @@ router.post(
|
|
|
215
221
|
flash_restart_if_required(form, req);
|
|
216
222
|
await save_config_from_form(form);
|
|
217
223
|
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
if (!req.xhr) {
|
|
225
|
+
req.flash("success", req.__("Site identity settings updated"));
|
|
226
|
+
res.redirect("/admin");
|
|
227
|
+
} else res.json({ success: "ok" });
|
|
220
228
|
}
|
|
221
229
|
})
|
|
222
230
|
);
|
|
@@ -309,7 +317,8 @@ router.post(
|
|
|
309
317
|
} else {
|
|
310
318
|
await save_config_from_form(form);
|
|
311
319
|
req.flash("success", req.__("Email settings updated"));
|
|
312
|
-
res.redirect("/admin/email");
|
|
320
|
+
if (!req.xhr) res.redirect("/admin/email");
|
|
321
|
+
else res.json({ success: "ok" });
|
|
313
322
|
}
|
|
314
323
|
})
|
|
315
324
|
);
|
|
@@ -336,6 +345,9 @@ router.get(
|
|
|
336
345
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
337
346
|
"auto_backup_expire_days"
|
|
338
347
|
);
|
|
348
|
+
const aSnapshotForm = snapshotForm(req);
|
|
349
|
+
aSnapshotForm.values.snapshots_enabled =
|
|
350
|
+
getState().getConfig("snapshots_enabled");
|
|
339
351
|
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
340
352
|
|
|
341
353
|
send_admin_page({
|
|
@@ -353,7 +365,7 @@ router.get(
|
|
|
353
365
|
post_btn(
|
|
354
366
|
"/admin/backup",
|
|
355
367
|
i({ class: "fas fa-download me-2" }) +
|
|
356
|
-
|
|
368
|
+
req.__("Download a backup"),
|
|
357
369
|
req.csrfToken(),
|
|
358
370
|
{
|
|
359
371
|
btnClass: "btn-outline-primary",
|
|
@@ -372,17 +384,38 @@ router.get(
|
|
|
372
384
|
},
|
|
373
385
|
isRoot
|
|
374
386
|
? {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
)
|
|
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 »"
|
|
383
394
|
),
|
|
384
|
-
|
|
395
|
+
script(
|
|
396
|
+
domReady(
|
|
397
|
+
`$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
),
|
|
401
|
+
}
|
|
385
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
|
+
},
|
|
386
419
|
],
|
|
387
420
|
},
|
|
388
421
|
});
|
|
@@ -458,6 +491,103 @@ router.get(
|
|
|
458
491
|
})
|
|
459
492
|
);
|
|
460
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
|
+
);
|
|
461
591
|
router.get(
|
|
462
592
|
"/auto-backup-download/:filename",
|
|
463
593
|
isAdmin,
|
|
@@ -484,9 +614,8 @@ router.get(
|
|
|
484
614
|
const autoBackupForm = (req) =>
|
|
485
615
|
new Form({
|
|
486
616
|
action: "/admin/set-auto-backup",
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
submitLabel: "Save settings",
|
|
617
|
+
onChange: `saveAndContinue(this);$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`,
|
|
618
|
+
noSubmitButton: true,
|
|
490
619
|
additionalButtons: [
|
|
491
620
|
{
|
|
492
621
|
label: "Backup now",
|
|
@@ -535,6 +664,43 @@ const autoBackupForm = (req) =>
|
|
|
535
664
|
],
|
|
536
665
|
});
|
|
537
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
|
+
);
|
|
538
704
|
router.post(
|
|
539
705
|
"/set-auto-backup",
|
|
540
706
|
isAdmin,
|
|
@@ -555,7 +721,8 @@ router.post(
|
|
|
555
721
|
} else {
|
|
556
722
|
await save_config_from_form(form);
|
|
557
723
|
req.flash("success", req.__("Backup settings updated"));
|
|
558
|
-
res.redirect("/admin/backup");
|
|
724
|
+
if (!req.xhr) res.redirect("/admin/backup");
|
|
725
|
+
else res.json({ success: "ok" });
|
|
559
726
|
}
|
|
560
727
|
})
|
|
561
728
|
);
|
|
@@ -573,6 +740,22 @@ router.post(
|
|
|
573
740
|
})
|
|
574
741
|
);
|
|
575
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
|
+
|
|
576
759
|
/**
|
|
577
760
|
* @name get/system
|
|
578
761
|
* @function
|
|
@@ -648,47 +831,47 @@ router.get(
|
|
|
648
831
|
th(req.__("Saltcorn version")),
|
|
649
832
|
td(
|
|
650
833
|
packagejson.version +
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
|
662
845
|
? span(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
+
)
|
|
675
858
|
: "")
|
|
676
859
|
)
|
|
677
860
|
),
|
|
678
861
|
git_commit &&
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
)
|
|
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)
|
|
690
872
|
)
|
|
691
|
-
)
|
|
873
|
+
)
|
|
874
|
+
),
|
|
692
875
|
tr(th(req.__("Node.js version")), td(process.version)),
|
|
693
876
|
tr(
|
|
694
877
|
th(req.__("Database")),
|
|
@@ -798,7 +981,7 @@ router.post(
|
|
|
798
981
|
res.attachment(fileName);
|
|
799
982
|
const file = fs.createReadStream(fileName);
|
|
800
983
|
file.on("end", function () {
|
|
801
|
-
fs.unlink(fileName, function () {});
|
|
984
|
+
fs.unlink(fileName, function () { });
|
|
802
985
|
});
|
|
803
986
|
file.pipe(res);
|
|
804
987
|
})
|
|
@@ -821,7 +1004,7 @@ router.post(
|
|
|
821
1004
|
);
|
|
822
1005
|
if (err) req.flash("error", err);
|
|
823
1006
|
else req.flash("success", req.__("Successfully restored backup"));
|
|
824
|
-
fs.unlink(newPath, function () {});
|
|
1007
|
+
fs.unlink(newPath, function () { });
|
|
825
1008
|
res.redirect(`/admin`);
|
|
826
1009
|
})
|
|
827
1010
|
);
|
|
@@ -896,7 +1079,7 @@ const clearAllForm = (req) =>
|
|
|
896
1079
|
{
|
|
897
1080
|
type: "Bool",
|
|
898
1081
|
name: "plugins",
|
|
899
|
-
label: req.__("
|
|
1082
|
+
label: req.__("Modules"),
|
|
900
1083
|
default: true,
|
|
901
1084
|
},
|
|
902
1085
|
],
|
|
@@ -966,8 +1149,8 @@ router.post(
|
|
|
966
1149
|
req.__(
|
|
967
1150
|
"LetsEncrypt SSL enabled. Restart for changes to take effect."
|
|
968
1151
|
) +
|
|
969
|
-
|
|
970
|
-
|
|
1152
|
+
" " +
|
|
1153
|
+
a({ href: "/admin/system" }, req.__("Restart here"))
|
|
971
1154
|
);
|
|
972
1155
|
res.redirect("/useradmin/ssl");
|
|
973
1156
|
} catch (e) {
|
|
@@ -1038,13 +1221,13 @@ router.get(
|
|
|
1038
1221
|
contents: div(
|
|
1039
1222
|
pass
|
|
1040
1223
|
? div(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
)
|
|
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")
|
|
1047
1229
|
)
|
|
1230
|
+
)
|
|
1048
1231
|
: errors.map(mkError)
|
|
1049
1232
|
),
|
|
1050
1233
|
},
|
|
@@ -1058,19 +1241,66 @@ router.get(
|
|
|
1058
1241
|
})
|
|
1059
1242
|
);
|
|
1060
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
|
+
|
|
1061
1288
|
router.get(
|
|
1062
1289
|
"/build-mobile-app",
|
|
1063
1290
|
isAdmin,
|
|
1064
1291
|
error_catcher(async (req, res) => {
|
|
1065
|
-
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
1066
1292
|
const views = await View.find();
|
|
1067
|
-
const
|
|
1068
|
-
"This is still under development and might run longer.";
|
|
1293
|
+
const pages = await Page.find();
|
|
1069
1294
|
|
|
1070
1295
|
send_admin_page({
|
|
1071
1296
|
res,
|
|
1072
1297
|
req,
|
|
1073
1298
|
active_sub: "Mobile app",
|
|
1299
|
+
headers: [
|
|
1300
|
+
{
|
|
1301
|
+
headerTag: buildDialogScript(),
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1074
1304
|
contents: {
|
|
1075
1305
|
above: [
|
|
1076
1306
|
{
|
|
@@ -1088,34 +1318,77 @@ router.get(
|
|
|
1088
1318
|
name: "_csrf",
|
|
1089
1319
|
value: req.csrfToken(),
|
|
1090
1320
|
}),
|
|
1321
|
+
input({
|
|
1322
|
+
type: "hidden",
|
|
1323
|
+
name: "entryPointType",
|
|
1324
|
+
value: "view",
|
|
1325
|
+
id: "entryPointTypeID",
|
|
1326
|
+
}),
|
|
1091
1327
|
div(
|
|
1092
1328
|
{ class: "container ps-2" },
|
|
1093
1329
|
div(
|
|
1094
1330
|
{ class: "row pb-2" },
|
|
1095
|
-
div({ class: "col-sm-4 fw-bold" }, "Entry
|
|
1096
|
-
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")),
|
|
1097
1333
|
div(
|
|
1098
1334
|
{
|
|
1099
1335
|
class: "col-sm-1 fw-bold d-flex justify-content-center",
|
|
1100
1336
|
},
|
|
1101
|
-
"docker"
|
|
1337
|
+
req.__("docker")
|
|
1102
1338
|
)
|
|
1103
1339
|
),
|
|
1104
1340
|
div(
|
|
1105
1341
|
{ class: "row" },
|
|
1106
1342
|
div(
|
|
1107
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
|
|
1108
1369
|
select(
|
|
1109
1370
|
{
|
|
1110
1371
|
class: "form-control",
|
|
1111
|
-
name: "
|
|
1112
|
-
id: "
|
|
1372
|
+
name: "entryPoint",
|
|
1373
|
+
id: "viewInputID",
|
|
1113
1374
|
},
|
|
1114
1375
|
views
|
|
1115
1376
|
.map((view) =>
|
|
1116
1377
|
option({ value: view.name }, view.name)
|
|
1117
1378
|
)
|
|
1118
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(",")
|
|
1119
1392
|
)
|
|
1120
1393
|
),
|
|
1121
1394
|
div(
|
|
@@ -1125,7 +1398,7 @@ router.get(
|
|
|
1125
1398
|
{ class: "container ps-0" },
|
|
1126
1399
|
div(
|
|
1127
1400
|
{ class: "row" },
|
|
1128
|
-
div({ class: "col-sm-8" }, "android"),
|
|
1401
|
+
div({ class: "col-sm-8" }, req.__("android")),
|
|
1129
1402
|
div(
|
|
1130
1403
|
{ class: "col-sm" },
|
|
1131
1404
|
input({
|
|
@@ -1138,7 +1411,7 @@ router.get(
|
|
|
1138
1411
|
),
|
|
1139
1412
|
div(
|
|
1140
1413
|
{ class: "row" },
|
|
1141
|
-
div({ class: "col-sm-8" }, "iOS"),
|
|
1414
|
+
div({ class: "col-sm-8" }, req.__("iOS")),
|
|
1142
1415
|
div(
|
|
1143
1416
|
{ class: "col-sm" },
|
|
1144
1417
|
input({
|
|
@@ -1170,7 +1443,7 @@ router.get(
|
|
|
1170
1443
|
for: "appNameInputId",
|
|
1171
1444
|
class: "form-label fw-bold",
|
|
1172
1445
|
},
|
|
1173
|
-
"App file"
|
|
1446
|
+
req.__("App file")
|
|
1174
1447
|
),
|
|
1175
1448
|
input({
|
|
1176
1449
|
type: "text",
|
|
@@ -1190,14 +1463,14 @@ router.get(
|
|
|
1190
1463
|
for: "serverURLInputId",
|
|
1191
1464
|
class: "form-label fw-bold",
|
|
1192
1465
|
},
|
|
1193
|
-
"Server URL"
|
|
1466
|
+
req.__("Server URL")
|
|
1194
1467
|
),
|
|
1195
1468
|
input({
|
|
1196
1469
|
type: "text",
|
|
1197
1470
|
class: "form-control",
|
|
1198
1471
|
name: "serverURL",
|
|
1199
1472
|
id: "serverURLInputId",
|
|
1200
|
-
placeholder:
|
|
1473
|
+
placeholder: getState().getConfig("base_url") || "",
|
|
1201
1474
|
})
|
|
1202
1475
|
)
|
|
1203
1476
|
)
|
|
@@ -1205,12 +1478,12 @@ router.get(
|
|
|
1205
1478
|
button(
|
|
1206
1479
|
{
|
|
1207
1480
|
type: "submit",
|
|
1208
|
-
onClick: `
|
|
1481
|
+
onClick: `handleMessages(); press_store_button(this);`,
|
|
1209
1482
|
class: "btn btn-warning",
|
|
1210
1483
|
},
|
|
1211
1484
|
i({ class: "fas fa-hammer pe-2" }),
|
|
1212
1485
|
|
|
1213
|
-
"Build mobile app"
|
|
1486
|
+
req.__("Build mobile app")
|
|
1214
1487
|
)
|
|
1215
1488
|
)
|
|
1216
1489
|
),
|
|
@@ -1226,7 +1499,8 @@ router.post(
|
|
|
1226
1499
|
isAdmin,
|
|
1227
1500
|
error_catcher(async (req, res) => {
|
|
1228
1501
|
let {
|
|
1229
|
-
|
|
1502
|
+
entryPoint,
|
|
1503
|
+
entryPointType,
|
|
1230
1504
|
androidPlatform,
|
|
1231
1505
|
iOSPlatform,
|
|
1232
1506
|
useDocker,
|
|
@@ -1244,12 +1518,20 @@ router.post(
|
|
|
1244
1518
|
req.flash("error", req.__("Only the android build supports docker."));
|
|
1245
1519
|
return res.redirect("/admin/build-mobile-app");
|
|
1246
1520
|
}
|
|
1247
|
-
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
|
+
}
|
|
1248
1528
|
const appOut = path.join(__dirname, "..", "mobile-app-out");
|
|
1249
1529
|
const spawnParams = [
|
|
1250
1530
|
"build-app",
|
|
1251
|
-
"-
|
|
1252
|
-
|
|
1531
|
+
"-e",
|
|
1532
|
+
entryPoint,
|
|
1533
|
+
"-t",
|
|
1534
|
+
entryPointType,
|
|
1253
1535
|
"-c",
|
|
1254
1536
|
appOut,
|
|
1255
1537
|
"-b",
|
|
@@ -1257,7 +1539,13 @@ router.post(
|
|
|
1257
1539
|
];
|
|
1258
1540
|
if (useDocker) spawnParams.push("-d");
|
|
1259
1541
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
1260
|
-
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
|
+
}
|
|
1261
1549
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1262
1550
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1263
1551
|
const child = spawn("saltcorn", spawnParams, {
|
|
@@ -1275,10 +1563,13 @@ router.post(
|
|
|
1275
1563
|
});
|
|
1276
1564
|
child.on("exit", async function (exitCode, signal) {
|
|
1277
1565
|
if (exitCode === 0) {
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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
|
+
)
|
|
1282
1573
|
);
|
|
1283
1574
|
res.sendWrap(req.__(`Admin`), {
|
|
1284
1575
|
above: [
|
|
@@ -1287,7 +1578,7 @@ router.post(
|
|
|
1287
1578
|
title: req.__("Build Result"),
|
|
1288
1579
|
contents: div("The build was successfully"),
|
|
1289
1580
|
},
|
|
1290
|
-
app_files_table(
|
|
1581
|
+
files.length > 0 ? app_files_table(files, req) : "",
|
|
1291
1582
|
],
|
|
1292
1583
|
});
|
|
1293
1584
|
} else
|