@saltcorn/server 0.8.5-beta.2 → 0.8.5-beta.4
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/testhelp.js +15 -0
- package/locales/en.json +20 -1
- package/locales/pl.json +1110 -0
- package/package.json +9 -8
- package/public/saltcorn-common.js +44 -8
- package/public/saltcorn.css +23 -0
- package/public/vis-network.min.js +49 -0
- package/routes/admin.js +30 -8
- package/routes/api.js +73 -18
- package/routes/fields.js +11 -1
- package/routes/list.js +8 -5
- package/routes/plugins.js +47 -16
- package/routes/tables.js +104 -35
- package/routes/tenant.js +4 -0
- package/routes/utils.js +19 -0
- package/tests/api.test.js +2 -2
- package/tests/table.test.js +19 -2
- package/wrapper.js +14 -2
package/routes/tables.js
CHANGED
|
@@ -439,8 +439,7 @@ router.get(
|
|
|
439
439
|
title: req.__("Tables"),
|
|
440
440
|
headers: [
|
|
441
441
|
{
|
|
442
|
-
script:
|
|
443
|
-
"https://unpkg.com/vis-network@9.1.2/standalone/umd/vis-network.min.js",
|
|
442
|
+
script: `/static_assets/${db.connectObj.version_tag}/vis-network.min.js`,
|
|
444
443
|
},
|
|
445
444
|
],
|
|
446
445
|
},
|
|
@@ -762,18 +761,22 @@ router.get(
|
|
|
762
761
|
})
|
|
763
762
|
)
|
|
764
763
|
),
|
|
764
|
+
!table.external &&
|
|
765
|
+
div(
|
|
766
|
+
{ class: "mx-auto" },
|
|
767
|
+
a(
|
|
768
|
+
{ href: `/table/constraints/${table.id}` },
|
|
769
|
+
i({ class: "fas fa-2x fa-tasks" }),
|
|
770
|
+
"<br/>",
|
|
771
|
+
req.__("Constraints")
|
|
772
|
+
)
|
|
773
|
+
),
|
|
774
|
+
|
|
765
775
|
// only if table is not external
|
|
766
776
|
!table.external &&
|
|
767
777
|
div(
|
|
768
778
|
{ class: "mx-auto" },
|
|
769
779
|
settingsDropdown(`dataMenuButton`, [
|
|
770
|
-
a(
|
|
771
|
-
{
|
|
772
|
-
class: "dropdown-item",
|
|
773
|
-
href: `/table/constraints/${table.id}`,
|
|
774
|
-
},
|
|
775
|
-
'<i class="fas fa-ban"></i> ' + req.__("Constraints")
|
|
776
|
-
),
|
|
777
780
|
// rename table doesnt supported for sqlite
|
|
778
781
|
!db.isSQLite &&
|
|
779
782
|
table.name !== "users" &&
|
|
@@ -1154,8 +1157,15 @@ router.get(
|
|
|
1154
1157
|
[
|
|
1155
1158
|
{ label: req.__("Type"), key: "type" },
|
|
1156
1159
|
{
|
|
1157
|
-
label: req.__("
|
|
1158
|
-
key: (r) =>
|
|
1160
|
+
label: req.__("What"),
|
|
1161
|
+
key: (r) =>
|
|
1162
|
+
r.type === "Unique"
|
|
1163
|
+
? r.configuration.fields.join(", ")
|
|
1164
|
+
: r.type === "Index"
|
|
1165
|
+
? r.configuration.field
|
|
1166
|
+
: r.type === "Formula"
|
|
1167
|
+
? r.configuration.formula
|
|
1168
|
+
: "",
|
|
1159
1169
|
},
|
|
1160
1170
|
{
|
|
1161
1171
|
label: req.__("Delete"),
|
|
@@ -1166,7 +1176,12 @@ router.get(
|
|
|
1166
1176
|
cons,
|
|
1167
1177
|
{ hover: true }
|
|
1168
1178
|
),
|
|
1169
|
-
|
|
1179
|
+
req.__("Add constraint: "),
|
|
1180
|
+
link(`/table/add-constraint/${id}/Unique`, req.__("Unique")),
|
|
1181
|
+
" | ",
|
|
1182
|
+
link(`/table/add-constraint/${id}/Formula`, req.__("Formula")),
|
|
1183
|
+
" | ",
|
|
1184
|
+
link(`/table/add-constraint/${id}/Index`, req.__("Index")),
|
|
1170
1185
|
],
|
|
1171
1186
|
},
|
|
1172
1187
|
],
|
|
@@ -1181,18 +1196,68 @@ router.get(
|
|
|
1181
1196
|
* @param {object[]} fields
|
|
1182
1197
|
* @returns {Form}
|
|
1183
1198
|
*/
|
|
1184
|
-
const constraintForm = (req, table_id, fields) =>
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1199
|
+
const constraintForm = (req, table_id, fields, type) => {
|
|
1200
|
+
switch (type) {
|
|
1201
|
+
case "Formula":
|
|
1202
|
+
return new Form({
|
|
1203
|
+
action: `/table/add-constraint/${table_id}/${type}`,
|
|
1204
|
+
|
|
1205
|
+
fields: [
|
|
1206
|
+
{
|
|
1207
|
+
name: "formula",
|
|
1208
|
+
label: req.__("Constraint formula"),
|
|
1209
|
+
validator: expressionValidator,
|
|
1210
|
+
type: "String",
|
|
1211
|
+
class: "validate-expression",
|
|
1212
|
+
sublabel:
|
|
1213
|
+
req.__(
|
|
1214
|
+
"Formula must evaluate to true for valid rows. In scope: "
|
|
1215
|
+
) +
|
|
1216
|
+
fields
|
|
1217
|
+
.map((f) => f.name)
|
|
1218
|
+
.map((fn) => code(fn))
|
|
1219
|
+
.join(", "),
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
name: "errormsg",
|
|
1223
|
+
label: "Error message",
|
|
1224
|
+
sublabel: "Shown the user if formula is false",
|
|
1225
|
+
type: "String",
|
|
1226
|
+
},
|
|
1227
|
+
],
|
|
1228
|
+
});
|
|
1229
|
+
case "Unique":
|
|
1230
|
+
return new Form({
|
|
1231
|
+
action: `/table/add-constraint/${table_id}/${type}`,
|
|
1232
|
+
blurb: req.__(
|
|
1233
|
+
"Tick the boxes for the fields that should be jointly unique"
|
|
1234
|
+
),
|
|
1235
|
+
fields: fields.map((f) => ({
|
|
1236
|
+
name: f.name,
|
|
1237
|
+
label: f.label,
|
|
1238
|
+
type: "Bool",
|
|
1239
|
+
})),
|
|
1240
|
+
});
|
|
1241
|
+
case "Index":
|
|
1242
|
+
return new Form({
|
|
1243
|
+
action: `/table/add-constraint/${table_id}/${type}`,
|
|
1244
|
+
blurb: req.__(
|
|
1245
|
+
"Choose the field to be indexed. This make searching the table faster."
|
|
1246
|
+
),
|
|
1247
|
+
fields: [
|
|
1248
|
+
{
|
|
1249
|
+
type: "String",
|
|
1250
|
+
name: "field",
|
|
1251
|
+
label: "Field",
|
|
1252
|
+
required: true,
|
|
1253
|
+
attributes: {
|
|
1254
|
+
options: fields.map((f) => ({ label: f.label, name: f.name })),
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
],
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1196
1261
|
|
|
1197
1262
|
/**
|
|
1198
1263
|
* Add constraint GET handler
|
|
@@ -1203,10 +1268,10 @@ const constraintForm = (req, table_id, fields) =>
|
|
|
1203
1268
|
* @function
|
|
1204
1269
|
*/
|
|
1205
1270
|
router.get(
|
|
1206
|
-
"/add-constraint/:id",
|
|
1271
|
+
"/add-constraint/:id/:type",
|
|
1207
1272
|
isAdmin,
|
|
1208
1273
|
error_catcher(async (req, res) => {
|
|
1209
|
-
const { id } = req.params;
|
|
1274
|
+
const { id, type } = req.params;
|
|
1210
1275
|
const table = await Table.findOne({ id });
|
|
1211
1276
|
if (!table) {
|
|
1212
1277
|
req.flash("error", `Table not found`);
|
|
@@ -1214,7 +1279,7 @@ router.get(
|
|
|
1214
1279
|
return;
|
|
1215
1280
|
}
|
|
1216
1281
|
const fields = await table.getFields();
|
|
1217
|
-
const form = constraintForm(req, table.id, fields);
|
|
1282
|
+
const form = constraintForm(req, table.id, fields, type);
|
|
1218
1283
|
res.sendWrap(req.__(`Add constraint to %s`, table.name), {
|
|
1219
1284
|
above: [
|
|
1220
1285
|
{
|
|
@@ -1231,7 +1296,7 @@ router.get(
|
|
|
1231
1296
|
},
|
|
1232
1297
|
{
|
|
1233
1298
|
type: "card",
|
|
1234
|
-
title: req.__(`Add constraint to %s`, table.name),
|
|
1299
|
+
title: req.__(`Add %s constraint to %s`, type, table.name),
|
|
1235
1300
|
contents: renderForm(form, req.csrfToken()),
|
|
1236
1301
|
},
|
|
1237
1302
|
],
|
|
@@ -1247,10 +1312,10 @@ router.get(
|
|
|
1247
1312
|
* @function
|
|
1248
1313
|
*/
|
|
1249
1314
|
router.post(
|
|
1250
|
-
"/add-constraint/:id",
|
|
1315
|
+
"/add-constraint/:id/:type",
|
|
1251
1316
|
isAdmin,
|
|
1252
1317
|
error_catcher(async (req, res) => {
|
|
1253
|
-
const { id } = req.params;
|
|
1318
|
+
const { id, type } = req.params;
|
|
1254
1319
|
const table = await Table.findOne({ id });
|
|
1255
1320
|
if (!table) {
|
|
1256
1321
|
req.flash("error", `Table not found`);
|
|
@@ -1258,16 +1323,20 @@ router.post(
|
|
|
1258
1323
|
return;
|
|
1259
1324
|
}
|
|
1260
1325
|
const fields = await table.getFields();
|
|
1261
|
-
const form = constraintForm(req, table.id, fields);
|
|
1326
|
+
const form = constraintForm(req, table.id, fields, type);
|
|
1262
1327
|
form.validate(req.body);
|
|
1263
1328
|
if (form.hasErrors) req.flash("error", req.__("An error occurred"));
|
|
1264
1329
|
else {
|
|
1330
|
+
let configuration = {};
|
|
1331
|
+
if (type === "Unique")
|
|
1332
|
+
configuration.fields = fields
|
|
1333
|
+
.map((f) => f.name)
|
|
1334
|
+
.filter((f) => form.values[f]);
|
|
1335
|
+
else configuration = form.values;
|
|
1265
1336
|
await TableConstraint.create({
|
|
1266
1337
|
table_id: table.id,
|
|
1267
|
-
type
|
|
1268
|
-
configuration
|
|
1269
|
-
fields: fields.map((f) => f.name).filter((f) => form.values[f]),
|
|
1270
|
-
},
|
|
1338
|
+
type,
|
|
1339
|
+
configuration,
|
|
1271
1340
|
});
|
|
1272
1341
|
}
|
|
1273
1342
|
res.redirect(`/table/constraints/${table.id}`);
|
package/routes/tenant.js
CHANGED
|
@@ -426,6 +426,10 @@ const tenant_settings_form = (req) =>
|
|
|
426
426
|
"create_tenant_warning",
|
|
427
427
|
"create_tenant_warning_text",
|
|
428
428
|
"tenant_template",
|
|
429
|
+
{ section_header: "Tenant application capabilities" },
|
|
430
|
+
"tenants_install_git",
|
|
431
|
+
"tenants_set_npm_modules",
|
|
432
|
+
"tenants_unsafe_plugins",
|
|
429
433
|
],
|
|
430
434
|
action: "/tenant/settings",
|
|
431
435
|
submitLabel: req.__("Save"),
|
package/routes/utils.js
CHANGED
|
@@ -18,6 +18,24 @@ const cookieSession = require("cookie-session");
|
|
|
18
18
|
const is = require("contractis/is");
|
|
19
19
|
const { validateHeaderName, validateHeaderValue } = require("http");
|
|
20
20
|
const Crash = require("@saltcorn/data/models/crash");
|
|
21
|
+
const si = require("systeminformation");
|
|
22
|
+
|
|
23
|
+
const get_sys_info = async () => {
|
|
24
|
+
const disks = await si.fsSize();
|
|
25
|
+
let size = 0;
|
|
26
|
+
let used = 0;
|
|
27
|
+
disks.forEach((d) => {
|
|
28
|
+
if (d && d.used && d.size) {
|
|
29
|
+
size += d.size;
|
|
30
|
+
used += d.used;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const diskUsage = Math.round(100 * (used / size));
|
|
34
|
+
const simem = await si.mem();
|
|
35
|
+
const memUsage = Math.round(100 - 100 * (simem.available / simem.total));
|
|
36
|
+
const cpuUsage = Math.round((await si.currentLoad()).currentLoad);
|
|
37
|
+
return { memUsage, diskUsage, cpuUsage };
|
|
38
|
+
};
|
|
21
39
|
|
|
22
40
|
/**
|
|
23
41
|
* Checks that user logged or not.
|
|
@@ -314,4 +332,5 @@ module.exports = {
|
|
|
314
332
|
get_tenant_from_req,
|
|
315
333
|
addOnDoneRedirect,
|
|
316
334
|
is_relative_url,
|
|
335
|
+
get_sys_info,
|
|
317
336
|
};
|
package/tests/api.test.js
CHANGED
|
@@ -19,7 +19,7 @@ beforeAll(async () => {
|
|
|
19
19
|
afterAll(db.close);
|
|
20
20
|
|
|
21
21
|
describe("API read", () => {
|
|
22
|
-
it("should get books for public", async () => {
|
|
22
|
+
it("should get books for public simple", async () => {
|
|
23
23
|
const app = await getApp({ disableCsrf: true });
|
|
24
24
|
await request(app)
|
|
25
25
|
.get("/api/books/")
|
|
@@ -32,7 +32,7 @@ describe("API read", () => {
|
|
|
32
32
|
)
|
|
33
33
|
);
|
|
34
34
|
});
|
|
35
|
-
it("should get books for public", async () => {
|
|
35
|
+
it("should get books for public fts", async () => {
|
|
36
36
|
const app = await getApp({ disableCsrf: true });
|
|
37
37
|
await request(app)
|
|
38
38
|
.get("/api/books/?_fts=Herman")
|
package/tests/table.test.js
CHANGED
|
@@ -5,11 +5,13 @@ const Field = require("@saltcorn/data/models/field");
|
|
|
5
5
|
const {
|
|
6
6
|
getStaffLoginCookie,
|
|
7
7
|
getAdminLoginCookie,
|
|
8
|
+
getUserLoginCookie,
|
|
8
9
|
itShouldRedirectUnauthToLogin,
|
|
9
10
|
toInclude,
|
|
10
11
|
toNotInclude,
|
|
11
12
|
toRedirect,
|
|
12
13
|
resetToFixtures,
|
|
14
|
+
succeedJsonWith,
|
|
13
15
|
} = require("../auth/testhelp");
|
|
14
16
|
const db = require("@saltcorn/data/db");
|
|
15
17
|
const User = require("@saltcorn/data/models/user");
|
|
@@ -198,11 +200,11 @@ Gordon Kane, 217`;
|
|
|
198
200
|
.set("Cookie", loginCookie)
|
|
199
201
|
.expect(toInclude("books constraints"));
|
|
200
202
|
await request(app)
|
|
201
|
-
.get("/table/add-constraint/" + id)
|
|
203
|
+
.get("/table/add-constraint/" + id + "/Unique")
|
|
202
204
|
.set("Cookie", loginCookie)
|
|
203
205
|
.expect(toInclude("Add constraint to books"));
|
|
204
206
|
await request(app)
|
|
205
|
-
.post("/table/add-constraint/" + id)
|
|
207
|
+
.post("/table/add-constraint/" + id + "/Unique")
|
|
206
208
|
.send("author=on")
|
|
207
209
|
.send("pages=on")
|
|
208
210
|
.set("Cookie", loginCookie)
|
|
@@ -262,7 +264,22 @@ describe("deletion to table with row ownership", () => {
|
|
|
262
264
|
const row = await persons.insertRow({ name: "something", owner: user.id });
|
|
263
265
|
expect(await persons.countRows()).toBe(1);
|
|
264
266
|
const loginCookie = await getStaffLoginCookie();
|
|
267
|
+
const uloginCookie = await getUserLoginCookie();
|
|
265
268
|
const app = await getApp({ disableCsrf: true });
|
|
269
|
+
await request(app).get("/api/owned").expect(401);
|
|
270
|
+
await request(app)
|
|
271
|
+
.get("/api/owned")
|
|
272
|
+
.set("Cookie", loginCookie)
|
|
273
|
+
.expect(
|
|
274
|
+
succeedJsonWith(
|
|
275
|
+
(rows) => rows.length == 1 && rows[0].name === "something"
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
await request(app)
|
|
279
|
+
.get("/api/owned")
|
|
280
|
+
.set("Cookie", uloginCookie)
|
|
281
|
+
.expect(succeedJsonWith((rows) => rows.length == 0));
|
|
282
|
+
|
|
266
283
|
await request(app)
|
|
267
284
|
.post("/delete/owned/" + row)
|
|
268
285
|
.expect(toRedirect("/list/owned"));
|
package/wrapper.js
CHANGED
|
@@ -58,10 +58,22 @@ const get_menu = (req) => {
|
|
|
58
58
|
]
|
|
59
59
|
: [
|
|
60
60
|
...(allow_signup
|
|
61
|
-
? [
|
|
61
|
+
? [
|
|
62
|
+
{
|
|
63
|
+
link: "/auth/signup",
|
|
64
|
+
icon: "fas fa-user-plus",
|
|
65
|
+
label: req.__("Sign up"),
|
|
66
|
+
},
|
|
67
|
+
]
|
|
62
68
|
: []),
|
|
63
69
|
...(login_menu
|
|
64
|
-
? [
|
|
70
|
+
? [
|
|
71
|
+
{
|
|
72
|
+
link: "/auth/login",
|
|
73
|
+
icon: "fas fa-sign-in-alt",
|
|
74
|
+
label: req.__("Login"),
|
|
75
|
+
},
|
|
76
|
+
]
|
|
65
77
|
: []),
|
|
66
78
|
];
|
|
67
79
|
// const schema = db.getTenantSchema();
|