@saltcorn/server 0.8.0-beta.1 → 0.8.0-beta.3

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 CHANGED
@@ -7,20 +7,12 @@
7
7
  // todo refactor to few modules + rename to be in sync with router url
8
8
  const Router = require("express-promise-router");
9
9
  const { contract, is } = require("contractis");
10
-
11
10
  const db = require("@saltcorn/data/db");
12
11
  const User = require("@saltcorn/data/models/user");
13
12
  const View = require("@saltcorn/data/models/view");
14
13
  const Field = require("@saltcorn/data/models/field");
15
14
  const Form = require("@saltcorn/data/models/form");
16
- const {
17
- mkTable,
18
- renderForm,
19
- link,
20
- post_btn,
21
- settingsDropdown,
22
- post_dropdown_item,
23
- } = require("@saltcorn/markup");
15
+ const { mkTable, renderForm, link, post_btn, settingsDropdown, post_dropdown_item } = require("@saltcorn/markup");
24
16
  const { isAdmin, error_catcher } = require("../routes/utils");
25
17
  const { send_reset_email } = require("./resetpw");
26
18
  const { getState } = require("@saltcorn/data/db/state");
@@ -35,16 +27,7 @@ const {
35
27
  is_hsts_tld,
36
28
  } = require("../markup/admin");
37
29
  const { send_verification_email } = require("@saltcorn/data/models/email");
38
- const {
39
- expressionValidator,
40
- } = require("@saltcorn/data/models/expression");
41
- /**
42
- * @type {object}
43
- * @const
44
- * @namespace auth/adminRouter
45
- * @category server
46
- * @subcategory auth
47
- */
30
+ const { expressionValidator } = require("@saltcorn/data/models/expression");
48
31
  const router = new Router();
49
32
  module.exports = router;
50
33
 
@@ -357,6 +340,7 @@ const permissions_settings_form = async (req) =>
357
340
  field_names: [
358
341
  "min_role_upload",
359
342
  "min_role_apikeygen",
343
+ //hidden "exttables_min_role_read",
360
344
  ],
361
345
  action: "/useradmin/permissions",
362
346
  submitLabel: req.__("Save"),
package/locales/en.json CHANGED
@@ -1046,6 +1046,14 @@
1046
1046
  "<p>You have views with a role to access lower than the table role to read, \n with no table ownership. In the next version of Saltcorn, this may cause a\n denial of access. Users will need to have table read access to any data displayed.</p> \n Views potentially affected: %s": "<p>You have views with a role to access lower than the table role to read, \n with no table ownership. In the next version of Saltcorn, this may cause a\n denial of access. Users will need to have table read access to any data displayed.</p> \n Views potentially affected: %s",
1047
1047
  "If the parent row is deleted, do this to the child rows.": "If the parent row is deleted, do this to the child rows.",
1048
1048
  "On delete": "On delete",
1049
- "Database name": "Database name",
1050
- "Database schema": "Database schema"
1049
+ "Permissions": "Permissions",
1050
+ "Permissions settings": "Permissions settings",
1051
+ "Files accept filter ": "Files accept filter ",
1052
+ "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`",
1053
+ "Permissions settings updated": "Permissions settings updated",
1054
+ "Upload file(s)": "Upload file(s)",
1055
+ "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`",
1056
+ "Default Files accept filter": "Default Files accept filter",
1057
+ "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`": "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`",
1058
+ "Destination page": "Destination page"
1051
1059
  }
package/locales/it.json CHANGED
@@ -484,5 +484,5 @@
484
484
  "Table access": "Table access",
485
485
  "HTTP": "HTTP",
486
486
  "Rights": "Rights",
487
- "Table access": "Table access"
487
+ "Permissions": "Permissions"
488
488
  }
package/locales/ru.json CHANGED
@@ -919,5 +919,29 @@
919
919
  "File not found": "Файл не найден",
920
920
  "Permissions settings": "Настройки разрешений",
921
921
  "Permissions": "Разрешения",
