@saltcorn/server 0.8.1-rc.2 → 0.8.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 +3 -4
- package/auth/routes.js +1 -0
- package/auth/testhelp.js +5 -3
- package/locales/da.json +4 -1
- package/locales/en.json +6 -1
- package/package.json +8 -8
- package/routes/admin.js +31 -6
- package/routes/fields.js +2 -1
- package/routes/files.js +7 -0
- package/routes/tables.js +16 -4
- package/routes/utils.js +12 -3
- package/routes/viewedit.js +1 -1
- package/serve.js +1 -1
package/auth/admin.js
CHANGED
|
@@ -336,6 +336,7 @@ const http_settings_form = async (req) =>
|
|
|
336
336
|
"cookie_duration",
|
|
337
337
|
"cookie_duration_remember",
|
|
338
338
|
"cookie_sessions",
|
|
339
|
+
"public_cache_maxage",
|
|
339
340
|
"custom_http_headers",
|
|
340
341
|
],
|
|
341
342
|
action: "/useradmin/http",
|
|
@@ -753,9 +754,7 @@ router.get(
|
|
|
753
754
|
for (const table of tables) {
|
|
754
755
|
if (table.external) continue;
|
|
755
756
|
const fields = await table.getFields();
|
|
756
|
-
const
|
|
757
|
-
.filter((f) => f.reftable_name === "users")
|
|
758
|
-
.map((f) => ({ value: f.id, label: f.name }));
|
|
757
|
+
const ownership_opts = await table.ownership_options();
|
|
759
758
|
const form = new Form({
|
|
760
759
|
action: "/table",
|
|
761
760
|
noSubmitButton: true,
|
|
@@ -770,7 +769,7 @@ router.get(
|
|
|
770
769
|
input_type: "select",
|
|
771
770
|
options: [
|
|
772
771
|
{ value: "", label: req.__("None") },
|
|
773
|
-
...
|
|
772
|
+
...ownership_opts,
|
|
774
773
|
{ value: "_formula", label: req.__("Formula") },
|
|
775
774
|
],
|
|
776
775
|
},
|
package/auth/routes.js
CHANGED
|
@@ -1061,6 +1061,7 @@ router.post(
|
|
|
1061
1061
|
else req.session.cookie.expires = false;
|
|
1062
1062
|
}
|
|
1063
1063
|
Trigger.emitEvent("Login", null, req.user);
|
|
1064
|
+
res?.cookie?.("loggedin", "true");
|
|
1064
1065
|
req.flash("success", req.__("Welcome, %s!", req.user.email));
|
|
1065
1066
|
if (req.smr) {
|
|
1066
1067
|
const dbUser = await User.findOne({ id: req.user.id });
|
package/auth/testhelp.js
CHANGED
|
@@ -88,6 +88,9 @@ const toNotInclude =
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
const resToLoginCookie = (res) =>
|
|
92
|
+
res.headers["set-cookie"].find((c) => c.includes("connect.sid"));
|
|
93
|
+
|
|
91
94
|
/**
|
|
92
95
|
*
|
|
93
96
|
* @returns {Promise<void>}
|
|
@@ -99,7 +102,7 @@ const getStaffLoginCookie = async () => {
|
|
|
99
102
|
.send("email=staff@foo.com")
|
|
100
103
|
.send("password=ghrarhr54hg");
|
|
101
104
|
if (res.statusCode !== 302) console.log(res.text);
|
|
102
|
-
return res
|
|
105
|
+
return resToLoginCookie(res);
|
|
103
106
|
};
|
|
104
107
|
|
|
105
108
|
/**
|
|
@@ -113,8 +116,7 @@ const getAdminLoginCookie = async () => {
|
|
|
113
116
|
.send("email=admin@foo.com")
|
|
114
117
|
.send("password=AhGGr6rhu45");
|
|
115
118
|
if (res.statusCode !== 302) console.log(res.text);
|
|
116
|
-
|
|
117
|
-
return res.headers["set-cookie"][0];
|
|
119
|
+
return resToLoginCookie(res);
|
|
118
120
|
};
|
|
119
121
|
|
|
120
122
|
/**
|
package/locales/da.json
CHANGED
|
@@ -555,5 +555,8 @@
|
|
|
555
555
|
"Create database view": "Create database view",
|
|
556
556
|
"Create an SQL view in the database with the fields in this list": "Create an SQL view in the database with the fields in this list",
|
|
557
557
|
"Rows per page": "Rows per page",
|
|
558
|
-
"List options": "List options"
|
|
558
|
+
"List options": "List options",
|
|
559
|
+
"Modules": "Modules",
|
|
560
|
+
"File not found": "File not found",
|
|
561
|
+
"Welcome, %s!": "Welcome, %s!"
|
|
559
562
|
}
|
package/locales/en.json
CHANGED
|
@@ -1077,5 +1077,10 @@
|
|
|
1077
1077
|
"Saved 2FA policy for role": "Saved 2FA policy for role",
|
|
1078
1078
|
"HTTP settings updated": "HTTP settings updated",
|
|
1079
1079
|
"%s configuration": "%s configuration",
|
|
1080
|
-
"Save indicator": "Save indicator"
|
|
1080
|
+
"Save indicator": "Save indicator",
|
|
1081
|
+
"Public cache TTL (minutes)": "Public cache TTL (minutes)",
|
|
1082
|
+
"Cache-control max-age for public views and pages. 0 to disable": "Cache-control max-age for public views and pages. 0 to disable",
|
|
1083
|
+
"Files accept filter": "Files accept filter",
|
|
1084
|
+
"User group": "User group",
|
|
1085
|
+
"Add relations to this table in dropdown options for ownership field": "Add relations to this table in dropdown options for ownership field"
|
|
1081
1086
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.8.1
|
|
3
|
+
"version": "0.8.1",
|
|
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.1
|
|
10
|
-
"@saltcorn/builder": "0.8.1
|
|
11
|
-
"@saltcorn/data": "0.8.1
|
|
12
|
-
"@saltcorn/admin-models": "0.8.1
|
|
13
|
-
"@saltcorn/filemanager": "0.8.1
|
|
14
|
-
"@saltcorn/markup": "0.8.1
|
|
15
|
-
"@saltcorn/sbadmin2": "0.8.1
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.1",
|
|
10
|
+
"@saltcorn/builder": "0.8.1",
|
|
11
|
+
"@saltcorn/data": "0.8.1",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.1",
|
|
13
|
+
"@saltcorn/filemanager": "0.8.1",
|
|
14
|
+
"@saltcorn/markup": "0.8.1",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.1",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"aws-sdk": "^2.1037.0",
|
package/routes/admin.js
CHANGED
|
@@ -97,6 +97,7 @@ const View = require("@saltcorn/data/models/view");
|
|
|
97
97
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
98
98
|
const os = require("os");
|
|
99
99
|
const Page = require("@saltcorn/data/models/page");
|
|
100
|
+
const { getSafeSaltcornCmd } = require("@saltcorn/data/utils");
|
|
100
101
|
|
|
101
102
|
const router = new Router();
|
|
102
103
|
module.exports = router;
|
|
@@ -1545,7 +1546,22 @@ router.get(
|
|
|
1545
1546
|
const checkFiles = async (outDir, fileNames) => {
|
|
1546
1547
|
const rootFolder = await File.rootFolder();
|
|
1547
1548
|
const mobile_app_dir = path.join(rootFolder.location, "mobile_app", outDir);
|
|
1548
|
-
const
|
|
1549
|
+
const unsafeFiles = await Promise.all(
|
|
1550
|
+
fs
|
|
1551
|
+
.readdirSync(mobile_app_dir)
|
|
1552
|
+
.map(
|
|
1553
|
+
async (outFile) => await File.from_file_on_disk(outFile, mobile_app_dir)
|
|
1554
|
+
)
|
|
1555
|
+
);
|
|
1556
|
+
const entries = unsafeFiles
|
|
1557
|
+
.filter(
|
|
1558
|
+
(file) =>
|
|
1559
|
+
file.user_id &&
|
|
1560
|
+
!isNaN(file.user_id) &&
|
|
1561
|
+
file.min_role_read &&
|
|
1562
|
+
!isNaN(file.min_role_read)
|
|
1563
|
+
)
|
|
1564
|
+
.map((file) => file.filename);
|
|
1549
1565
|
return fileNames.some((fileName) => entries.indexOf(fileName) >= 0);
|
|
1550
1566
|
};
|
|
1551
1567
|
|
|
@@ -1638,6 +1654,8 @@ router.post(
|
|
|
1638
1654
|
buildDir,
|
|
1639
1655
|
"-b",
|
|
1640
1656
|
`${os.userInfo().homedir}/mobile_app_build`,
|
|
1657
|
+
"-u",
|
|
1658
|
+
req.user.email, // ensured by isAdmin
|
|
1641
1659
|
];
|
|
1642
1660
|
if (useDocker) spawnParams.push("-d");
|
|
1643
1661
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
@@ -1659,7 +1677,7 @@ router.post(
|
|
|
1659
1677
|
// end http call, return the out directory name
|
|
1660
1678
|
// the gui polls for results
|
|
1661
1679
|
res.json({ build_dir_name: outDirName });
|
|
1662
|
-
const child = spawn(
|
|
1680
|
+
const child = spawn(getSafeSaltcornCmd(), spawnParams, {
|
|
1663
1681
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1664
1682
|
cwd: ".",
|
|
1665
1683
|
});
|
|
@@ -1672,29 +1690,36 @@ router.post(
|
|
|
1672
1690
|
// console.log(data.toString());
|
|
1673
1691
|
childOutputs.push(data ? data.toString() : req.__("An error occurred"));
|
|
1674
1692
|
});
|
|
1675
|
-
child.on("exit",
|
|
1693
|
+
child.on("exit", (exitCode, signal) => {
|
|
1676
1694
|
const logFile = exitCode === 0 ? "logs.txt" : "error_logs.txt";
|
|
1677
1695
|
fs.writeFile(
|
|
1678
1696
|
path.join(buildDir, logFile),
|
|
1679
1697
|
childOutputs.join("\n"),
|
|
1680
|
-
(error) => {
|
|
1698
|
+
async (error) => {
|
|
1681
1699
|
if (error) {
|
|
1682
1700
|
console.log(`unable to write '${logFile}' to '${buildDir}'`);
|
|
1683
1701
|
console.log(error);
|
|
1702
|
+
} else {
|
|
1703
|
+
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
1704
|
+
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
1684
1705
|
}
|
|
1685
1706
|
}
|
|
1686
1707
|
);
|
|
1687
1708
|
});
|
|
1688
|
-
child.on("error",
|
|
1709
|
+
child.on("error", (msg) => {
|
|
1689
1710
|
const message = msg.message ? msg.message : msg.code;
|
|
1690
1711
|
const stack = msg.stack ? msg.stack : "";
|
|
1712
|
+
const logFile = "error_logs.txt";
|
|
1691
1713
|
fs.writeFile(
|
|
1692
1714
|
path.join(buildDir, "error_logs.txt"),
|
|
1693
1715
|
[message, stack].join("\n"),
|
|
1694
|
-
(error) => {
|
|
1716
|
+
async (error) => {
|
|
1695
1717
|
if (error) {
|
|
1696
1718
|
console.log(`unable to write logFile to '${buildDir}'`);
|
|
1697
1719
|
console.log(error);
|
|
1720
|
+
} else {
|
|
1721
|
+
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
1722
|
+
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
1698
1723
|
}
|
|
1699
1724
|
}
|
|
1700
1725
|
);
|
package/routes/fields.js
CHANGED
|
@@ -996,7 +996,8 @@ router.post(
|
|
|
996
996
|
formStyle: "vert",
|
|
997
997
|
fields: formFields,
|
|
998
998
|
});
|
|
999
|
-
if (_columndef
|
|
999
|
+
if (_columndef && _columndef !== "undefined")
|
|
1000
|
+
form.values = JSON.parse(_columndef);
|
|
1000
1001
|
res.send(mkFormContentNoLayout(form));
|
|
1001
1002
|
})
|
|
1002
1003
|
);
|
package/routes/files.js
CHANGED
|
@@ -393,6 +393,13 @@ router.post(
|
|
|
393
393
|
f.s3_store ? s3storage.unlinkObject : undefined
|
|
394
394
|
);
|
|
395
395
|
if (result && result.error) {
|
|
396
|
+
if (req.xhr) {
|
|
397
|
+
const root = path.join(db.connectObj.file_store, db.getTenantSchema());
|
|
398
|
+
res.json({
|
|
399
|
+
error: result.error.replaceAll(root, ""),
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
396
403
|
req.flash("error", result.error);
|
|
397
404
|
}
|
|
398
405
|
res.redirect(`/files?dir=${encodeURIComponent(f.current_folder)}`);
|
package/routes/tables.js
CHANGED
|
@@ -73,9 +73,7 @@ const tableForm = async (table, req) => {
|
|
|
73
73
|
value: r.id,
|
|
74
74
|
label: r.role,
|
|
75
75
|
}));
|
|
76
|
-
const
|
|
77
|
-
.filter((f) => f.reftable_name === "users")
|
|
78
|
-
.map((f) => ({ value: f.id, label: f.name }));
|
|
76
|
+
const ownership_opts = await table.ownership_options();
|
|
79
77
|
const form = new Form({
|
|
80
78
|
action: "/table",
|
|
81
79
|
noSubmitButton: true,
|
|
@@ -92,7 +90,7 @@ const tableForm = async (table, req) => {
|
|
|
92
90
|
input_type: "select",
|
|
93
91
|
options: [
|
|
94
92
|
{ value: "", label: req.__("None") },
|
|
95
|
-
...
|
|
93
|
+
...ownership_opts,
|
|
96
94
|
{ value: "_formula", label: req.__("Formula") },
|
|
97
95
|
],
|
|
98
96
|
},
|
|
@@ -109,6 +107,14 @@ const tableForm = async (table, req) => {
|
|
|
109
107
|
.join(", "),
|
|
110
108
|
showIf: { ownership_field_id: "_formula" },
|
|
111
109
|
},
|
|
110
|
+
{
|
|
111
|
+
label: req.__("User group"),
|
|
112
|
+
sublabel: req.__(
|
|
113
|
+
"Add relations to this table in dropdown options for ownership field"
|
|
114
|
+
),
|
|
115
|
+
name: "is_user_group",
|
|
116
|
+
type: "Bool",
|
|
117
|
+
},
|
|
112
118
|
]
|
|
113
119
|
: []),
|
|
114
120
|
// description of table
|
|
@@ -898,6 +904,12 @@ router.post(
|
|
|
898
904
|
notify = req.__(`Invalid ownership formula: %s`, fmlValidRes);
|
|
899
905
|
hasError = true;
|
|
900
906
|
}
|
|
907
|
+
} else if (
|
|
908
|
+
typeof rest.ownership_field_id === "string" &&
|
|
909
|
+
rest.ownership_field_id.startsWith("Fml:")
|
|
910
|
+
) {
|
|
911
|
+
rest.ownership_formula = rest.ownership_field_id.replace("Fml:", "");
|
|
912
|
+
rest.ownership_field_id = null;
|
|
901
913
|
} else rest.ownership_formula = null;
|
|
902
914
|
await table.update(rest);
|
|
903
915
|
|
package/routes/utils.js
CHANGED
|
@@ -76,7 +76,7 @@ const setLanguage = (req, res, state) => {
|
|
|
76
76
|
} else if (req.cookies?.lang) {
|
|
77
77
|
req.setLocale(req.cookies?.lang);
|
|
78
78
|
}
|
|
79
|
-
set_custom_http_headers(res, state);
|
|
79
|
+
set_custom_http_headers(res, req, state);
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -85,8 +85,17 @@ const setLanguage = (req, res, state) => {
|
|
|
85
85
|
* @param {string} state
|
|
86
86
|
* @returns {void}
|
|
87
87
|
*/
|
|
88
|
-
const set_custom_http_headers = (res, state) => {
|
|
89
|
-
const
|
|
88
|
+
const set_custom_http_headers = (res, req, state) => {
|
|
89
|
+
const state1 = state || getState();
|
|
90
|
+
const hdrs = state1.getConfig("custom_http_headers");
|
|
91
|
+
if (!req.user) {
|
|
92
|
+
const public_cache_maxage = +state1.getConfig("public_cache_maxage", 0);
|
|
93
|
+
if (public_cache_maxage)
|
|
94
|
+
res.header(
|
|
95
|
+
"Cache-Control",
|
|
96
|
+
`public, max-age=${public_cache_maxage * 60}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
90
99
|
if (!hdrs) return;
|
|
91
100
|
for (const ln of hdrs.split("\n")) {
|
|
92
101
|
const [k, v] = ln.split(":");
|
package/routes/viewedit.js
CHANGED
|
@@ -464,7 +464,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
464
464
|
type: "breadcrumbs",
|
|
465
465
|
crumbs: [
|
|
466
466
|
{ text: req.__("Views"), href: "/viewedit" },
|
|
467
|
-
{ href: `/
|
|
467
|
+
{ href: `/view/${view.name}`, text: view.name },
|
|
468
468
|
{ workflow: wf, step: wfres },
|
|
469
469
|
],
|
|
470
470
|
},
|
package/serve.js
CHANGED
|
@@ -377,7 +377,7 @@ const setupSocket = (...servers) => {
|
|
|
377
377
|
const view = View.findOne({ name: viewname });
|
|
378
378
|
if (view.viewtemplateObj.authorize_join) {
|
|
379
379
|
view.viewtemplateObj
|
|
380
|
-
.authorize_join(view
|
|
380
|
+
.authorize_join(view, room_id, socket.request.user)
|
|
381
381
|
.then((authorized) => {
|
|
382
382
|
if (authorized) socket.join(`${ten}_${viewname}_${room_id}`);
|
|
383
383
|
});
|