@saltcorn/server 1.1.1 → 1.1.2-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/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Notable changes
2
2
 
3
- ## 1.1.1 - In beta
3
+ ## 1.1.2 - In beta
4
+
5
+ * Upgrade a large number of dependencies (express, typescript, oclif, pg, webpack, typescript). Node.js 18+ is require for this release.
6
+
7
+ ## 1.1.1 - Released 2 February 2025
4
8
 
5
9
  * Full-text search improvements:
6
10
  - An index for full-text search can now be created. When creating an index in
package/app.js CHANGED
@@ -32,7 +32,7 @@ const { getAllTenants } = require("@saltcorn/admin-models/models/tenant");
32
32
  const path = require("path");
33
33
  const helmet = require("helmet");
34
34
  const wrapper = require("./wrapper");
35
- const csrf = require("csurf");
35
+ const csrf = require("@dr.pogodin/csurf");
36
36
  const { I18n } = require("i18n");
37
37
  const { h1 } = require("@saltcorn/markup/tags");
38
38
  const is = require("contractis/is");
@@ -139,9 +139,14 @@ const getApp = async (opts = {}) => {
139
139
  connectSrc: ["'self'", "data:"],
140
140
  scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
141
141
  "script-src-attr": ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
142
- styleSrc: ["'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "'unsafe-inline'"],
142
+ styleSrc: [
143
+ "'self'",
144
+ "https://fonts.googleapis.com",
145
+ "https://fonts.gstatic.com",
146
+ "'unsafe-inline'",
147
+ ],
143
148
  imgSrc: ["'self'", "data:"],
144
- fontSrc: ["'self'", "data:", "https://fonts.gstatic.com",],
149
+ fontSrc: ["'self'", "data:", "https://fonts.gstatic.com"],
145
150
  "form-action": ["'self'", "javascript:"],
146
151
  },
147
152
  },