922
- "Permissions settings updated": "Настройки разрешений обновлены"
922
+ "Permissions settings updated": "Настройки разрешений обновлены",
923
+ "Upload file(s)": "Upload file(s)",
924
+ "Split paste": "Split paste",
925
+ "Separate paste content into separate inputs": "Separate paste content into separate inputs",
926
+ "Preview": "Preview",
927
+ "Create new row": "Create new row",
928
+ "Descending?": "Descending?",
929
+ "Only include rows where this formula is true. ": "Only include rows where this formula is true. ",
930
+ "Use %s to access current user ID": "Use %s to access current user ID",
931
+ "Formula value": "Formula value",
932
+ "Units": "Units",
933
+ "%s view - %s on %s": "%s view - %s on %s",
934
+ "Password Repeat": "Password Repeat",
935
+ "Remember me": "Remember me",
936
+ "Files accept filter ": "Files accept filter ",
937
+ "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`",
938
+ "Home Page by Role": "Home Page by Role",
939
+ "Files accept filter": "Files accept filter",
940
+ "Specify how to create a new row": "Specify how to create a new row",
941
+ "Cascade delete to file": "Cascade delete to file",
942
+ "Deleting a row will also delete the file referenced by this field": "Deleting a row will also delete the file referenced by this field",
943
+ "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`",
944
+ "Default Files accept filter": "Default Files accept filter",
945
+ "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`": "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`",
946
+ "No file found": "No file found"
923
947
  }
package/markup/admin.js CHANGED
@@ -7,7 +7,6 @@
7
7
 
8
8
  const {
9
9
  div,
10
- //hr,
11
10
  form,
12
11
  input,
13
12
  label,
@@ -18,16 +17,8 @@ const {
18
17
  li,
19
18
  } = require("@saltcorn/markup/tags");
20
19
  const db = require("@saltcorn/data/db");
21
- const {
22
- //getConfig,
23
- //setConfig,
24
- //getAllConfigOrDefaults,
25
- //deleteConfig,
26
- configTypes,
27
- isFixedConfig,
28
- } = require("@saltcorn/data/models/config");
20
+ const { configTypes, isFixedConfig } = require("@saltcorn/data/models/config");
29
21
  const { getState } = require("@saltcorn/data/db/state");
30
-
31
22
  const Form = require("@saltcorn/data/models/form");
32
23
  const Table = require("@saltcorn/data/models/table");
33
24
  const View = require("@saltcorn/data/models/view");
@@ -245,6 +236,7 @@ const send_files_page = (args) => {
245
236
  sub_sections: [
246
237
  { text: "Files", href: "/files" },
247
238
  { text: "Storage", href: "/files/storage" },
239
+ { text: "Settings", href: "/files/settings" },
248
240
  ],
249
241
  ...args,
250
242
  });
