@saltcorn/server 1.0.0-beta.9 → 1.0.0-rc.10

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.
@@ -14,7 +14,7 @@ const {
14
14
  } = require("../markup/admin.js");
15
15
  const { getState } = require("@saltcorn/data/db/state");
16
16
  const { div, a, i, text } = require("@saltcorn/markup/tags");
17
- const { mkTable, renderForm } = require("@saltcorn/markup");
17
+ const { mkTable, renderForm, post_delete_btn } = require("@saltcorn/markup");
18
18
  const Form = require("@saltcorn/data/models/form");
19
19
 
20
20
  /**
@@ -119,6 +119,15 @@ router.get(
119
119
  })
120
120
  : "",
121
121
  },
122
+ {
123
+ label: req.__("Delete"),
124
+ key: (r) =>
125
+ post_delete_btn(
126
+ `/site-structure/localizer/delete-lang/${r.locale}`,
127
+ req,
128
+ r.name
129
+ ),
130
+ },
122
131
  ],
123
132
  Object.values(cfgLangs)
124
133
  ),
@@ -234,7 +243,14 @@ router.post(
234
243
  isAdmin,
235
244
  error_catcher(async (req, res) => {
236
245
  const { lang, defstring } = req.params;
237
-
246
+ if (
247
+ lang === "__proto__" ||
248
+ defstring === "__proto__" ||
249
+ lang === "constructor"
250
+ ) {
251
+ res.redirect(`/`);
252
+ return;
253
+ }
238
254
  const cfgStrings = getState().getConfigCopy("localizer_strings");
239
255
  if (cfgStrings[lang]) cfgStrings[lang][defstring] = text(req.body.value);
240
256
  else cfgStrings[lang] = { [defstring]: text(req.body.value) };
@@ -280,3 +296,25 @@ router.post(
280
296
  }
281
297
  })
282
298
  );
299
+
300
+ /**
301
+ * @name post/localizer/save-lang
302
+ * @function
303
+ * @memberof module:routes/infoarch~infoarchRouter
304
+ * @function
305
+ */
306
+ router.post(
307
+ "/localizer/delete-lang/:lang",
308
+ isAdmin,
309
+ error_catcher(async (req, res) => {
310
+ const { lang } = req.params;
311
+
312
+ const cfgLangs = getState().getConfig("localizer_languages");
313
+ if (cfgLangs[lang]) {
314
+ delete cfgLangs[lang];
315
+ await getState().setConfig("localizer_languages", cfgLangs);
316
+ }
317
+ if (!req.xhr) res.redirect(`/site-structure/localizer`);
318
+ else res.json({ success: "ok" });
319
+ })
320
+ );
package/routes/list.js CHANGED
@@ -411,7 +411,7 @@ router.get(
411
411
  ajax_indicator(false);
412
412
  //if (item._versions) item._versions = +item._versions + 1;
413
413
  //data.resolve(fixKeys(item));
414
- if(resp.success &&typeof resp.success ==="number" && !row.id) {
414
+ if(resp.success &&(typeof resp.success ==="number" || typeof resp.success ==="string") && !row.id) {
415
415
  window.tabulator_table.updateRow(cell.getRow(), {id: resp.success});
416
416
  }
417
417
 
package/routes/menu.js CHANGED
@@ -269,6 +269,9 @@ const menuForm = async (req) => {
269
269
  name: "icon",
270
270
  class: "item-menu",
271
271
  input_type: "hidden",
272
+ showIf: {
273
+ type: ["View", "Page", "Page Group", "Link", "Header", "Action"],
274
+ },
272
275
  },
273
276
  {
274
277
  name: "tooltip",
@@ -183,6 +183,12 @@ const pageBuilderData = async (req, context) => {
183
183
  });
184
184
  }
185
185
  }
