@saltcorn/server 0.7.4-beta.0 → 0.7.4-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/routes/admin.js CHANGED
@@ -154,7 +154,7 @@ const email_form = async (req) => {
154
154
  return form;
155
155
  };
156
156
 
157
- const app_files_table = (file, req) =>
157
+ const app_files_table = (files, req) =>
158
158
  mkTable(
159
159
  [
160
160
  {
@@ -168,7 +168,7 @@ const app_files_table = (file, req) =>
168
168
  key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
169
169
  },
170
170
  ],
171
- [file]
171
+ files
172
172
  );
173
173
 
174
174
  /**
@@ -365,7 +365,7 @@ router.get(
365
365
  post_btn(
366
366
  "/admin/backup",
367
367
  i({ class: "fas fa-download me-2" }) +
368
- req.__("Download a backup"),
368
+ req.__("Download a backup"),
369
369
  req.csrfToken(),
370
370
  {
371
371
  btnClass: "btn-outline-primary",
@@ -384,21 +384,21 @@ router.get(
384
384
  },
385
385
  isRoot
386
386
  ? {
387
- type: "card",
388
- title: req.__("Automated backup"),
389
- contents: div(
390
- renderForm(backupForm, req.csrfToken()),
391
- a(
392
- { href: "/admin/auto-backup-list" },
393
- "Restore/download automated backups »"
394
- ),
395
- script(
396
- domReady(
397
- `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
398
- )
399
- )
387
+ type: "card",
388
+ title: req.__("Automated backup"),
389
+ contents: div(
390
+ renderForm(backupForm, req.csrfToken()),
391
+ a(
392
+ { href: "/admin/auto-backup-list" },
393
+ "Restore/download automated backups »"
400
394
  ),
401
- }
395
+ script(
396
+ domReady(
397
+ `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
398
+ )
399
+ )
400
+ ),
401
+ }
402
402
  : { type: "blank", contents: "" },
403
403
  {
404
404
  type: "card",
@@ -831,47 +831,47 @@ router.get(
831
831
  th(req.__("Saltcorn version")),
832
832
  td(
833
833
  packagejson.version +
834
- (isRoot && can_update
835
- ? post_btn(
836
- "/admin/upgrade",
837
- req.__("Upgrade"),
838
- req.csrfToken(),
839
- {
840
- btnClass: "btn-primary btn-sm",
841
- formClass: "d-inline",
842
- }
843
- )
844
- : isRoot && is_latest
834
+ (isRoot && can_update
835
+ ? post_btn(
836
+ "/admin/upgrade",
837
+ req.__("Upgrade"),
838
+ req.csrfToken(),
839
+ {
840
+ btnClass: "btn-primary btn-sm",
841
+ formClass: "d-inline",
842
+ }
843
+ )
844
+ : isRoot && is_latest
845
845
  ? span(
846
- { class: "badge bg-primary ms-2" },
847
- req.__("Latest")
848
- ) +
849
- post_btn(
850
- "/admin/check-for-upgrade",
851
- req.__("Check for updates"),
852
- req.csrfToken(),
853
- {
854
- btnClass: "btn-primary btn-sm px-1 py-0",
855
- formClass: "d-inline",
856
- }
857
- )
846
+ { class: "badge bg-primary ms-2" },
847
+ req.__("Latest")
848
+ ) +
849
+ post_btn(
850
+ "/admin/check-for-upgrade",
851
+ req.__("Check for updates"),
852
+ req.csrfToken(),
853
+ {
854
+ btnClass: "btn-primary btn-sm px-1 py-0",
855
+ formClass: "d-inline",
856
+ }
857
+ )
858
858
  : "")
859
859
  )
860
860
  ),
861
861
  git_commit &&
862
- tr(
863
- th(req.__("git commit")),
864
- td(
865
- a(
866
- {
867
- href:
868
- "https://github.com/saltcorn/saltcorn/commit/" +
869
- git_commit,
870
- },
871
- git_commit.substring(0, 6)
872
- )
862
+ tr(
863
+ th(req.__("git commit")),
864
+ td(
865
+ a(
866
+ {
867
+ href:
868
+ "https://github.com/saltcorn/saltcorn/commit/" +
869
+ git_commit,
870
+ },
871
+ git_commit.substring(0, 6)
873
872
  )
874
- ),
873
+ )
874
+ ),
875
875
  tr(th(req.__("Node.js version")), td(process.version)),
876
876
  tr(
877
877
  th(req.__("Database")),
@@ -981,7 +981,7 @@ router.post(
981
981
  res.attachment(fileName);
982
982
  const file = fs.createReadStream(fileName);
983
983
  file.on("end", function () {
984
- fs.unlink(fileName, function () {});
984
+ fs.unlink(fileName, function () { });
985
985
  });
986
986
  file.pipe(res);
987
987
  })
@@ -1004,7 +1004,7 @@ router.post(
1004
1004
  );
1005
1005
  if (err) req.flash("error", err);
1006
1006
  else req.flash("success", req.__("Successfully restored backup"));
1007
- fs.unlink(newPath, function () {});
1007
+ fs.unlink(newPath, function () { });
1008
1008
  res.redirect(`/admin`);
1009
1009
  })
1010
1010
  );
@@ -1079,7 +1079,7 @@ const clearAllForm = (req) =>
1079
1079
  {
1080
1080
  type: "Bool",
1081
1081
  name: "plugins",
1082
- label: req.__("Plugins"),
1082
+ label: req.__("Modules"),
1083
1083
  default: true,
1084
1084
  },
1085
1085
  ],
@@ -1149,8 +1149,8 @@ router.post(
1149
1149
  req.__(
1150
1150
  "LetsEncrypt SSL enabled. Restart for changes to take effect."
1151
1151
  ) +
1152
- " " +
1153
- a({ href: "/admin/system" }, req.__("Restart here"))
1152
+ " " +
1153
+ a({ href: "/admin/system" }, req.__("Restart here"))
1154
1154
  );
1155
1155
  res.redirect("/useradmin/ssl");
1156
1156
  } catch (e) {
@@ -1221,13 +1221,13 @@ router.get(
1221
1221
  contents: div(
1222
1222
  pass
1223
1223
  ? div(
1224
- { class: "alert alert-success", role: "alert" },
1225
- i({ class: "fas fa-check-circle fa-lg me-2" }),
1226
- h5(
1227
- { class: "d-inline" },
1228
- req.__("No errors detected during configuration check")
1229
- )
1224
+ { class: "alert alert-success", role: "alert" },
1225
+ i({ class: "fas fa-check-circle fa-lg me-2" }),
1226
+ h5(
1227
+ { class: "d-inline" },
1228
+ req.__("No errors detected during configuration check")
1230
1229
  )
1230
+ )
1231
1231
  : errors.map(mkError)
1232
1232
  ),
1233
1233
  },
@@ -1241,32 +1241,49 @@ router.get(
1241
1241
  })
1242
1242
  );
1243
1243
 
1244
- const dialogScript = `<script>
1245
- function swapEntryInputs(activeTab, activeInput, disabledTab, disabledInput) {
1246
- activeTab.addClass("active");
1247
- activeInput.removeClass("d-none");
1248
- activeInput.addClass("d-block");
1249
- activeInput.attr("name", "entryPoint");
1250
- disabledTab.removeClass("active");
1251
- disabledInput.removeClass("d-block");
1252
- disabledInput.addClass("d-none");
1253
- disabledInput.removeAttr("name");
1254
- }
1255
-
1256
- function showEntrySelect(type) {
1257
- const viewNavLin = $("#viewNavLinkID");
1258
- const pageNavLink = $("#pageNavLinkID");
1259
- const viewInp = $("#viewInputID");
1260
- const pageInp = $("#pageInputID");
1261
- if (type === "page") {
1262
- swapEntryInputs(pageNavLink, pageInp, viewNavLin, viewInp);
1244
+ const buildDialogScript = () => {
1245
+ return `<script>
1246
+ function swapEntryInputs(activeTab, activeInput, disabledTab, disabledInput) {
1247
+ activeTab.addClass("active");
1248
+ activeInput.removeClass("d-none");
1249
+ activeInput.addClass("d-block");
1250
+ activeInput.attr("name", "entryPoint");
1251
+ disabledTab.removeClass("active");
1252
+ disabledInput.removeClass("d-block");
1253
+ disabledInput.addClass("d-none");
1254
+ disabledInput.removeAttr("name");
1263
1255
  }
1264
- else if (type === "view") {
1265
- swapEntryInputs(viewNavLin, viewInp, pageNavLink, pageInp);
1256
+
1257
+ function showEntrySelect(type) {
1258
+ const viewNavLin = $("#viewNavLinkID");
1259
+ const pageNavLink = $("#pageNavLinkID");
1260
+ const viewInp = $("#viewInputID");
1261
+ const pageInp = $("#pageInputID");
1262
+ if (type === "page") {
1263
+ swapEntryInputs(pageNavLink, pageInp, viewNavLin, viewInp);
1264
+ }
1265
+ else if (type === "view") {
1266
+ swapEntryInputs(viewNavLin, viewInp, pageNavLink, pageInp);
1267
+ }
1268
+ $("#entryPointTypeID").attr("value", type);
1269
+ }
1270
+
1271
+ function handleMessages() {
1272
+ notifyAlert("This is still under development and might run longer.")
1273
+ ${
1274
+ getState().getConfig("apple_team_id") &&
1275
+ getState().getConfig("apple_team_id") !== "null"
1276
+ ? ""
1277
+ : `
1278
+ if ($("#iOSCheckboxId")[0].checked) {
1279
+ notifyAlert(
1280
+ "No 'Apple Team ID' is configured, I will try to build a project for the iOS simulator."
1281
+ );
1282
+ }`
1283
+ }
1266
1284
  }
1267
- $("#entryPointTypeID").attr("value", type);
1268
- }
1269
- </script>`;
1285
+ </script>`;
1286
+ };
1270
1287
 
1271
1288
  router.get(
1272
1289
  "/build-mobile-app",
@@ -1274,8 +1291,6 @@ router.get(
1274
1291
  error_catcher(async (req, res) => {
1275
1292
  const views = await View.find();
1276
1293
  const pages = await Page.find();
1277
- const execBuildMsg =
1278
- "This is still under development and might run longer.";
1279
1294
 
1280
1295
  send_admin_page({
1281
1296
  res,
@@ -1283,7 +1298,7 @@ router.get(
1283
1298
  active_sub: "Mobile app",
1284
1299
  headers: [
1285
1300
  {
1286
- headerTag: dialogScript,
1301
+ headerTag: buildDialogScript(),
1287
1302
  },
1288
1303
  ],
1289
1304
  contents: {
@@ -1313,13 +1328,13 @@ router.get(
1313
1328
  { class: "container ps-2" },
1314
1329
  div(
1315
1330
  { class: "row pb-2" },
1316
- div({ class: "col-sm-4 fw-bold" }, "Entry point"),
1317
- div({ class: "col-sm-4 fw-bold" }, "Platform"),
1331
+ div({ class: "col-sm-4 fw-bold" }, req.__("Entry point")),
1332
+ div({ class: "col-sm-4 fw-bold" }, req.__("Platform")),
1318
1333
  div(
1319
1334
  {
1320
1335
  class: "col-sm-1 fw-bold d-flex justify-content-center",
1321
1336
  },
1322
- "docker"
1337
+ req.__("docker")
1323
1338
  )
1324
1339
  ),
1325
1340
  div(
@@ -1336,7 +1351,7 @@ router.get(
1336
1351
  },
1337
1352
  div(
1338
1353
  { class: "nav-link active", id: "viewNavLinkID" },
1339
- "View"
1354
+ req.__("View")
1340
1355
  )
1341
1356
  ),
1342
1357
  li(
@@ -1346,7 +1361,7 @@ router.get(
1346
1361
  },
1347
1362
  div(
1348
1363
  { class: "nav-link", id: "pageNavLinkID" },
1349
- "Page"
1364
+ req.__("Page")
1350
1365
  )
1351
1366
  )
1352
1367
  ),
@@ -1383,7 +1398,7 @@ router.get(
1383
1398
  { class: "container ps-0" },
1384
1399
  div(
1385
1400
  { class: "row" },
1386
- div({ class: "col-sm-8" }, "android"),
1401
+ div({ class: "col-sm-8" }, req.__("android")),
1387
1402
  div(
1388
1403
  { class: "col-sm" },
1389
1404
  input({
@@ -1396,7 +1411,7 @@ router.get(
1396
1411
  ),
1397
1412
  div(
1398
1413
  { class: "row" },
1399
- div({ class: "col-sm-8" }, "iOS"),
1414
+ div({ class: "col-sm-8" }, req.__("iOS")),
1400
1415
  div(
1401
1416
  { class: "col-sm" },
1402
1417
  input({
@@ -1428,7 +1443,7 @@ router.get(
1428
1443
  for: "appNameInputId",
1429
1444
  class: "form-label fw-bold",
1430
1445
  },
1431
- "App file"
1446
+ req.__("App file")
1432
1447
  ),
1433
1448
  input({
1434
1449
  type: "text",
@@ -1448,7 +1463,7 @@ router.get(
1448
1463
  for: "serverURLInputId",
1449
1464
  class: "form-label fw-bold",
1450
1465
  },
1451
- "Server URL"
1466
+ req.__("Server URL")
1452
1467
  ),
1453
1468
  input({
1454
1469
  type: "text",
@@ -1463,12 +1478,12 @@ router.get(
1463
1478
  button(
1464
1479
  {
1465
1480
  type: "submit",
1466
- onClick: `notifyAlert('${execBuildMsg}'); press_store_button(this);`,
1481
+ onClick: `handleMessages(); press_store_button(this);`,
1467
1482
  class: "btn btn-warning",
1468
1483
  },
1469
1484
  i({ class: "fas fa-hammer pe-2" }),
1470
1485
 
1471
- "Build mobile app"
1486
+ req.__("Build mobile app")
1472
1487
  )
1473
1488
  )
1474
1489
  ),
@@ -1503,7 +1518,6 @@ router.post(
1503
1518
  req.flash("error", req.__("Only the android build supports docker."));
1504
1519
  return res.redirect("/admin/build-mobile-app");
1505
1520
  }
1506
- if (appFile && !appFile.endsWith(".apk")) appFile = `${appFile}.apk`;
1507
1521
  if (!serverURL || serverURL.length == 0) {
1508
1522
  serverURL = getState().getConfig("base_url") || "";
1509
1523
  }
@@ -1525,7 +1539,13 @@ router.post(
1525
1539
  ];
1526
1540
  if (useDocker) spawnParams.push("-d");
1527
1541
  if (androidPlatform) spawnParams.push("-p", "android");
1528
- if (iOSPlatform) spawnParams.push("-p", "ios");
1542
+ if (iOSPlatform) {
1543
+ spawnParams.push("-p", "ios");
1544
+ const teamId = getState().getConfig("apple_team_id");
1545
+ if (!teamId || teamId === "null") {
1546
+ spawnParams.push("--buildForEmulator");
1547
+ }
1548
+ }
1529
1549
  if (appFile) spawnParams.push("-a", appFile);
1530
1550
  if (serverURL) spawnParams.push("-s", serverURL);
1531
1551
  const child = spawn("saltcorn", spawnParams, {
@@ -1543,10 +1563,13 @@ router.post(
1543
1563
  });
1544
1564
  child.on("exit", async function (exitCode, signal) {
1545
1565
  if (exitCode === 0) {
1546
- const file = await File.from_existing_file(
1547
- appOut,
1548
- appFile ? appFile : "app-debug.apk",
1549
- req.user.id
1566
+ const files = await Promise.all(
1567
+ fs
1568
+ .readdirSync(appOut)
1569
+ .map(
1570
+ async (outFile) =>
1571
+ await File.from_existing_file(appOut, outFile, req.user.id)
1572
+ )
1550
1573
  );
1551
1574
  res.sendWrap(req.__(`Admin`), {
1552
1575
  above: [
@@ -1555,7 +1578,7 @@ router.post(
1555
1578
  title: req.__("Build Result"),
1556
1579
  contents: div("The build was successfully"),
1557
1580
  },
1558
- app_files_table(file, req),
1581
+ files.length > 0 ? app_files_table(files, req) : "",
1559
1582
  ],
1560
1583
  });
1561
1584
  } else
package/routes/fields.js CHANGED
@@ -348,13 +348,12 @@ const fieldFlow = (req) =>
348
348
  // todo sublabel
349
349
  input_type: "custom_html",
350
350
  attributes: {
351
- html: `<button type="button" id="test_formula_btn" onclick="test_formula('${
352
- table.name
353
- }', ${JSON.stringify(
354
- context.stored
355
- )})" class="btn btn-outline-secondary">${req.__(
356
- "Test"
357
- )}</button>
351
+ html: `<button type="button" id="test_formula_btn" onclick="test_formula('${table.name
352
+ }', ${JSON.stringify(
353
+ context.stored
354
+ )})" class="btn btn-outline-secondary">${req.__(
355
+ "Test"
356
+ )}</button>
358
357
  <div id="test_formula_output"></div>`,
359
358
  },
360
359
  }),
@@ -633,8 +632,7 @@ router.post(
633
632
  result = f(rows[0]);
634
633
  }
635
634
  res.send(
636
- `Result of running on row with id=${
637
- rows[0].id
635
+ `Result of running on row with id=${rows[0].id
638
636
  } is: <pre>${JSON.stringify(result)}</pre>`
639
637
  );
640
638
  } catch (e) {
@@ -788,22 +786,22 @@ router.post(
788
786
  field.type === "Key"
789
787
  ? getState().keyFieldviews
790
788
  : field.type === "File"
791
- ? getState().fileviews
792
- : field.type.fieldviews;
789
+ ? getState().fileviews
790
+ : field.type.fieldviews;
793
791
  if (!field.type || !fieldviews) {
794
792
  res.send("");
795
793
  return;
796
794
  }
797
795
  const fv = fieldviews[fieldview];
798
796
  if (!fv && field.type === "Key" && fieldview === "select")
799
- res.send("<select disabled></select>");
797
+ res.send(`<input readonly class="form-control form-select"></input>`);
800
798
  else if (!fv) res.send("");
801
799
  else if (fv.isEdit || fv.isFilter)
802
800
  res.send(
803
801
  fv.run(
804
802
  field.name,
805
803
  undefined,
806
- { disabled: true, ...configuration, ...(field.attributes || {}) },
804
+ { readonly: true, ...configuration, ...(field.attributes || {}) },
807
805
  "",
808
806
  false,
809
807
  field
@@ -54,9 +54,9 @@ const tableCard = (tables, req) => ({
54
54
  contents:
55
55
  (tables.length <= 1
56
56
  ? p(
57
- { class: "mt-2 pe-2" },
58
- i(req.__("Tables organise data by fields and rows."))
59
- )
57
+ { class: "mt-2 pe-2" },
58
+ i(req.__("Tables organise data by fields and rows."))
59
+ )
60
60
  : "") + tableTable(tables, req),
61
61
  bodyClass: "py-0 pe-0",
62
62
  footer: div(
@@ -107,13 +107,13 @@ const viewCard = (views, req) => ({
107
107
  contents:
108
108
  (views.length <= 1
109
109
  ? p(
110
- { class: "mt-2 pe-2" },
111
- i(
112
- req.__(
113
- "Views display data from tables. A view is a view template applied to a table, with configuration."
114
- )
110
+ { class: "mt-2 pe-2" },
111
+ i(
112
+ req.__(
113
+ "Views display data from tables. A view is a view pattern applied to a table, with configuration."
115
114
  )
116
115
  )
116
+ )
117
117
  : "") +
118
118
  (views.length > 0 ? viewTable(views, req) : p(req.__("No views"))),
119
119
 
@@ -160,13 +160,13 @@ const pageCard = (pages, req) => ({
160
160
  contents:
161
161
  (pages.length <= 1
162
162
  ? p(
163
- { class: "mt-2 pe-2" },
164
- i(
165
- req.__(
166
- "Pages are the web pages of your application built with a drag-and-drop builder. They have static content, and by embedding views, dynamic content."
167
- )
163
+ { class: "mt-2 pe-2" },
164
+ i(
165
+ req.__(
166
+ "Pages are the web pages of your application built with a drag-and-drop builder. They have static content, and by embedding views, dynamic content."
168
167
  )
169
168
  )
169
+ )
170
170
  : "") +
171
171
  (pages.length > 0
172
172
  ? pageTable(pages, req)
@@ -191,16 +191,16 @@ const filesTab = async (req) => {
191
191
  files.length === 0
192
192
  ? p(req.__("No files"))
193
193
  : mkTable(
194
- [
195
- {
196
- label: req.__("Filename"),
197
- key: (r) => link(`/files/serve/${r.id}`, r.filename),
198
- },
199
- { label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
200
- { label: req.__("Media type"), key: (r) => r.mimetype },
201
- ],
202
- files
203
- ),
194
+ [
195
+ {
196
+ label: req.__("Filename"),
197
+ key: (r) => link(`/files/serve/${r.id}`, r.filename),
198
+ },
199
+ { label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
200
+ { label: req.__("Media type"), key: (r) => r.mimetype },
201
+ ],
202
+ files
203
+ ),
204
204
  fileUploadForm(req)
205
205
  );
206
206
  };
@@ -244,30 +244,30 @@ const actionsTab = async (req, triggers) => {
244
244
  return div(
245
245
  { class: "pb-3" },
246
246
  triggers.length <= 1 &&
247
- p(
248
- { class: "mt-2 pe-2" },
249
- i(req.__("Triggers run actions in response to events."))
250
- ),
247
+ p(
248
+ { class: "mt-2 pe-2" },
249
+ i(req.__("Triggers run actions in response to events."))
250
+ ),
251
251
  triggers.length === 0
252
252
  ? p(req.__("No triggers"))
253
253
  : mkTable(
254
- [
255
- { label: req.__("Name"), key: "name" },
256
- { label: req.__("Action"), key: "action" },
257
- {
258
- label: req.__("Table or Channel"),
259
- key: (r) => r.table_name || r.channel,
260
- },
261
- {
262
- label: req.__("When"),
263
- key: (a) =>
264
- a.when_trigger === "API call"
265
- ? `API: ${base_url}api/action/${a.name}`
266
- : a.when_trigger,
267
- },
268
- ],
269
- triggers
270
- ),
254
+ [
255
+ { label: req.__("Name"), key: "name" },
256
+ { label: req.__("Action"), key: "action" },
257
+ {
258
+ label: req.__("Table or Channel"),
259
+ key: (r) => r.table_name || r.channel,
260
+ },
261
+ {
262
+ label: req.__("When"),
263
+ key: (a) =>
264
+ a.when_trigger === "API call"
265
+ ? `API: ${base_url}api/action/${a.name}`
266
+ : a.when_trigger,
267
+ },
268
+ ],
269
+ triggers
270
+ ),
271
271
  a(
272
272
  { href: "/actions/new", class: "btn btn-secondary my-3" },
273
273
  req.__("Add trigger")
@@ -385,15 +385,15 @@ const welcome_page = async (req) => {
385
385
  tabContents:
386
386
  triggers.length > 0
387
387
  ? {
388
- Triggers: await actionsTab(req, triggers),
389
- Files: await filesTab(req),
390
- Packs: packTab(req, packlist),
391
- }
388
+ Triggers: await actionsTab(req, triggers),
389
+ Files: await filesTab(req),
390
+ Packs: packTab(req, packlist),
391
+ }
392
392
  : {
393
- Packs: packTab(req, packlist),
394
- Triggers: await actionsTab(req, triggers),
395
- Files: await filesTab(req),
396
- },
393
+ Packs: packTab(req, packlist),
394
+ Triggers: await actionsTab(req, triggers),
395
+ Files: await filesTab(req),
396
+ },
397
397
  },
398
398
  {
399
399
  type: "card",
@@ -403,13 +403,13 @@ const welcome_page = async (req) => {
403
403
  tabContents:
404
404
  users.length > 4
405
405
  ? {
406
- Users: await usersTab(req, users, roleMap),
407
- Help: helpCard(req),
408
- }
406
+ Users: await usersTab(req, users, roleMap),
407
+ Help: helpCard(req),
408
+ }
409
409
  : {
410
- Help: helpCard(req),
411
- Users: await usersTab(req, users, roleMap),
412
- },
410
+ Help: helpCard(req),
411
+ Users: await usersTab(req, users, roleMap),
412
+ },
413
413
  },
414
414
  ],
415
415
  },
@@ -440,8 +440,8 @@ const no_views_logged_in = async (req, res) => {
440
440
  packagejson.version,
441
441
  latest
442
442
  ) +
443
- " " +
444
- a({ href: "/admin/system" }, req.__("Upgrade here"))
443
+ " " +
444
+ a({ href: "/admin/system" }, req.__("Upgrade here"))
445
445
  );
446
446
 
447
447
  res.sendWrap(req.__("Hello"), await welcome_page(req));