@saltcorn/server 0.8.6-beta.16 → 0.8.6-beta.18
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 +6 -1
- package/package.json +9 -9
- package/public/cm_modes/handlebars.js +70 -0
- package/public/cm_modes/htmlmixed.js +153 -0
- package/routes/admin.js +98 -12
- package/routes/tables.js +9 -0
- package/s3storage.js +1 -0
- package/serve.js +24 -0
package/locales/en.json
CHANGED
|
@@ -1167,5 +1167,10 @@
|
|
|
1167
1167
|
"Migrations": "Migrations",
|
|
1168
1168
|
"Tag Entries": "Tag Entries",
|
|
1169
1169
|
"Not a valid field name": "Not a valid field name",
|
|
1170
|
-
"Set a default value for missing data": "Set a default value for missing data"
|
|
1170
|
+
"Set a default value for missing data": "Set a default value for missing data",
|
|
1171
|
+
"Table triggers: ": "Table triggers: ",
|
|
1172
|
+
"App name": "App name",
|
|
1173
|
+
"App icon": "App icon",
|
|
1174
|
+
"Splash Page": "Splash Page",
|
|
1175
|
+
"App version": "App version"
|
|
1171
1176
|
}
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.8.6-beta.
|
|
3
|
+
"version": "0.8.6-beta.18",
|
|
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.8.6-beta.
|
|
10
|
-
"@saltcorn/builder": "0.8.6-beta.
|
|
11
|
-
"@saltcorn/data": "0.8.6-beta.
|
|
12
|
-
"@saltcorn/admin-models": "0.8.6-beta.
|
|
13
|
-
"@saltcorn/filemanager": "0.8.6-beta.
|
|
14
|
-
"@saltcorn/markup": "0.8.6-beta.
|
|
15
|
-
"@saltcorn/sbadmin2": "0.8.6-beta.
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.6-beta.18",
|
|
10
|
+
"@saltcorn/builder": "0.8.6-beta.18",
|
|
11
|
+
"@saltcorn/data": "0.8.6-beta.18",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.6-beta.18",
|
|
13
|
+
"@saltcorn/filemanager": "0.8.6-beta.18",
|
|
14
|
+
"@saltcorn/markup": "0.8.6-beta.18",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.6-beta.18",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"adm-zip": "0.5.10",
|
|
19
|
-
"aws-sdk": "^2.
|
|
19
|
+
"aws-sdk": "^2.1386.0",
|
|
20
20
|
"connect-flash": "^0.1.1",
|
|
21
21
|
"connect-pg-simple": "^6.1.0",
|
|
22
22
|
"content-disposition": "^0.5.3",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2
|
+
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
3
|
+
|
|
4
|
+
(function(mod) {
|
|
5
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
6
|
+
mod(require("../../lib/codemirror"), require("../../addon/mode/simple"), require("../../addon/mode/multiplex"));
|
|
7
|
+
else if (typeof define == "function" && define.amd) // AMD
|
|
8
|
+
define(["../../lib/codemirror", "../../addon/mode/simple", "../../addon/mode/multiplex"], mod);
|
|
9
|
+
else // Plain browser env
|
|
10
|
+
mod(CodeMirror);
|
|
11
|
+
})(function(CodeMirror) {
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
CodeMirror.defineSimpleMode("handlebars-tags", {
|
|
15
|
+
start: [
|
|
16
|
+
{ regex: /\{\{\{/, push: "handlebars_raw", token: "tag" },
|
|
17
|
+
{ regex: /\{\{!--/, push: "dash_comment", token: "comment" },
|
|
18
|
+
{ regex: /\{\{!/, push: "comment", token: "comment" },
|
|
19
|
+
{ regex: /\{\{/, push: "handlebars", token: "tag" }
|
|
20
|
+
],
|
|
21
|
+
handlebars_raw: [
|
|
22
|
+
{ regex: /\}\}\}/, pop: true, token: "tag" },
|
|
23
|
+
],
|
|
24
|
+
handlebars: [
|
|
25
|
+
{ regex: /\}\}/, pop: true, token: "tag" },
|
|
26
|
+
|
|
27
|
+
// Double and single quotes
|
|
28
|
+
{ regex: /"(?:[^\\"]|\\.)*"?/, token: "string" },
|
|
29
|
+
{ regex: /'(?:[^\\']|\\.)*'?/, token: "string" },
|
|
30
|
+
|
|
31
|
+
// Handlebars keywords
|
|
32
|
+
{ regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" },
|
|
33
|
+
{ regex: /(?:else|this)\b/, token: "keyword" },
|
|
34
|
+
|
|
35
|
+
// Numeral
|
|
36
|
+
{ regex: /\d+/i, token: "number" },
|
|
37
|
+
|
|
38
|
+
// Atoms like = and .
|
|
39
|
+
{ regex: /=|~|@|true|false/, token: "atom" },
|
|
40
|
+
|
|
41
|
+
// Paths
|
|
42
|
+
{ regex: /(?:\.\.\/)*(?:[A-Za-z_][\w\.]*)+/, token: "variable-2" }
|
|
43
|
+
],
|
|
44
|
+
dash_comment: [
|
|
45
|
+
{ regex: /--\}\}/, pop: true, token: "comment" },
|
|
46
|
+
|
|
47
|
+
// Commented code
|
|
48
|
+
{ regex: /./, token: "comment"}
|
|
49
|
+
],
|
|
50
|
+
comment: [
|
|
51
|
+
{ regex: /\}\}/, pop: true, token: "comment" },
|
|
52
|
+
{ regex: /./, token: "comment" }
|
|
53
|
+
],
|
|
54
|
+
meta: {
|
|
55
|
+
blockCommentStart: "{{--",
|
|
56
|
+
blockCommentEnd: "--}}"
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
CodeMirror.defineMode("handlebars", function(config, parserConfig) {
|
|
61
|
+
var handlebars = CodeMirror.getMode(config, "handlebars-tags");
|
|
62
|
+
if (!parserConfig || !parserConfig.base) return handlebars;
|
|
63
|
+
return CodeMirror.multiplexingMode(
|
|
64
|
+
CodeMirror.getMode(config, parserConfig.base),
|
|
65
|
+
{open: "{{", close: /\}\}\}?/, mode: handlebars, parseDelimiters: true}
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
CodeMirror.defineMIME("text/x-handlebars-template", "handlebars");
|
|
70
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2
|
+
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
3
|
+
|
|
4
|
+
(function(mod) {
|
|
5
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
6
|
+
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
|
|
7
|
+
else if (typeof define == "function" && define.amd) // AMD
|
|
8
|
+
define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
|
|
9
|
+
else // Plain browser env
|
|
10
|
+
mod(CodeMirror);
|
|
11
|
+
})(function(CodeMirror) {
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
var defaultTags = {
|
|
15
|
+
script: [
|
|
16
|
+
["lang", /(javascript|babel)/i, "javascript"],
|
|
17
|
+
["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
|
|
18
|
+
["type", /./, "text/plain"],
|
|
19
|
+
[null, null, "javascript"]
|
|
20
|
+
],
|
|
21
|
+
style: [
|
|
22
|
+
["lang", /^css$/i, "css"],
|
|
23
|
+
["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
|
|
24
|
+
["type", /./, "text/plain"],
|
|
25
|
+
[null, null, "css"]
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function maybeBackup(stream, pat, style) {
|
|
30
|
+
var cur = stream.current(), close = cur.search(pat);
|
|
31
|
+
if (close > -1) {
|
|
32
|
+
stream.backUp(cur.length - close);
|
|
33
|
+
} else if (cur.match(/<\/?$/)) {
|
|
34
|
+
stream.backUp(cur.length);
|
|
35
|
+
if (!stream.match(pat, false)) stream.match(cur);
|
|
36
|
+
}
|
|
37
|
+
return style;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var attrRegexpCache = {};
|
|
41
|
+
function getAttrRegexp(attr) {
|
|
42
|
+
var regexp = attrRegexpCache[attr];
|
|
43
|
+
if (regexp) return regexp;
|
|
44
|
+
return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getAttrValue(text, attr) {
|
|
48
|
+
var match = text.match(getAttrRegexp(attr))
|
|
49
|
+
return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getTagRegexp(tagName, anchored) {
|
|
53
|
+
return new RegExp((anchored ? "^" : "") + "<\/\\s*" + tagName + "\\s*>", "i");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function addTags(from, to) {
|
|
57
|
+
for (var tag in from) {
|
|
58
|
+
var dest = to[tag] || (to[tag] = []);
|
|
59
|
+
var source = from[tag];
|
|
60
|
+
for (var i = source.length - 1; i >= 0; i--)
|
|
61
|
+
dest.unshift(source[i])
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function findMatchingMode(tagInfo, tagText) {
|
|
66
|
+
for (var i = 0; i < tagInfo.length; i++) {
|
|
67
|
+
var spec = tagInfo[i];
|
|
68
|
+
if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
|
|
73
|
+
var htmlMode = CodeMirror.getMode(config, {
|
|
74
|
+
name: "xml",
|
|
75
|
+
htmlMode: true,
|
|
76
|
+
multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
|
|
77
|
+
multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag,
|
|
78
|
+
allowMissingTagName: parserConfig.allowMissingTagName,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
var tags = {};
|
|
82
|
+
var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
|
|
83
|
+
addTags(defaultTags, tags);
|
|
84
|
+
if (configTags) addTags(configTags, tags);
|
|
85
|
+
if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
|
|
86
|
+
tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
|
|
87
|
+
|
|
88
|
+
function html(stream, state) {
|
|
89
|
+
var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
|
|
90
|
+
if (tag && !/[<>\s\/]/.test(stream.current()) &&
|
|
91
|
+
(tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
|
|
92
|
+
tags.hasOwnProperty(tagName)) {
|
|
93
|
+
state.inTag = tagName + " "
|
|
94
|
+
} else if (state.inTag && tag && />$/.test(stream.current())) {
|
|
95
|
+
var inTag = /^([\S]+) (.*)/.exec(state.inTag)
|
|
96
|
+
state.inTag = null
|
|
97
|
+
var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
|
|
98
|
+
var mode = CodeMirror.getMode(config, modeSpec)
|
|
99
|
+
var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
|
|
100
|
+
state.token = function (stream, state) {
|
|
101
|
+
if (stream.match(endTagA, false)) {
|
|
102
|
+
state.token = html;
|
|
103
|
+
state.localState = state.localMode = null;
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
|
|
107
|
+
};
|
|
108
|
+
state.localMode = mode;
|
|
109
|
+
state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
|
|
110
|
+
} else if (state.inTag) {
|
|
111
|
+
state.inTag += stream.current()
|
|
112
|
+
if (stream.eol()) state.inTag += " "
|
|
113
|
+
}
|
|
114
|
+
return style;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
startState: function () {
|
|
119
|
+
var state = CodeMirror.startState(htmlMode);
|
|
120
|
+
return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
copyState: function (state) {
|
|
124
|
+
var local;
|
|
125
|
+
if (state.localState) {
|
|
126
|
+
local = CodeMirror.copyState(state.localMode, state.localState);
|
|
127
|
+
}
|
|
128
|
+
return {token: state.token, inTag: state.inTag,
|
|
129
|
+
localMode: state.localMode, localState: local,
|
|
130
|
+
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
token: function (stream, state) {
|
|
134
|
+
return state.token(stream, state);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
indent: function (state, textAfter, line) {
|
|
138
|
+
if (!state.localMode || /^\s*<\//.test(textAfter))
|
|
139
|
+
return htmlMode.indent(state.htmlState, textAfter, line);
|
|
140
|
+
else if (state.localMode.indent)
|
|
141
|
+
return state.localMode.indent(state.localState, textAfter, line);
|
|
142
|
+
else
|
|
143
|
+
return CodeMirror.Pass;
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
innerMode: function (state) {
|
|
147
|
+
return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}, "xml", "javascript", "css");
|
|
151
|
+
|
|
152
|
+
CodeMirror.defineMIME("text/html", "htmlmixed");
|
|
153
|
+
});
|
package/routes/admin.js
CHANGED
|
@@ -986,7 +986,7 @@ router.post(
|
|
|
986
986
|
"npm",
|
|
987
987
|
["install", "-g", "@saltcorn/cli@latest", "--unsafe"],
|
|
988
988
|
{
|
|
989
|
-
stdio: ["ignore", "pipe",
|
|
989
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
990
990
|
}
|
|
991
991
|
);
|
|
992
992
|
child.stdout.on("data", (data) => {
|
|
@@ -1387,6 +1387,9 @@ router.get(
|
|
|
1387
1387
|
error_catcher(async (req, res) => {
|
|
1388
1388
|
const views = await View.find();
|
|
1389
1389
|
const pages = await Page.find();
|
|
1390
|
+
const images = (await File.find({ mime_super: "image" })).filter((image) =>
|
|
1391
|
+
image.filename?.endsWith(".png")
|
|
1392
|
+
);
|
|
1390
1393
|
|
|
1391
1394
|
send_admin_page({
|
|
1392
1395
|
res,
|
|
@@ -1464,7 +1467,7 @@ router.get(
|
|
|
1464
1467
|
// select entry-view
|
|
1465
1468
|
select(
|
|
1466
1469
|
{
|
|
1467
|
-
class: "form-
|
|
1470
|
+
class: "form-select",
|
|
1468
1471
|
name: "entryPoint",
|
|
1469
1472
|
id: "viewInputID",
|
|
1470
1473
|
},
|
|
@@ -1477,14 +1480,14 @@ router.get(
|
|
|
1477
1480
|
// select entry-page
|
|
1478
1481
|
select(
|
|
1479
1482
|
{
|
|
1480
|
-
class: "form-
|
|
1483
|
+
class: "form-select d-none",
|
|
1481
1484
|
id: "pageInputID",
|
|
1482
1485
|
},
|
|
1483
1486
|
pages
|
|
1484
1487
|
.map((page) =>
|
|
1485
1488
|
option({ value: page.name }, page.name)
|
|
1486
1489
|
)
|
|
1487
|
-
.join("
|
|
1490
|
+
.join("")
|
|
1488
1491
|
)
|
|
1489
1492
|
),
|
|
1490
1493
|
div(
|
|
@@ -1530,28 +1533,51 @@ router.get(
|
|
|
1530
1533
|
})
|
|
1531
1534
|
)
|
|
1532
1535
|
),
|
|
1536
|
+
// app name
|
|
1533
1537
|
div(
|
|
1534
1538
|
{ class: "row pb-2" },
|
|
1535
1539
|
div(
|
|
1536
1540
|
{ class: "col-sm-8" },
|
|
1537
1541
|
label(
|
|
1538
1542
|
{
|
|
1539
|
-
for: "
|
|
1543
|
+
for: "appFileInputId",
|
|
1540
1544
|
class: "form-label fw-bold",
|
|
1541
1545
|
},
|
|
1542
|
-
req.__("App
|
|
1546
|
+
req.__("App name")
|
|
1543
1547
|
),
|
|
1544
1548
|
input({
|
|
1545
1549
|
type: "text",
|
|
1546
1550
|
class: "form-control",
|
|
1547
|
-
name: "
|
|
1548
|
-
id: "
|
|
1549
|
-
placeholder: "
|
|
1551
|
+
name: "appName",
|
|
1552
|
+
id: "appNameInputId",
|
|
1553
|
+
placeholder: "SaltcornMobileApp",
|
|
1550
1554
|
})
|
|
1551
1555
|
)
|
|
1552
1556
|
),
|
|
1557
|
+
// app version
|
|
1553
1558
|
div(
|
|
1554
|
-
{ class: "row pb-
|
|
1559
|
+
{ class: "row pb-2" },
|
|
1560
|
+
div(
|
|
1561
|
+
{ class: "col-sm-8" },
|
|
1562
|
+
label(
|
|
1563
|
+
{
|
|
1564
|
+
for: "appVersionInputId",
|
|
1565
|
+
class: "form-label fw-bold",
|
|
1566
|
+
},
|
|
1567
|
+
req.__("App version")
|
|
1568
|
+
),
|
|
1569
|
+
input({
|
|
1570
|
+
type: "text",
|
|
1571
|
+
class: "form-control",
|
|
1572
|
+
name: "appVersion",
|
|
1573
|
+
id: "appVersionInputId",
|
|
1574
|
+
placeholder: "1.0.0",
|
|
1575
|
+
})
|
|
1576
|
+
)
|
|
1577
|
+
),
|
|
1578
|
+
// server url
|
|
1579
|
+
div(
|
|
1580
|
+
{ class: "row pb-2" },
|
|
1555
1581
|
div(
|
|
1556
1582
|
{ class: "col-sm-8" },
|
|
1557
1583
|
label(
|
|
@@ -1570,6 +1596,60 @@ router.get(
|
|
|
1570
1596
|
})
|
|
1571
1597
|
)
|
|
1572
1598
|
),
|
|
1599
|
+
// app icon
|
|
1600
|
+
div(
|
|
1601
|
+
{ class: "row pb-2" },
|
|
1602
|
+
div(
|
|
1603
|
+
{ class: "col-sm-8" },
|
|
1604
|
+
label(
|
|
1605
|
+
{
|
|
1606
|
+
for: "appIconInputId",
|
|
1607
|
+
class: "form-label fw-bold",
|
|
1608
|
+
},
|
|
1609
|
+
req.__("App icon")
|
|
1610
|
+
),
|
|
1611
|
+
select(
|
|
1612
|
+
{
|
|
1613
|
+
class: "form-select",
|
|
1614
|
+
name: "appIcon",
|
|
1615
|
+
id: "appIconInputId",
|
|
1616
|
+
},
|
|
1617
|
+
[
|
|
1618
|
+
option({ value: "" }, ""),
|
|
1619
|
+
...images.map((image) =>
|
|
1620
|
+
option({ value: image.location }, image.filename)
|
|
1621
|
+
),
|
|
1622
|
+
].join("")
|
|
1623
|
+
)
|
|
1624
|
+
)
|
|
1625
|
+
),
|
|
1626
|
+
div(
|
|
1627
|
+
{ class: "row pb-3" },
|
|
1628
|
+
div(
|
|
1629
|
+
{ class: "col-sm-8" },
|
|
1630
|
+
label(
|
|
1631
|
+
{
|
|
1632
|
+
for: "splashPageInputId",
|
|
1633
|
+
class: "form-label fw-bold",
|
|
1634
|
+
},
|
|
1635
|
+
req.__("Splash Page")
|
|
1636
|
+
),
|
|
1637
|
+
select(
|
|
1638
|
+
{
|
|
1639
|
+
class: "form-select",
|
|
1640
|
+
name: "splashPage",
|
|
1641
|
+
id: "splashPageInputId",
|
|
1642
|
+
},
|
|
1643
|
+
[
|
|
1644
|
+
option({ value: "" }, ""),
|
|
1645
|
+
...pages.map((page) =>
|
|
1646
|
+
option({ value: page.name }, page.name)
|
|
1647
|
+
),
|
|
1648
|
+
].join("")
|
|
1649
|
+
)
|
|
1650
|
+
)
|
|
1651
|
+
),
|
|
1652
|
+
|
|
1573
1653
|
div(
|
|
1574
1654
|
// TODO only for some tables?
|
|
1575
1655
|
{ class: "row pb-2" },
|
|
@@ -1688,8 +1768,11 @@ router.post(
|
|
|
1688
1768
|
androidPlatform,
|
|
1689
1769
|
iOSPlatform,
|
|
1690
1770
|
useDocker,
|
|
1691
|
-
|
|
1771
|
+
appName,
|
|
1772
|
+
appVersion,
|
|
1773
|
+
appIcon,
|
|
1692
1774
|
serverURL,
|
|
1775
|
+
splashPage,
|
|
1693
1776
|
allowOfflineMode,
|
|
1694
1777
|
} = req.body;
|
|
1695
1778
|
if (!androidPlatform && !iOSPlatform) {
|
|
@@ -1736,8 +1819,11 @@ router.post(
|
|
|
1736
1819
|
spawnParams.push("--buildForEmulator");
|
|
1737
1820
|
}
|
|
1738
1821
|
}
|
|
1739
|
-
if (
|
|
1822
|
+
if (appName) spawnParams.push("--appName", appName);
|
|
1823
|
+
if (appVersion) spawnParams.push("--appVersion", appVersion);
|
|
1824
|
+
if (appIcon) spawnParams.push("--appIcon", appIcon);
|
|
1740
1825
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1826
|
+
if (splashPage) spawnParams.push("--splashPage", splashPage);
|
|
1741
1827
|
if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
|
|
1742
1828
|
if (
|
|
1743
1829
|
db.is_it_multi_tenant() &&
|
package/routes/tables.js
CHANGED
|
@@ -11,6 +11,7 @@ const Table = require("@saltcorn/data/models/table");
|
|
|
11
11
|
const File = require("@saltcorn/data/models/file");
|
|
12
12
|
const View = require("@saltcorn/data/models/view");
|
|
13
13
|
const User = require("@saltcorn/data/models/user");
|
|
14
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
14
15
|
const {
|
|
15
16
|
mkTable,
|
|
16
17
|
renderForm,
|
|
@@ -579,6 +580,7 @@ router.get(
|
|
|
579
580
|
const inbound_refs = [
|
|
580
581
|
...new Set(child_relations.map(({ table }) => table.name)),
|
|
581
582
|
];
|
|
583
|
+
const triggers = table.id ? Trigger.find({ table_id: table.id }) : [];
|
|
582
584
|
let fieldCard;
|
|
583
585
|
if (fields.length === 0) {
|
|
584
586
|
fieldCard = [
|
|
@@ -647,6 +649,13 @@ router.get(
|
|
|
647
649
|
inbound_refs.map((tnm) => link(`/table/${tnm}`, tnm)).join(", ") +
|
|
648
650
|
"<br>"
|
|
649
651
|
: "",
|
|
652
|
+
triggers.length
|
|
653
|
+
? req.__("Table triggers: ") +
|
|
654
|
+
triggers
|
|
655
|
+
.map((t) => link(`/actions/configure/${t.id}`, t.name))
|
|
656
|
+
.join(", ") +
|
|
657
|
+
"<br>"
|
|
658
|
+
: "",
|
|
650
659
|
!table.external &&
|
|
651
660
|
a(
|
|
652
661
|
{
|
package/s3storage.js
CHANGED
package/serve.js
CHANGED
|
@@ -8,6 +8,7 @@ const runScheduler = require("@saltcorn/data/models/scheduler");
|
|
|
8
8
|
const User = require("@saltcorn/data/models/user");
|
|
9
9
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
10
10
|
const db = require("@saltcorn/data/db");
|
|
11
|
+
const { getConfigFile, configFilePath } = require("@saltcorn/data/db/connect");
|
|
11
12
|
const {
|
|
12
13
|
getState,
|
|
13
14
|
init_multi_tenant,
|
|
@@ -49,11 +50,33 @@ const {
|
|
|
49
50
|
} = require("@saltcorn/admin-models/models/tenant");
|
|
50
51
|
const { auto_backup_now } = require("@saltcorn/admin-models/models/backup");
|
|
51
52
|
const Snapshot = require("@saltcorn/admin-models/models/snapshot");
|
|
53
|
+
const { writeFileSync } = require("fs");
|
|
52
54
|
|
|
53
55
|
const take_snapshot = async () => {
|
|
54
56
|
return await Snapshot.take_if_changed();
|
|
55
57
|
};
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Ensure the cfg file has a jwt_secret
|
|
61
|
+
*/
|
|
62
|
+
const ensureJwtSecret = () => {
|
|
63
|
+
const cfg = getConfigFile();
|
|
64
|
+
if (cfg && !cfg.jwt_secret) {
|
|
65
|
+
try {
|
|
66
|
+
const newSecret = require("crypto").randomBytes(64).toString("hex");
|
|
67
|
+
cfg.jwt_secret = newSecret;
|
|
68
|
+
writeFileSync(configFilePath, JSON.stringify(cfg, null, 2));
|
|
69
|
+
db.connectObj.jwt_secret = newSecret;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.log(
|
|
72
|
+
`Unable to set a jwt_secret: ${
|
|
73
|
+
error.message ? error.message : "Unknown error"
|
|
74
|
+
}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
57
80
|
// helpful https://gist.github.com/jpoehls/2232358
|
|
58
81
|
/**
|
|
59
82
|
* @param {object} opts
|
|
@@ -195,6 +218,7 @@ module.exports =
|
|
|
195
218
|
dev,
|
|
196
219
|
...appargs
|
|
197
220
|
} = {}) => {
|
|
221
|
+
ensureJwtSecret();
|
|
198
222
|
process.on("unhandledRejection", (reason, p) => {
|
|
199
223
|
console.error(reason, "Unhandled Rejection at Promise");
|
|
200
224
|
});
|