@@ -260,7 +265,7 @@ const getApp = async (opts = {}) => {
260
265
  new CustomStrategy((req, done) => {
261
266
  loginAttempt();
262
267
  async function loginAttempt() {
263
- const { remember, _csrf, dest, ...userobj } = req.body;
268
+ const { remember, _csrf, dest, ...userobj } = req.body || {};
264
269
  if (!is.objVals(is.str).check(userobj))
265
270
  return done(
266
271
  null,
@@ -455,7 +460,7 @@ Sitemap: ${base}sitemap.xml
455
460
  // file store ensure
456
461
  await File.ensure_file_store();
457
462
  // 404 handling
458
- app.get("*", function (req, res) {
463
+ app.get("*any", function (req, res) {
459
464
  res.status(404).sendWrap(req.__("Not found"), h1(req.__("Page not found")));
460
465
  });
461
466
 
package/auth/admin.js CHANGED
@@ -465,7 +465,7 @@ router.post(
465
465
  isAdmin,
466
466
  error_catcher(async (req, res) => {
467
467
  const form = await auth_settings_form(req);
468
- form.validate(req.body);
468
+ form.validate(req.body || {});
469
469
  if (form.hasErrors) {
470
470
  send_users_page({
471
471
  res,
@@ -523,7 +523,7 @@ router.post(
523
523
  isAdmin,
524
524
  error_catcher(async (req, res) => {
525
525
  const form = await http_settings_form(req);
526
- form.validate(req.body);
526
+ form.validate(req.body || {});
527
527
  if (form.hasErrors) {
528
528
  send_users_page({
529
529
  res,
@@ -594,7 +594,7 @@ router.post(
594
594
  isAdmin,
595
595
  error_catcher(async (req, res) => {
596
596
  const form = await permissions_settings_form(req);
597
- form.validate(req.body);
597
+ form.validate(req.body || {});
598
598
  if (form.hasErrors) {
599
599
  send_users_page({
600
600
  res,
@@ -794,7 +794,7 @@ router.post(
794
794
  isAdmin,
795
795
  error_catcher(async (req, res) => {
796
796
  const form = await ssl_form(req);
797
- form.validate(req.body);
797
+ form.validate(req.body || {});
798
798
  if (form.hasErrors) {
799
799
  send_users_page({
800
800
  res,
@@ -998,15 +998,15 @@ router.post(
998
998
  isAdmin,
999
999
  error_catcher(async (req, res) => {
1000
1000
  let form, sub2;
1001
- if (req.body.id) {
1002
- const user = await User.findOne({ id: req.body.id });
1001
+ if ((req.body || {}).id) {
1002
+ const user = await User.findOne({ id: (req.body || {}).id });
1003
1003
  form = await userForm(req, user);
1004
1004
  sub2 = user.email;
1005
1005
  } else {
1006
1006
  form = await userForm(req);
1007
1007
  sub2 = "New";
1008
1008
  }
1009
- form.validate(req.body);
1009
+ form.validate(req.body || {});
1010
1010
  if (form.hasErrors) {
1011
1011
  send_users_page({
1012
1012
  res,
package/auth/roleadmin.js CHANGED
@@ -227,7 +227,7 @@ router.post(
227
227
  isAdmin,
228
228
  error_catcher(async (req, res) => {
229
229
  const form = await roleForm(req);
230
- form.validate(req.body);
230
+ form.validate(req.body || {});
231
231
  if (form.hasErrors) {
232
232
  send_users_page({
233
233
  res,
@@ -265,7 +265,7 @@ router.post(
265
265
  error_catcher(async (req, res) => {
266
266
  const { id } = req.params;
267
267
  const layout_by_role = getState().getConfigCopy("layout_by_role");
268
- layout_by_role[+id] = req.body.layout;
268
+ layout_by_role[+id] = (req.body || {}).layout;
269
269
  await getState().setConfig("layout_by_role", layout_by_role);
270
270
  req.flash("success", req.__(`Saved layout for role`));
271
271
 
@@ -286,7 +286,7 @@ router.post(
286
286
  const twofa_policy_by_role = getState().getConfigCopy(
287
287
  "twofa_policy_by_role"
288
288
  );
289
- twofa_policy_by_role[+id] = req.body.policy;
289
+ twofa_policy_by_role[+id] = (req.body || {}).policy;
290
290
  await getState().setConfig("twofa_policy_by_role", twofa_policy_by_role);
291
291
  req.flash("success", req.__(`Saved 2FA policy for role`));
292
292
 
package/auth/routes.js CHANGED
@@ -432,9 +432,9 @@ router.post(
432
432
  "/reset",
433
433
  error_catcher(async (req, res) => {
434
434
  const result = await User.resetPasswordWithToken({
435
- email: req.body.email,
436
- reset_password_token: req.body.token,
437
- password: req.body.password,
435
+ email: (req.body || {}).email,
436
+ reset_password_token: (req.body || {}).token,
437
+ password: (req.body || {}).password,
438
438
  });
439
439
  if (result.success) {
440
440
  req.flash(
@@ -457,7 +457,7 @@ router.post(
457
457
  "/forgot",
458
458
  error_catcher(async (req, res) => {
459
459
  if (getState().getConfig("allow_forgot")) {
460
- const { email } = req.body;
460
+ const { email } = req.body || {};
461
461
  const u = await User.findOne({ email });
462
462
  const respond = () => {
463
463
  req.flash("success", req.__("Email with password reset link sent"));
@@ -614,7 +614,7 @@ router.post(
614
614
  const hasUsers = await User.nonEmpty();
615
615
  if (!hasUsers) {
616
616
  const form = loginForm(req, true);
617
- form.validate(req.body);
617
+ form.validate(req.body || {});
618
618
 
619
619
  if (form.hasErrors) {
620
620
  form.action = "/auth/create_first_user";
@@ -784,7 +784,7 @@ router.post(
784
784
  const form = await getNewUserForm(new_user_form, req, !req.user.email);
785
785
  form.action = "/auth/signup_final_ext";
786
786
 
787
- await form.asyncValidate(req.body);
787
+ await form.asyncValidate(req.body || {});
788
788
  if (form.hasErrors) {
789
789
  res.sendAuthWrap(new_user_form, form, getAuthLinks("signup", true));
790
790
  return;
@@ -846,7 +846,7 @@ router.post(
846
846
  });
847
847
  }
848
848
  }
849
- await form.asyncValidate(req.body);
849
+ await form.asyncValidate(req.body || {});
850
850
  if (form.hasErrors) {
851
851
  res.sendAuthWrap(new_user_form, form, getAuthLinks("signup", true));
852
852
  } else if (form.values.email && !check_email_mask(form.values.email)) {
@@ -961,7 +961,7 @@ router.post(
961
961
  req,
962
962
  false
963
963
  );
964
- await signup_form.asyncValidate(req.body);
964
+ await signup_form.asyncValidate(req.body || {});
965
965
  if (signup_form.hasErrors) {
966
966
  signup_form.action = "/auth/signup";
967
967
  res.sendAuthWrap(
@@ -1010,7 +1010,7 @@ router.post(
1010
1010
  }
1011
1011
 
1012
1012
  const form = await default_signup_form(req);
1013
- await form.asyncValidate(req.body);
1013
+ await form.asyncValidate(req.body || {});
1014
1014
 
1015
1015
  if (form.hasErrors) {
1016
1016
  form.action = "/auth/signup";
@@ -1049,9 +1049,9 @@ router.post(
1049
1049
  */
1050
1050
  function handler(req, res) {
1051
1051
  console.log(
1052
- `Failed login attempt for: ${req.body.email} from ${req.ip} UA ${req.get(
1053
- "User-Agent"
1054
- )}`
1052
+ `Failed login attempt for: ${(req.body || {}).email} from ${
1053
+ req.ip
1054
+ } UA ${req.get("User-Agent")}`
1055
1055
  );
1056
1056
  req.flash(
1057
1057
  "error",
@@ -1084,7 +1084,7 @@ const userLimiter = rateLimit({
1084
1084
  // TBD create config parameter
1085
1085
  windowMs: 5 * 60 * 1000, // 5 minutes
1086
1086
  max: 3, // limit each IP to 100 requests per windowMs
1087
- keyGenerator: (req) => userIdKey(req.body),
1087
+ keyGenerator: (req) => userIdKey(req.body || {}),
1088
1088
  handler,
1089
1089
  });
1090
1090
 
@@ -1111,14 +1111,14 @@ router.post(
1111
1111
  }),
1112
1112
  error_catcher(async (req, res) => {
1113
1113
  ipLimiter.resetKey(req.ip);
1114
- userLimiter.resetKey(userIdKey(req.body));
1114
+ userLimiter.resetKey(userIdKey(req.body || {}));
1115
1115
  if (req.user.pending_user) {
1116
1116
  res.redirect("/auth/twofa/login/totp");
1117
1117
  return;
1118
1118
  }
1119
1119
  let maxAge = null;
1120
1120
  if (req.session.cookie)
1121
- if (req.body.remember) {
1121
+ if ((req.body || {}).remember) {
1122
1122
  const setDur = +getState().getConfig("cookie_duration_remember", 720);
1123
1123
  if (setDur) {
1124
1124
  maxAge = setDur * 60 * 60 * 1000;
@@ -1147,10 +1147,10 @@ router.post(
1147
1147
  if (getState().get2FApolicy(req.user) === "Mandatory") {
1148
1148
  res.redirect("/auth/twofa/setup/totp");
1149
1149
  } else if (
1150
- req.body.dest &&
1151
- is_relative_url(decodeURIComponent(req.body.dest))
1150
+ (req.body || {}).dest &&
1151
+ is_relative_url(decodeURIComponent((req.body || {}).dest))
1152
1152
  ) {
1153
- res.redirect(decodeURIComponent(req.body.dest));
1153
+ res.redirect(decodeURIComponent((req.body || {}).dest));
1154
1154
  } else res.redirect("/");
1155
1155
  })
1156
1156
  );
@@ -1566,9 +1566,9 @@ router.post(
1566
1566
  loggedIn,
1567
1567
  error_catcher(async (req, res) => {
1568
1568
  const u = await User.findForSession({ id: req.user.id });
1569
- const newlang = available_languages[req.body.locale];
1569
+ const newlang = available_languages[(req.body || {}).locale];
1570
1570
  if (newlang && u) {
1571
- await u.set_language(req.body.locale);
1571
+ await u.set_language((req.body || {}).locale);
1572
1572
  req.login(u.session_object, function (err) {
1573
1573
  if (!err) {
1574
1574
  req.flash("success", req.__("Language changed to %s", newlang));
@@ -1656,7 +1656,7 @@ router.post(
1656
1656
  "/set-email",
1657
1657
  error_catcher(async (req, res) => {
1658
1658
  const form = setEmailForm(req);
1659
- form.validate(req.body);
1659
+ form.validate(req.body || {});
1660
1660
  if (form.hasErrors || !req.user || !req.user.id) {
1661
1661
  res.sendWrap(req.__("Set Email"), renderForm(form, req.csrfToken()));
1662
1662
  return;
@@ -1706,7 +1706,7 @@ router.post(
1706
1706
  );
1707
1707
  return;
1708
1708
  }
1709
- if (req.body.new_password && user.password) {
1709
+ if ((req.body || {}).new_password && user.password) {
1710
1710
  const pwform = changPwForm(req);
1711
1711
 
1712
1712
  pwform.fields[0].validator = (oldpw) => {
@@ -1715,7 +1715,7 @@ router.post(
1715
1715
  else return req.__("Password does not match");
1716
1716
  };
1717
1717
 
1718
- pwform.validate(req.body);
1718
+ pwform.validate(req.body || {});
1719
1719
 
1720
1720
  if (pwform.hasErrors) {
1721
1721
  res.sendWrap(
@@ -1738,7 +1738,7 @@ router.post(
1738
1738
  json() {},
1739
1739
  redirect() {},
1740
1740
  };
1741
- await view.runPost({ id: user.id }, req.body, {
1741
+ await view.runPost({ id: user.id }, req.body || {}, {
1742
1742
  req,
1743
1743
  res: fakeRes,
1744
1744
  redirect: "/auth/settings",
@@ -1773,7 +1773,7 @@ router.all(
1773
1773
  return;
1774
1774
  }
1775
1775
  verifier.action = "/auth/verification-flow";
1776
- const wfres = await verifier.run(req.body || {}, req);
1776
+ const wfres = await verifier.run(req.body || {} || {}, req);
1777
1777
  if (wfres.flash) req.flash(wfres.flash[0], wfres.flash[1]);
1778
1778
  if (wfres.renderForm) {
1779
1779
  res.sendWrap(
@@ -1860,7 +1860,7 @@ router.post(
1860
1860
  }
1861
1861
 
1862
1862
  const form = totpForm(req);
1863
- form.validate(req.body);
1863
+ form.validate(req.body || {});
1864
1864
  if (form.hasErrors) {
1865
1865
  req.flash("danger", req.__("Error processing form"));
1866
1866
  console.log("Error processing form");
@@ -1918,7 +1918,7 @@ router.post(
1918
1918
  error_catcher(async (req, res) => {
1919
1919
  const user = await User.findOne({ id: req.user.id });
1920
1920
  const form = totpForm(req, "/auth/twofa/disable/totp");
1921
- form.validate(req.body);
1921
+ form.validate(req.body || {});
1922
1922
  if (form.hasErrors) {
1923
1923
  req.flash("danger", req.__("Error processing form"));
1924
1924
  res.redirect("/auth/twofa/disable/totp");
package/package.json CHANGED
@@ -1,72 +1,72 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "1.1.1",
3
+ "version": "1.1.2-beta.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
- "@aws-sdk/client-s3": "^3.451.0",
10
- "@saltcorn/base-plugin": "1.1.1",
11
- "@saltcorn/builder": "1.1.1",
12
- "@saltcorn/data": "1.1.1",
13
- "@saltcorn/admin-models": "1.1.1",
14
- "@saltcorn/filemanager": "1.1.1",
15
- "@saltcorn/markup": "1.1.1",
16
- "@saltcorn/plugins-loader": "1.1.1",
17
- "@saltcorn/sbadmin2": "1.1.1",
9
+ "@aws-sdk/client-s3": "^3.735.0",
10
+ "@dr.pogodin/csurf": "^1.14.1",
11
+ "@saltcorn/base-plugin": "1.1.2-beta.1",
12
+ "@saltcorn/builder": "1.1.2-beta.1",
13
+ "@saltcorn/data": "1.1.2-beta.1",
14
+ "@saltcorn/admin-models": "1.1.2-beta.1",
15
+ "@saltcorn/filemanager": "1.1.2-beta.1",
16
+ "@saltcorn/markup": "1.1.2-beta.1",
17
+ "@saltcorn/plugins-loader": "1.1.2-beta.1",
18
+ "@saltcorn/sbadmin2": "1.1.2-beta.1",
18
19
  "@socket.io/cluster-adapter": "^0.2.1",
19
20
  "@socket.io/sticky": "^1.0.1",
20
- "adm-zip": "0.5.10",
21
+ "adm-zip": "0.5.16",
21
22
  "connect-flash": "^0.1.1",
22
- "connect-pg-simple": "^6.1.0",
23
- "content-disposition": "^0.5.3",
23
+ "connect-pg-simple": "^9.0.1",
24
+ "content-disposition": "^0.5.4",
24
25
  "contractis": "^0.1.0",
25
- "cookie-parser": "^1.4.4",
26
- "cookie-session": "^1.4.0",
26
+ "cookie-parser": "^1.4.7",
27
+ "cookie-session": "^2.1.0",
27
28
  "cors": "2.8.5",
28
- "csurf": "^1.11.0",
29
- "csv-stringify": "^5.5.0",
30
- "dockerode": "~4.0.2",
31
- "express": "^4.17.1",
32
- "express-fileupload": "^1.1.8",
29
+ "csv-stringify": "^6.5.2",
30
+ "dockerode": "~4.0.4",
31
+ "express": "^5.0.1",
32
+ "express-fileupload": "^1.5.1",
33
33
  "express-promise-router": "^3.0.3",
34
- "express-rate-limit": "^5.1.3",
35
- "express-session": "^1.17.1",
34
+ "express-rate-limit": "^7.5.0",
35
+ "express-session": "^1.18.1",
36
36
  "greenlock": "^4.0.4",
37
37
  "greenlock-express": "^4.0.3",
38
- "helmet": "^7.1.0",
38
+ "helmet": "^8.0.0",
39
39
  "i18n": "^0.15.1",
40
- "imapflow": "1.0.123",
41
- "jsonwebtoken": "^9.0.0",
42
- "markdown-it": "^13.0.2",
43
- "moment": "^2.29.4",
40
+ "imapflow": "1.0.178",
41
+ "jsonwebtoken": "^9.0.2",
42
+ "markdown-it": "^14.1.0",
43
+ "moment": "^2.30.1",
44
44
  "multer": "1.4.5-lts.1",
45
45
  "multer-s3": "^3.0.1",
46
46
  "node-fetch": "2.6.9",
47
- "node-watch": "^0.7.2",
47
+ "node-watch": "^0.7.4",
48
48
  "notp": "2.0.3",
49
49
  "npm-registry-fetch": "17.1.0",
50
- "passport": "^0.6.0",
50
+ "passport": "^0.7.0",
51
51
  "passport-custom": "^1.1.1",
52
52
  "passport-http-bearer": "^1.0.1",
53
53
  "passport-jwt": "4.0.1",
54
54
  "passport-totp": "0.0.2",
55
- "pg": "^8.2.1",
55
+ "pg": "^8.13.1",
56
56
  "pluralize": "^8.0.0",
57
57
  "qrcode": "1.5.1",
58
58
  "resize-with-sharp-or-jimp": "0.1.8",
59
- "semver": "^7.6.0",
60
- "socket.io": "4.6.0",
61
- "systeminformation": "^5.21.7",
59
+ "semver": "^7.6.3",
60
+ "socket.io": "4.8.1",
61
+ "systeminformation": "^5.25.11",
62
62
  "thirty-two": "1.0.2",
63
- "tmp-promise": "^3.0.2",
64
- "ua-parser-js": "^1.0.37",
65
- "underscore": "1.13.6",
66
- "uuid": "^10.0.0"
63
+ "tmp-promise": "^3.0.3",
64
+ "ua-parser-js": "^2.0.0",
65
+ "underscore": "1.13.7",
66
+ "uuid": "^11.0.5"
67
67
  },
68
68
  "optionalDependencies": {
69
- "connect-sqlite3": "^0.9.11",
69
+ "connect-sqlite3": "^0.9.15",
70
70
  "sd-notify": "^2.8.0"
71
71
  },
72
72
  "repository": "github:saltcorn/saltcorn",
@@ -1120,7 +1120,11 @@ function initialize_page() {
1120
1120
  decodeURIComponent($(that).attr("data-explainers"))
1121
1121
  );
1122
1122
  var currentVal = explainers[$(that).val()];
1123
- $("#" + id).html(`<strong>${$(that).val()}</strong>: ${currentVal}`);
1123
+ $("#" + id).html(
1124
+ `<strong>${
1125
+ $(that).find("option:selected").text() || $(that).val()
1126
+ }</strong>: ${currentVal}`
1127
+ );
1124
1128
  if (currentVal) $("#" + id).show();
1125
1129
  else $("#" + id).hide();
1126
1130
  }
package/routes/actions.js CHANGED
@@ -424,7 +424,7 @@ router.post(
424
424
  error_catcher(async (req, res) => {
425
425
  const form = await triggerForm(req);
426
426
 
427
- form.validate(req.body);
427
+ form.validate(req.body || {});
428
428
  if (form.hasErrors) {
429
429
  send_events_page({
430
430
  res,
@@ -471,7 +471,7 @@ router.post(
471
471
 
472
472
  const form = await triggerForm(req, trigger);
473
473
 
474
- form.validate(req.body);
474
+ form.validate(req.body || {});
475
475
  if (form.hasErrors) {
476
476
  send_events_page({
477
477
  res,
@@ -1097,7 +1097,7 @@ router.post(
1097
1097
  fields: cfgFields,
1098
1098
  });
1099
1099
  }
1100
- form.validate(req.body);
1100
+ form.validate(req.body || {});
1101
1101
  if (form.hasErrors) {
1102
1102
  if (req.xhr) {
1103
1103
  res.status(400).json({ error: form.errorSummary });
@@ -1304,7 +1304,7 @@ router.post(
1304
1304
  * @function
1305
1305
  */
1306
1306
  router.get(
1307
- "/stepedit/:trigger_id/:step_id?",
1307
+ "/stepedit/:trigger_id{/:step_id}",
1308
1308
  isAdminOrHasConfigMinRole("min_role_edit_triggers"),
1309
1309
  error_catcher(async (req, res) => {
1310
1310
  const { trigger_id, step_id } = req.params;
@@ -1352,7 +1352,7 @@ router.post(
1352
1352
  const { trigger_id } = req.params;
1353
1353
  const trigger = await Trigger.findOne({ id: trigger_id });
1354
1354
  const form = await getWorkflowStepForm(trigger, req);
1355
- form.validate(req.body);
1355
+ form.validate(req.body || {});
1356
1356
  if (form.hasErrors) {
1357
1357
  if (req.xhr) {
1358
1358
  res.json({ error: form.errorSummary });
@@ -1451,7 +1451,7 @@ router.post(
1451
1451
  const { trigger_id } = req.params;
1452
1452
  const trigger = await Trigger.findOne({ id: trigger_id });
1453
1453
  await WorkflowStep.deleteForTrigger(trigger.id);
1454
- const description = req.body.description;
1454
+ const description = (req.body || {}).description;
1455
1455
  await Trigger.update(trigger.id, { description });
1456
1456
  const steps = await getState().functions.copilot_generate_workflow.run(
1457
1457
  description,
@@ -1774,7 +1774,7 @@ router.post(
1774
1774
  });
1775
1775
 
1776
1776
  const form = await getWorkflowStepUserForm(run, trigger, step, req);
1777
- form.validate(req.body);
1777
+ form.validate(req.body || {});
1778
1778
  if (form.hasErrors) {
1779
1779
  const title = "Fill form";
1780
1780
  res.sendWrap(title, renderForm(form, req.csrfToken()));
package/routes/admin.js CHANGED
@@ -757,7 +757,9 @@ router.get(
757
757
  return;
758
758
  }
759
759
  const auto_backup_directory = getState().getConfig("auto_backup_directory");
760
- res.download(path.join(auto_backup_directory, filename), filename);
760
+ res.download(path.join(auto_backup_directory, filename), filename, {
761
+ dotfiles: "allow",
762
+ });
761
763
  })
762
764
  );
763
765
 
@@ -982,7 +984,7 @@ router.post(
982
984
  isAdmin,
983
985
  error_catcher(async (req, res) => {
984
986
  const form = await snapshotForm(req);
985
- form.validate(req.body);
987
+ form.validate(req.body || {});
986
988
 
987
989
  await save_config_from_form(form);
988
990
 
@@ -1000,7 +1002,7 @@ router.post(
1000
1002
  isAdmin,
1001
1003
  error_catcher(async (req, res) => {
1002
1004
  const form = await backupFilePrefixForm(req);
1003
- form.validate(req.body);
1005
+ form.validate(req.body || {});
1004
1006
  if (form.hasErrors) {
1005
1007
  send_admin_page({
1006
1008
  res,
@@ -1029,7 +1031,7 @@ router.post(
1029
1031
  isAdmin,
1030
1032
  error_catcher(async (req, res) => {
1031
1033
  const form = await autoBackupForm(req);
1032
- form.validate(req.body);
1034
+ form.validate(req.body || {});
1033
1035
  if (form.hasErrors) {
1034
1036
  send_admin_page({
1035
1037
  res,
@@ -1072,7 +1074,7 @@ router.post(
1072
1074
  * Do Snapshot now
1073
1075
  */
1074
1076
  router.post(
1075
- "/snapshot-now/:snapshotname?",
1077
+ "/snapshot-now{/:snapshotname}",
1076
1078
  isAdmin,
1077
1079
  error_catcher(async (req, res) => {
1078
1080
  const { snapshotname } = req.params;
@@ -1629,7 +1631,7 @@ const doInstall = async (req, res, version, deepClean, runPull) => {
1629
1631
  };
1630
1632
 
1631
1633
  router.post("/install", isAdmin, async (req, res) => {
1632
- const { version, deep_clean } = req.body;
1634
+ const { version, deep_clean } = req.body || {};
1633
1635
  await doInstall(req, res, version, deep_clean === "on", false);
1634
1636
  });
1635
1637
 
@@ -3349,7 +3351,7 @@ router.post(
3349
3351
  "/build-mobile-app/finish",
3350
3352
  isAdmin,
3351
3353
  error_catcher(async (req, res) => {
3352
- const { out_dir_name, build_dir } = req.body;
3354
+ const { out_dir_name, build_dir } = req.body || {};
3353
3355
  const content = await fs.promises.readFile(
3354
3356
  path.join(build_dir, "spawnParams.json")
3355
3357
  );
@@ -3435,7 +3437,10 @@ router.post(
3435
3437
  "/build-mobile-app",
3436
3438
  isAdmin,
3437
3439
  error_catcher(async (req, res) => {
3438
- getState().log(2, `starting mobile build: ${JSON.stringify(req.body)}`);
3440
+ getState().log(
3441
+ 2,
3442
+ `starting mobile build: ${JSON.stringify(req.body || {})}`
3443
+ );
3439
3444
  const msgs = [];
3440
3445
  let mode = "full";
3441
3446
  let {
@@ -3460,7 +3465,7 @@ router.post(
3460
3465
  keystoreFile,
3461
3466
  keystoreAlias,
3462
3467
  keystorePassword,
3463
- } = req.body;
3468
+ } = req.body || {};
3464
3469
  const receiveShareTriggers = Trigger.find({
3465
3470
  when_trigger: "ReceiveMobileShareData",
3466
3471
  });
@@ -3725,7 +3730,7 @@ router.post(
3725
3730
  isAdmin,
3726
3731
  error_catcher(async (req, res) => {
3727
3732
  try {
3728
- const newCfg = { ...req.body };
3733
+ const newCfg = { ...(req.body || {}) };
3729
3734
  const excludedPlugins = (await Plugin.find())
3730
3735
  .filter(
3731
3736
  (plugin) =>
@@ -3753,7 +3758,7 @@ router.post(
3753
3758
  isAdmin,
3754
3759
  error_catcher(async (req, res) => {
3755
3760
  const form = clearAllForm(req);
3756
- form.validate(req.body);
3761
+ form.validate(req.body || {});
3757
3762
  //order: page_groups, pages, views, user fields, tableconstraints, fields, table triggers, table history, tables, plugins, config+crashes+nontable triggers, users
3758
3763
  if (form.values.page_groups) {
3759
3764
  await PageGroup.delete({});
@@ -4147,7 +4152,7 @@ router.post(
4147
4152
  const { name } = req.params;
4148
4153
  const code_pages = getState().getConfigCopy("function_code_pages", {});
4149
4154
 
4150
- const code = req.body.code;
4155
+ const code = (req.body || {}).code;
4151
4156
  await getState().setConfig("function_code_pages", {
4152
4157
  ...code_pages,
4153
4158
  [name]: code,