186
+ const actionsNotRequiringRow = Trigger.action_options({
187
+ notRequireRow: true,
188
+ apiNeverTriggers: true,
189
+ builtInLabel: "Page Actions",
190
+ builtIns: ["GoBack"],
191
+ });
186
192
  const library = (await Library.find({})).filter((l) => l.suitableFor("page"));
187
193
  const fixed_state_fields = {};
188
194
  for (const view of views) {
@@ -228,7 +234,7 @@ const pageBuilderData = async (req, context) => {
228
234
  images,
229
235
  pages,
230
236
  page_groups,
231
- actions,
237
+ actions: actionsNotRequiringRow,
232
238
  builtInActions: ["GoBack"],
233
239
  library,
234
240
  min_role: context.min_role,
@@ -642,7 +648,8 @@ router.post(
642
648
  const { pagename } = req.params;
643
649
 
644
650
  let redirectTarget =
645
- req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
651
+ req.query.on_done_redirect &&
652
+ is_relative_url("/" + req.query.on_done_redirect)
646
653
  ? `/${req.query.on_done_redirect}`
647
654
  : "/pageedit";
648
655
  const page = await Page.findOne({ name: pagename });
package/routes/plugins.js CHANGED
@@ -49,6 +49,8 @@ const {
49
49
  input,
50
50
  label,
51
51
  text,
52
+ script,
53
+ domReady,
52
54
  } = require("@saltcorn/markup/tags");
53
55
  const { search_bar } = require("@saltcorn/markup/helpers");
54
56
  const fs = require("fs");
@@ -59,6 +61,10 @@ const { sleep, removeNonWordChars } = require("@saltcorn/data/utils");
59
61
  const { loadAllPlugins } = require("../load_plugins");
60
62
  const npmFetch = require("npm-registry-fetch");
61
63
  const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
64
+ const {
65
+ supportedVersion,
66
+ isVersionSupported,
67
+ } = require("@saltcorn/plugins-loader/stable_versioning");
62
68
 
63
69
  /**
64
70
  * @type {object}
@@ -177,6 +183,8 @@ const get_store_items = async () => {
177
183
  has_auth: plugin.has_auth,
178
184
  unsafe: plugin.unsafe,
179
185
  source: plugin.source,
186
+ ready_for_mobile:
187
+ plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
180
188
  }))
181
189
  .filter((p) => !p.unsafe || isRoot || tenants_unsafe_plugins);
182
190
  const local_logins = installed_plugins
@@ -190,6 +198,8 @@ const get_store_items = async () => {
190
198
  github: plugin.source === "github",
191
199
  git: plugin.source === "git",
192
200
  local: plugin.source === "local",
201
+ ready_for_mobile:
202
+ plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
193
203
  }));
194
204
 
195
205
  const pack_items = packs_available.map((pack) => ({
@@ -274,7 +284,8 @@ const store_item_html = (req) => (item) => ({
274
284
  item.github && badge("GitHub"),
275
285
  item.git && badge("Git"),
276
286
  item.local && badge(req.__("Local")),
277
- item.installed && badge(req.__("Installed"))
287
+ item.installed && badge(req.__("Installed")),
288
+ item.ready_for_mobile && badge(req.__("Mobile"))
278
289
  ),
279
290
  div(item.description || ""),
280
291
  item.documentation_link
@@ -584,7 +595,9 @@ router.get(
584
595
  error_catcher(async (req, res) => {
585
596
  const { name } = req.params;
586
597
  const withoutOrg = name.replace(/^@saltcorn\//, "");
587
- const plugin = await Plugin.store_by_name(decodeURIComponent(withoutOrg));
598
+ let plugin = await Plugin.store_by_name(decodeURIComponent(withoutOrg));
599
+ if (!plugin)
600
+ plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
588
601
  if (!plugin) {
589
602
  getState().log(
590
603
  2,
@@ -603,12 +616,14 @@ router.get(
603
616
  res.set("Page-Title", req.__("%s versions", text(withoutOrg)));
604
617
  const versions = Object.keys(pkgInfo.versions);
605
618
  if (versions.length === 0) throw new Error(req.__("No versions found"));
619
+ const tags = pkgInfo["dist-tags"] || {};
606
620
  let selected = null;
607
621
  if (getState().plugins[plugin.name]) {
608
622
  const mod = await load_plugins.requirePlugin(plugin);
609
623
  if (mod) selected = mod.version;
610
624
  }
611
625
  if (!selected) selected = versions[versions.length - 1];
626
+ const scVersion = getState().scVersion;
612
627
  return res.send(
613
628
  form(
614
629
  {
@@ -618,6 +633,7 @@ router.get(
618
633
  input({ type: "hidden", name: "_csrf", value: req.csrfToken() }),
619
634
  div(
620
635
  { class: "form-group" },
636
+ // version
621
637
  label(
622
638
  {
623
639
  for: "version_select",
@@ -631,14 +647,49 @@ router.get(
631
647
  class: "form-control form-select",
632
648
  name: "version",
633
649
  },
634
- versions.map((version) =>
635
- option({
636
- id: `${version}_opt`,
637
- value: version,
638
- label: version,
639
- selected: version === selected,
640
- })
641
- )
650
+ versions
651
+ .filter((v) =>
652
+ isVersionSupported(v, pkgInfo.versions, scVersion)
653
+ )
654
+ .map((version) =>
655
+ option({
656
+ id: `${version}_opt`,
657
+ value: version,
658
+ label: version,
659
+ selected: version === selected,
660
+ })
661
+ )
662
+ ),
663
+ // tag
664
+ label(
665
+ {
666
+ for: "tag_select",
667
+ class: "form-label fw-bold mt-2",
668
+ },
669
+ req.__("Tags")
670
+ ),
671
+ select(
672
+ {
673
+ id: "tag_select",
674
+ class: "form-control form-select",
675
+ },
676
+ option({
677
+ id: "empty_opt",
678
+ value: "",
679
+ label: req.__("Select tag"),
680
+ selected: true,
681
+ }),
682
+ Object.keys(tags)
683
+ .filter((tag) =>
684
+ isVersionSupported(tags[tag], pkgInfo.versions, scVersion)
685
+ )
686
+ .map((tag) =>
687
+ option({
688
+ id: `${tag}_opt`,
689
+ value: tags[tag],
690
+ label: `${tag} (${tags[tag]})`,
691
+ })
692
+ )
642
693
  )
643
694
  ),
644
695
  div(
@@ -660,7 +711,19 @@ router.get(
660
711
  req.__("Install")
661
712
  )
662
713
  )
663
- )
714
+ ) +
715
+ script(
716
+ domReady(`
717
+ document.getElementById('tag_select').onchange = () => {
718
+ const version = document.getElementById('tag_select').value;
719
+ if (version) document.getElementById('version_select').value = version;
720
+ };
721
+ document.getElementById('version_select').onchange = () => {
722
+ const tagSelect = document.getElementById('tag_select');
723
+ tagSelect.value = '';
724
+ };
725
+ `)
726
+ )
664
727
  );
665
728
  } catch (error) {
666
729
  getState().log(
@@ -836,12 +899,13 @@ router.post(
836
899
  };
837
900
  await plugin.upsert();
838
901
  await load_plugins.loadPlugin(plugin);
902
+
903
+ getState().processSend({
904
+ refresh_plugin_cfg: plugin.name,
905
+ tenant: db.getTenantSchema(),
906
+ });
907
+ res.json({ success: "ok" });
839
908
  }
840
- getState().processSend({
841
- refresh_plugin_cfg: plugin.name,
842
- tenant: db.getTenantSchema(),
843
- });
844
- res.json({ success: "ok" });
845
909
  }
846
910
  })
847
911
  );
@@ -1168,9 +1232,22 @@ router.get(
1168
1232
  const update_permitted =
1169
1233
  db.getTenantSchema() === db.connectObj.default_schema &&
1170
1234
  plugin_db.source === "npm";
1171
- const latest =
1235
+
1236
+ let latest =
1172
1237
  update_permitted &&
1173
1238
  (await get_latest_npm_version(plugin_db.location, 1000));
1239
+ let engineInfos = await load_plugins.getEngineInfos(plugin_db); // with cache
1240
+ let forceFetch = true;
1241
+ if (latest && !engineInfos[latest]) {
1242
+ engineInfos = await load_plugins.getEngineInfos(plugin_db, forceFetch);
1243
+ forceFetch = false;
1244
+ }
1245
+ if (latest && !isVersionSupported(latest, engineInfos)) {
1246
+ latest = supportedVersion(
1247
+ latest,
1248
+ await load_plugins.getEngineInfos(plugin_db, forceFetch)
1249
+ );
1250
+ }
1174
1251
  const can_update = update_permitted && latest && mod.version !== latest;
1175
1252
  const can_select_version = update_permitted && plugin_db.source === "npm";
1176
1253
  let pkgjson;
@@ -1316,8 +1393,8 @@ router.get(
1316
1393
  error_catcher(async (req, res) => {
1317
1394
  const schema = db.getTenantSchema();
1318
1395
  if (schema === db.connectObj.default_schema) {
1319
- await upgrade_all_tenants_plugins((p, f) =>
1320
- load_plugins.loadPlugin(p, f)
1396
+ await upgrade_all_tenants_plugins((p, f, forceFetch) =>
1397
+ load_plugins.loadPlugin(p, f, forceFetch)
1321
1398
  );
1322
1399
  req.flash(
1323
1400
  "success",
@@ -1328,7 +1405,9 @@ router.get(
1328
1405
  } else {
1329
1406
  const installed_plugins = await Plugin.find({});
1330
1407
  for (const plugin of installed_plugins) {
1331
- await plugin.upgrade_version((p, f) => load_plugins.loadPlugin(p, f));
1408
+ await plugin.upgrade_version((p, f, forceFetch) =>
1409
+ load_plugins.loadPlugin(p, f, forceFetch)
1410
+ );
1332
1411
  }
1333
1412
  req.flash("success", req.__(`Modules up-to-date`));
1334
1413
  await restart_tenant(loadAllPlugins);
@@ -1354,7 +1433,12 @@ router.get(
1354
1433
  const { name } = req.params;
1355
1434
 
1356
1435
  const plugin = await Plugin.findOne({ name });
1357
- await plugin.upgrade_version((p, f) => load_plugins.loadPlugin(p, f));
1436
+ const versions = await load_plugins.getEngineInfos(plugin, true);
1437
+
1438
+ await plugin.upgrade_version(
1439
+ (p, f) => load_plugins.loadPlugin(p, f),
1440
+ supportedVersion("latest", versions, require("../package.json").version)
1441
+ );
1358
1442
  req.flash("success", req.__(`Module up-to-date`));
1359
1443
 
1360
1444
  res.redirect(`/plugins/info/${encodeURIComponent(plugin.name)}`);
@@ -1385,11 +1469,12 @@ router.post(
1385
1469
  res.redirect(`/plugins`);
1386
1470
  } else {
1387
1471
  try {
1388
- await load_plugins.loadAndSaveNewPlugin(
1472
+ const msgs = await load_plugins.loadAndSaveNewPlugin(
1389
1473
  plugin,
1390
1474
  schema === db.connectObj.default_schema || plugin.source === "github"
1391
1475
  );
1392
1476
  req.flash("success", req.__(`Module %s installed`, plugin.name));
1477
+ for (const msg of msgs) req.flash("warning", msg);
1393
1478
  res.redirect(`/plugins`);
1394
1479
  } catch (e) {
1395
1480
  req.flash("error", `${e.message}`);
@@ -1488,12 +1573,23 @@ router.post(
1488
1573
  res.redirect(`/plugins`);
1489
1574
  return;
1490
1575
  }
1491
- const msgs = await load_plugins.loadAndSaveNewPlugin(
1492
- plugin,
1493
- forceReInstall,
1494
- undefined,
1495
- req.__
1496
- );
1576
+
1577
+ let msgs = null;
1578
+ try {
1579
+ msgs = await load_plugins.loadAndSaveNewPlugin(
1580
+ plugin,
1581
+ forceReInstall,
1582
+ undefined,
1583
+ req.__
1584
+ );
1585
+ } catch (e) {
1586
+ req.flash(
1587
+ "error",
1588
+ e.message || req.__("Error installing module %s", plugin.name)
1589
+ );
1590
+ res.redirect(`/plugins`);
1591
+ return;
1592
+ }
1497
1593
  const plugin_module = getState().plugins[name];
1498
1594
  await sleep(1000); // Allow other workers to load this plugin
1499
1595
  await getState().refresh_views();
package/routes/scapi.js CHANGED
@@ -25,6 +25,7 @@ const {
25
25
  stateFieldsToWhere,
26
26
  readState,
27
27
  } = require("@saltcorn/data/plugin-helper");
28
+ const { getState } = require("@saltcorn/data/db/state");
28
29
 
29
30
  /**
30
31
  * @type {object}
@@ -307,3 +308,21 @@ router.get(
307
308
  )(req, res, next);
308
309
  })
309
310
  );
311
+
312
+ router.get(
313
+ "/reload",
314
+ error_catcher(async (req, res, next) => {
315
+ await passport.authenticate(
316
+ "api-bearer",
317
+ { session: false },
318
+ async function (err, user, info) {
319
+ if (accessAllowedRead(req, user)) {
320
+ await getState().refresh_plugins();
321
+ res.json({ success: true });
322
+ } else {
323
+ res.status(401).json({ error: req.__("Not authorized") });
324
+ }
325
+ }
326
+ )(req, res, next);
327
+ })
328
+ );
package/routes/search.js CHANGED
@@ -202,7 +202,12 @@ const runSearch = async ({ q, _page, table }, req, res) => {
202
202
  let tablesWithResults = [];
203
203
  let tablesConfigured = 0;
204
204
  for (const [tableName, viewName] of Object.entries(cfg)) {
205
- if (!viewName || viewName === "" || viewName === "search_table_description")
205
+ if (
206
+ !viewName ||
207
+ viewName === "" ||
208
+ viewName === "search_table_description" ||
209
+ tableName === "search_table_description"
210
+ )
206
211
  continue;
207
212
  tablesConfigured += 1;
208
213
  if (table && tableName !== table) continue;
package/routes/sync.js CHANGED
@@ -337,13 +337,11 @@ router.post(
337
337
  const { dir_name } = req.body;
338
338
  try {
339
339
  const rootFolder = await File.rootFolder();
340
- const syncDir = path.join(
341
- rootFolder.location,
342
- "mobile_app",
343
- "sync",
340
+ const syncDir = File.normalise_in_base(
341
+ path.join(rootFolder.location, "mobile_app", "sync"),
344
342
  dir_name
345
343
  );
346
- await fs.rm(syncDir, { recursive: true, force: true });
344
+ if (syncDir) await fs.rm(syncDir, { recursive: true, force: true });
347
345
  res.status(200).send("");
348
346
  } catch (error) {
349
347
  getState().log(2, `POST /sync/clean_sync_dir: '${error.message}'`);
package/routes/tables.js CHANGED
@@ -192,6 +192,9 @@ const tableForm = async (table, req) => {
192
192
  "if(!this.checked && !confirm('Are you sure? This will delete all history')) {this.checked = true; return false}",
193
193
  },
194
194
  type: "Bool",
195
+ help: {
196
+ topic: "Table history",
197
+ },
195
198
  },
196
199
  ...(table.name === "users"
197
200
  ? []
@@ -670,7 +673,10 @@ router.get(
670
673
  * @param {string} lbl
671
674
  * @returns {string}
672
675
  */
673
- const badge = (col, lbl) => `<span class="badge bg-${col}">${lbl}</span>&nbsp;`;
676
+ const badge = (col, lbl, title) =>
677
+ `<span ${
678
+ title ? `title="${title}" ` : ""
679
+ }class="badge bg-${col}">${lbl}</span>&nbsp;`;
674
680
 
675
681
  /**
676
682
  * @param {object} f
@@ -708,7 +714,11 @@ const attribBadges = (f) => {
708
714
  ].includes(k)
709
715
  )
710
716
  return;
711
- if (v || v === 0) s += badge("secondary", k);
717
+ if (Array.isArray(v) && !v.length) return;
718
+ const title = ["string", "number", "boolean"].includes(typeof v)
719
+ ? `${v}`
720
+ : null;
721
+ if (v || v === 0) s += badge("secondary", k, title);
712
722
  });
713
723
  }
714
724
  return s;
@@ -791,7 +801,7 @@ router.get(
791
801
  key: (r) => attribBadges(r),
792
802
  },
793
803
  { label: req.__("Variable name"), key: (t) => code(t.name) },
794
- ...(table.external || db.isSQLite
804
+ ...(table.external
795
805
  ? []
796
806
  : [
797
807
  {
@@ -1911,7 +1921,7 @@ router.post(
1911
1921
  const table = Table.findOne({ name });
1912
1922
 
1913
1923
  try {
1914
- await table.deleteRows({});
1924
+ await table.deleteRows({}, req.user);
1915
1925
  req.flash("success", req.__("Deleted all rows"));
1916
1926
  } catch (e) {
1917
1927
  req.flash("error", e.message);
@@ -766,7 +766,8 @@ router.post(
766
766
  )
767
767
  );
768
768
  let redirectTarget =
769
- req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
769
+ req.query.on_done_redirect &&
770
+ is_relative_url("/" + req.query.on_done_redirect)
770
771
  ? `/${req.query.on_done_redirect}`
771
772
  : "/viewedit";
772
773
  res.redirect(redirectTarget);
@@ -791,7 +792,8 @@ router.post(
791
792
  req.__("View %s duplicated as %s", view.name, newview.name)
792
793
  );
793
794
  let redirectTarget =
794
- req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
795
+ req.query.on_done_redirect &&
796
+ is_relative_url("/" + req.query.on_done_redirect)
795
797
  ? `/${req.query.on_done_redirect}`
796
798
  : "/viewedit";
797
799
  res.redirect(redirectTarget);
@@ -812,7 +814,8 @@ router.post(
812
814
  await View.delete({ id });
813
815
  req.flash("success", req.__("View deleted"));
814
816
  let redirectTarget =
815
- req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
817
+ req.query.on_done_redirect &&
818
+ is_relative_url("/" + req.query.on_done_redirect)
816
819
  ? `/${req.query.on_done_redirect}`
817
820
  : "/viewedit";
818
821
  res.redirect(redirectTarget);
@@ -910,7 +913,7 @@ router.post(
910
913
  req.flash("success", message);
911
914
  let redirectTarget =
912
915
  req.query.on_done_redirect &&
913
- is_relative_url(req.query.on_done_redirect)
916
+ is_relative_url("/" + req.query.on_done_redirect)
914
917
  ? `/${req.query.on_done_redirect}`
915
918
  : "/viewedit";
916
919
  res.redirect(redirectTarget);
package/serve.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @category server
5
5
  * @module serve
6
6
  */
7
- const runScheduler = require("@saltcorn/data/models/scheduler");
7
+ const { runScheduler } = require("@saltcorn/data/models/scheduler");
8
8
  const User = require("@saltcorn/data/models/user");
9
9
  const Plugin = require("@saltcorn/data/models/plugin");
10
10
  const db = require("@saltcorn/data/db");
@@ -27,7 +27,7 @@ const {
27
27
  loadAndSaveNewPlugin,
28
28
  loadPlugin,
29
29
  } = require("./load_plugins");
30
- const { getConfig } = require("@saltcorn/data/models/config");
30
+ const { getConfig, setConfig } = require("@saltcorn/data/models/config");
31
31
  const { migrate } = require("@saltcorn/data/migrate");
32
32
  const socketio = require("socket.io");
33
33
  const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
@@ -77,6 +77,17 @@ const ensureJwtSecret = () => {
77
77
  }
78
78
  };
79
79
 
80
+ /**
81
+ * Ensure the engines cache is up to date with the current sc version
82
+ */
83
+ const ensureEnginesCache = async () => {
84
+ const cacheScVersion = await getConfig("engines_cache_sc_version", "");
85
+ if (!cacheScVersion || cacheScVersion !== getState().scVersion) {
86
+ await setConfig("engines_cache", {});
87
+ await setConfig("engines_cache_sc_version", getState().scVersion);
88
+ }
89
+ };
90
+
80
91
  // helpful https://gist.github.com/jpoehls/2232358
81
92
  /**
82
93
  * @param {object} opts
@@ -222,7 +233,10 @@ module.exports =
222
233
  dev,
223
234
  ...appargs
224
235
  } = {}) => {
225
- ensureJwtSecret();
236
+ if (cluster.isMaster) {
237
+ ensureJwtSecret();
238
+ await ensureEnginesCache();
239
+ }
226
240
  process.on("unhandledRejection", (reason, p) => {
227
241
  console.error(reason, "Unhandled Rejection at Promise");
228
242
  });
package/tests/api.test.js CHANGED
@@ -2,6 +2,8 @@ const request = require("supertest");
2
2
  const getApp = require("../app");
3
3
  const Table = require("@saltcorn/data/models/table");
4
4
  const Trigger = require("@saltcorn/data/models/trigger");
5
+ const File = require("@saltcorn/data/models/file");
6
+ const fs = require("fs").promises;
5
7
 
6
8
  const Field = require("@saltcorn/data/models/field");
7
9
  const {
@@ -19,6 +21,19 @@ const User = require("@saltcorn/data/models/user");
19
21
 
20
22
  beforeAll(async () => {
21
23
  await resetToFixtures();
24
+ await File.ensure_file_store();
25
+ await File.from_req_files(
26
+ {
27
+ mimetype: "image/png",
28
+ name: "rick1.png",
29
+ mv: async (fnm) => {
30
+ await fs.writeFile(fnm, "nevergonnagiveyouup");
31
+ },
32
+ size: 245752,
33
+ },
34
+ 1,
35
+ 80
36
+ );
22
37
  });
23
38
  afterAll(db.close);
24
39
 
@@ -352,6 +367,20 @@ describe("API authentication", () => {
352
367
 
353
368
  .expect(succeedJsonWith((rows) => rows.length == 2));
354
369
  });
370
+ it("should not show file to public", async () => {
371
+ const app = await getApp();
372
+ await request(app)
373
+ .get("/api/serve-files/rick1.png")
374
+ .expect(respondJsonWith(404, (b) => b.error === "Not found"));
375
+ });
376
+ it("should show file to user", async () => {
377
+ const app = await getApp();
378
+ const u = await User.findOne({ id: 1 });
379
+ await request(app)
380
+ .get("/api/serve-files/rick1.png")
381
+ .set("Authorization", "Bearer " + u.api_token)
382
+ .expect(200);
383
+ });
355
384
  });
356
385
 
357
386
  describe("API action", () => {