@@ -369,7 +361,7 @@ const config_fields_form = async ({
369
361
 
370
362
  for (const name of field_names) {
371
363
  values[name] = state.getConfig(name);
372
- //console.log(`config field name: %s`,name);
364
+ // console.log(`config field name: %s`,name);
373
365
  if (configTypes[name].root_only && tenant !== db.connectObj.default_schema)
374
366
  continue;
375
367
  const isView = (configTypes[name].type || "").startsWith("View ");
package/markup/forms.js CHANGED
@@ -11,13 +11,11 @@ const {
11
11
  text,
12
12
  label,
13
13
  input,
14
- div,
15
- i,
16
- h5,
17
14
  } = require("@saltcorn/markup/tags");
18
15
  const { csrfField } = require("../routes/utils");
19
16
 
20
17
  /**
18
+ * Edit Role form (for admin)
21
19
  * @param {object} opts
22
20
  * @param {string} opts.url
23
21
  * @param {Role} opts.current_role
@@ -47,30 +45,36 @@ const editRoleForm = ({ url, current_role, roles, req }) =>
47
45
  );
48
46
 
49
47
  /**
50
- * @param {object} req
48
+ * File upload form (for admin)
49
+ * @param {object} req
50
+ * @param folder
51
51
  * @returns {Form}
52
52
  */
53
- const fileUploadForm = (req, folder) =>
54
- form(
55
- {
56
- action: "/files/upload",
57
- method: "post",
58
- encType: "multipart/form-data",
59
- },
60
- csrfField(req),
61
- label(req.__("Upload file ")),
62
- input({
63
- name: "file",
64
- class: "form-control ms-1 w-unset d-inline",
65
- type: "file",
66
- onchange: "form.submit()",
67
- multiple: true,
68
- }),
69
- folder && input({ type: "hidden", name: "folder", value: folder })
70
- );
53
+ const fileUploadForm = (req, folder
54
+ ) => {
55
+ const frm = form(
56
+ {
57
+ action: "/files/upload",
58
+ method: "post",
59
+ enctype: "multipart/form-data",
60
+ },
61
+ csrfField(req),
62
+ label(req.__("Upload file(s)")),
63
+ input({
64
+ name: "file",
65
+ class: "form-control ms-1 w-unset d-inline",
66
+ type: "file",
67
+ onchange: "form.submit()",
68
+ multiple: true,
69
+ }),
70
+ folder && input({type: "hidden", name: "folder", value: folder})
71
+ );
72
+ return frm;
73
+ };
71
74
 
72
75
  /**
73
- * @param {string} wizardTitle
76
+ * Get Wizard Card Title
77
+ * @param {string} wizardTitle
74
78
  * @param {*} wf
75
79
  * @param {object} wfres
76
80
  * @returns {string}
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.8.0-beta.1",
3
+ "version": "0.8.0-beta.3",
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.0-beta.1",
10
- "@saltcorn/builder": "0.8.0-beta.1",
11
- "@saltcorn/data": "0.8.0-beta.1",
12
- "@saltcorn/admin-models": "0.8.0-beta.1",
13
- "@saltcorn/filemanager": "0.8.0-beta.1",
14
- "@saltcorn/markup": "0.8.0-beta.1",
15
- "@saltcorn/sbadmin2": "0.8.0-beta.1",
9
+ "@saltcorn/base-plugin": "0.8.0-beta.3",
10
+ "@saltcorn/builder": "0.8.0-beta.3",
11
+ "@saltcorn/data": "0.8.0-beta.3",
12
+ "@saltcorn/admin-models": "0.8.0-beta.3",
13
+ "@saltcorn/filemanager": "0.8.0-beta.3",
14
+ "@saltcorn/markup": "0.8.0-beta.3",
15
+ "@saltcorn/sbadmin2": "0.8.0-beta.3",
16
16
  "@socket.io/cluster-adapter": "^0.1.0",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "aws-sdk": "^2.1037.0",
@@ -201,12 +201,12 @@ function view_post(viewname, route, data, onDone) {
201
201
  notifyAlert({ type: "danger", text: res.responseText });
202
202
  });
203
203
  }
204
- var logged_errors = [];
204
+ let logged_errors = [];
205
205
  function globalErrorCatcher(message, source, lineno, colno, error) {
206
206
  if (error && error.preventDefault) error.preventDefault();
207
207
  if (logged_errors.includes(message)) return;
208
208
  logged_errors.push(message);
209
- var data = { message, stack: (error && error.stack) || "" };
209
+ const data = { message, stack: (error && error.stack) || "" };
210
210
  $.ajax("/crashlog/", {
211
211
  dataType: "json",
212
212
  type: "POST",
package/routes/admin.js CHANGED
@@ -366,7 +366,7 @@ router.get(
366
366
  post_btn(
367
367
  "/admin/backup",
368
368
  i({ class: "fas fa-download me-2" }) +
369
- req.__("Download a backup"),
369
+ req.__("Download a backup"),
370
370
  req.csrfToken(),
371
371
  {
372
372
  btnClass: "btn-outline-primary",
@@ -385,21 +385,21 @@ router.get(
385
385
  },
386
386
  isRoot
387
387
  ? {
388
- type: "card",
389
- title: req.__("Automated backup"),
390
- contents: div(
391
- renderForm(backupForm, req.csrfToken()),
392
- a(
393
- { href: "/admin/auto-backup-list" },
394
- req.__("Restore/download automated backups &raquo;")
395
- ),
396
- script(
397
- domReady(
398
- `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
388
+ type: "card",
389
+ title: req.__("Automated backup"),
390
+ contents: div(
391
+ renderForm(backupForm, req.csrfToken()),
392
+ a(
393
+ { href: "/admin/auto-backup-list" },
394
+ req.__("Restore/download automated backups &raquo;")
395
+ ),
396
+ script(
397
+ domReady(
398
+ `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
399
+ )
399
400
  )
400
- )
401
- ),
402
- }
401
+ ),
402
+ }
403
403
  : { type: "blank", contents: "" },
404
404
  {
405
405
  type: "card",
@@ -407,7 +407,9 @@ router.get(
407
407
  contents: div(
408
408
  p(
409
409
  i(
410
- req.__("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
+ req.__(
411
+ "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)."
412
+ )
411
413
  )
412
414
  ),
413
415
  renderForm(aSnapshotForm, req.csrfToken()),
@@ -475,12 +477,17 @@ router.get(
475
477
  ol(
476
478
  li(req.__("Download one of the backups above")),
477
479
  li(
478
- a({ href: "/admin/clear-all" }, req.__("Clear this application")),
480
+ a(
481
+ { href: "/admin/clear-all" },
482
+ req.__("Clear this application")
483
+ ),
479
484
  " ",
480
485
  req.__("(tick all boxes)")
481
486
  ),
482
487
  li(
483
- req.__("When prompted to create the first user, click the link to restore a backup")
488
+ req.__(
489
+ "When prompted to create the first user, click the link to restore a backup"
490
+ )
484
491
  ),
485
492
  li(req.__("Select the downloaded backup file"))
486
493
  )
@@ -768,7 +775,8 @@ router.get(
768
775
  isAdmin,
769
776
  error_catcher(async (req, res) => {
770
777
  const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
771
- const latest = isRoot && (await get_latest_npm_version("@saltcorn/cli"));
778
+ const latest =
779
+ isRoot && (await get_latest_npm_version("@saltcorn/cli", 1000));
772
780
  const is_latest = packagejson.version === latest;
773
781
  const git_commit = getGitRevision();
774
782
  const can_update =
@@ -832,83 +840,71 @@ router.get(
832
840
  th(req.__("Saltcorn version")),
833
841
  td(
834
842
  packagejson.version +
835
- (isRoot && can_update
836
- ? post_btn(
837
- "/admin/upgrade",
838
- req.__("Upgrade"),
839
- req.csrfToken(),
840
- {
841
- btnClass: "btn-primary btn-sm",
842
- formClass: "d-inline",
843
- }
844
- )
845
- : isRoot && is_latest
843
+ (isRoot && can_update
844
+ ? post_btn(
845
+ "/admin/upgrade",
846
+ req.__("Upgrade"),
847
+ req.csrfToken(),
848
+ {
849
+ btnClass: "btn-primary btn-sm",
850
+ formClass: "d-inline",
851
+ }
852
+ )
853
+ : isRoot && is_latest
846
854
  ? span(
847
- { class: "badge bg-primary ms-2" },
848
- req.__("Latest")
849
- ) +
850
- post_btn(
851
- "/admin/check-for-upgrade",
852
- req.__("Check for updates"),
853
- req.csrfToken(),
854
- {
855
- btnClass: "btn-primary btn-sm px-1 py-0",
856
- formClass: "d-inline",
857
- }
858
- )
855
+ { class: "badge bg-primary ms-2" },
856
+ req.__("Latest")
857
+ ) +
858
+ post_btn(
859
+ "/admin/check-for-upgrade",
860
+ req.__("Check for updates"),
861
+ req.csrfToken(),
862
+ {
863
+ btnClass: "btn-primary btn-sm px-1 py-0",
864
+ formClass: "d-inline",
865
+ }
866
+ )
859
867
  : "")
860
868
  )
861
869
  ),
862
870
  git_commit &&
863
- tr(
864
- th(req.__("git commit")),
865
- td(
866
- a(
867
- {
868
- href:
869
- "https://github.com/saltcorn/saltcorn/commit/" +
870
- git_commit,
871
- },
872
- git_commit.substring(0, 6)
871
+ tr(
872
+ th(req.__("git commit")),
873
+ td(
874
+ a(
875
+ {
876
+ href:
877
+ "https://github.com/saltcorn/saltcorn/commit/" +
878
+ git_commit,
879
+ },
880
+ git_commit.substring(0, 6)
881
+ )
873
882
  )
874
- )
875
- ),
883
+ ),
876
884
  tr(th(req.__("Node.js version")), td(process.version)),
877
885
  tr(
878
886
  th(req.__("Database type")),
879
887
  td(db.isSQLite ? "SQLite " : "PostgreSQL ", dbversion)
880
888
  ),
881
- (isRoot?
882
- tr(
883
- th(req.__("Database host")),
884
- td(db.connectObj.host)
885
- )
886
- : ""),
887
- (isRoot?
888
- tr(
889
- th(req.__("Database port")),
890
- td(db.connectObj.port)
891
- )
892
- : ""),
893
- (isRoot?
894
- tr(
889
+ isRoot
890
+ ? tr(th(req.__("Database host")), td(db.connectObj.host))
891
+ : "",
892
+ isRoot
893
+ ? tr(th(req.__("Database port")), td(db.connectObj.port))
894
+ : "",
895
+ isRoot
896
+ ? tr(
895
897
  th(req.__("Database name")),
896
898
  td(db.connectObj.database)
897
- )
898
- : ""),
899
- (isRoot?
900
- tr(
901
- th(req.__("Database user")),
902
- td(db.connectObj.user)
903
- )
904
- : ""),
905
- tr(
906
- th(req.__("Database schema")),
907
- td(db.getTenantSchema())
908
- ),
899
+ )
900
+ : "",
901
+ isRoot
902
+ ? tr(th(req.__("Database user")), td(db.connectObj.user))
903
+ : "",
904
+ tr(th(req.__("Database schema")), td(db.getTenantSchema())),
909
905
  tr(
910
- th(req.__("Process uptime")),
911
- td(moment(get_process_init_time()).fromNow(true))
906
+ th(req.__("Process uptime")),
907
+ td(moment(get_process_init_time()).fromNow(true))
912
908
  )
913
909
  )
914
910
  ),
@@ -974,7 +970,9 @@ router.post(
974
970
  });
975
971
  child.on("exit", function (code, signal) {
976
972
  res.end(
977
- req.__(`Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`)
973
+ req.__(
974
+ `Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`
975
+ )
978
976
  );
979
977
  setTimeout(() => {
980
978
  if (process.send) process.send("RestartServer");
@@ -1178,8 +1176,8 @@ router.post(
1178
1176
  req.__(
1179
1177
  "LetsEncrypt SSL enabled. Restart for changes to take effect."
1180
1178
  ) +
1181
- " " +
1182
- a({ href: "/admin/system" }, req.__("Restart here"))
1179
+ " " +
1180
+ a({ href: "/admin/system" }, req.__("Restart here"))
1183
1181
  );
1184
1182
  res.redirect("/useradmin/ssl");
1185
1183
  } catch (e) {
@@ -1255,13 +1253,13 @@ router.get(
1255
1253
  contents: div(
1256
1254
  pass
1257
1255
  ? div(
1258
- { class: "alert alert-success", role: "alert" },
1259
- i({ class: "fas fa-check-circle fa-lg me-2" }),
1260
- h5(
1261
- { class: "d-inline" },
1262
- req.__("No errors detected during configuration check")
1256
+ { class: "alert alert-success", role: "alert" },
1257
+ i({ class: "fas fa-check-circle fa-lg me-2" }),
1258
+ h5(
1259
+ { class: "d-inline" },
1260
+ req.__("No errors detected during configuration check")
1261
+ )
1263
1262
  )
1264
- )
1265
1263
  : errors.map(mkError),
1266
1264
  (warnings || []).map(mkWarning)
1267
1265
  ),
@@ -1819,15 +1817,11 @@ router.post(
1819
1817
  * @returns {Promise<Form>} form
1820
1818
  */
1821
1819
  const dev_form = async (req) => {
1822
- return await config_fields_form({
1823
- req,
1824
- field_names: [
1825
- "development_mode",
1826
- "log_sql",
1827
- "log_level",
1828
- ],
1829
- action: "/admin/dev",
1830
- });
1820
+ return await config_fields_form({
1821
+ req,
1822
+ field_names: ["development_mode", "log_sql", "log_level"],
1823
+ action: "/admin/dev",
1824
+ });
1831
1825
  };
1832
1826
  /**
1833
1827
  * Developer Mode page
@@ -1836,19 +1830,19 @@ const dev_form = async (req) => {
1836
1830
  * @memberof module:routes/admin~routes/adminRouter
1837
1831
  */
1838
1832
  router.get(
1839
- "/dev",
1840
- isAdmin,
1841
- error_catcher(async (req, res) => {
1842
- const form = await dev_form(req);
1843
- send_admin_page({
1844
- res,
1845
- req,
1846
- active_sub: "Development",
1847
- contents: {
1848
- type: "card",
1849
- title: req.__("Development settings"),
1850
- contents: [
1851
- renderForm(form, req.csrfToken())/*,
1833
+ "/dev",
1834
+ isAdmin,
1835
+ error_catcher(async (req, res) => {
1836
+ const form = await dev_form(req);
1837
+ send_admin_page({
1838
+ res,
1839
+ req,
1840
+ active_sub: "Development",
1841
+ contents: {
1842
+ type: "card",
1843
+ title: req.__("Development settings"),
1844
+ contents: [
1845
+ renderForm(form, req.csrfToken()) /*,
1852
1846
  a(
1853
1847
  {
1854
1848
  id: "testemail",
@@ -1856,11 +1850,11 @@ router.get(
1856
1850
  class: "btn btn-primary",
1857
1851
  },
1858
1852
  req.__("Send test email")
1859
- ),*/
1860
- ],
1861
- },
1862
- });
1863
- })
1853
+ ),*/,
1854
+ ],
1855
+ },
1856
+ });
1857
+ })
1864
1858
  );
1865
1859
 
1866
1860
  /**
@@ -1870,27 +1864,27 @@ router.get(
1870
1864
  * @memberof module:routes/admin~routes/adminRouter
1871
1865
  */
1872
1866
  router.post(
1873
- "/dev",
1874
- isAdmin,
1875
- error_catcher(async (req, res) => {
1876
- const form = await dev_form(req);
1877
- form.validate(req.body);
1878
- if (form.hasErrors) {
1879
- send_admin_page({
1880
- res,
1881
- req,
1882
- active_sub: "Development",
1883
- contents: {
1884
- type: "card",
1885
- title: req.__("Development settings"),
1886
- contents: [renderForm(form, req.csrfToken())],
1887
- },
1888
- });
1889
- } else {
1890
- await save_config_from_form(form);
1891
- req.flash("success", req.__("Development mode settings updated"));
1892
- if (!req.xhr) res.redirect("/admin/dev");
1893
- else res.json({ success: "ok" });
1894
- }
1895
- })
1896
- );
1867
+ "/dev",
1868
+ isAdmin,
1869
+ error_catcher(async (req, res) => {
1870
+ const form = await dev_form(req);
1871
+ form.validate(req.body);
1872
+ if (form.hasErrors) {
1873
+ send_admin_page({
1874
+ res,
1875
+ req,
1876
+ active_sub: "Development",
1877
+ contents: {
1878
+ type: "card",
1879
+ title: req.__("Development settings"),
1880
+ contents: [renderForm(form, req.csrfToken())],
1881
+ },
1882
+ });
1883
+ } else {
1884
+ await save_config_from_form(form);
1885
+ req.flash("success", req.__("Development mode settings updated"));
1886
+ if (!req.xhr) res.redirect("/admin/dev");
1887
+ else res.json({ success: "ok" });
1888
+ }
1889
+ })
1890
+ );