@saltcorn/server 1.1.1-rc.4 → 1.1.2-beta.0

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/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,
@@ -505,83 +505,6 @@ router.post(
505
505
  })
506
506
  );
507
507
 
508
- function genWorkflowDiagram(steps) {
509
- const stepNames = steps.map((s) => s.name);
510
- const nodeLines = steps.map(
511
- (s) => ` ${s.mmname}["\`**${s.name}**
512
- ${s.action_name}\`"]:::wfstep${s.id}${s.only_if ? "@{ shape: hex }" : ""}`
513
- );
514
-
515
- nodeLines.unshift(` _Start@{ shape: circle, label: "Start" }`);
516
- const linkLines = [];
517
- let step_ix = 0;
518
- for (const step of steps) {
519
- if (step.initial_step)
520
- linkLines.push(
521
- ` _Start-- <i class="fas fa-plus add-btw-nodes btw-nodes-${0}-${
522
- step.name
523
- }"></i> ---${step.mmname}`
524
- );
525
- if (stepNames.includes(step.next_step)) {
526
- linkLines.push(
527
- ` ${step.mmname} -- <i class="fas fa-plus add-btw-nodes btw-nodes-${step.id}-${step.next_step}"></i> --- ${step.mmnext}`
528
- );
529
- } else if (step.next_step) {
530
- let found = false;
531
- for (const otherStep of stepNames)
532
- if (step.next_step.includes(otherStep)) {
533
- linkLines.push(
534
- ` ${step.mmname} --> ${WorkflowStep.mmescape(otherStep)}`
535
- );
536
- found = true;
537
- }
538
- if (!found) {
539
- linkLines.push(
540
- ` ${step.mmname}-- <a href="/actions/stepedit/${step.trigger_id}/${step.id}">Error: missing next step in ${step.mmname}</a> ---_End_${step.mmname}`
541
- );
542
- nodeLines.push(
543
- ` _End_${step.mmname}:::wfadd${step.id}@{ shape: circle, label: "<i class='fas fa-plus with-link'></i>" }`
544
- );
545
- }
546
- } else if (!step.next_step) {
547
- linkLines.push(` ${step.mmname} --> _End_${step.mmname}`);
548
- nodeLines.push(
549
- ` _End_${step.mmname}:::wfadd${step.id}@{ shape: circle, label: "<i class='fas fa-plus with-link'></i>" }`
550
- );
551
- }
552
- if (step.action_name === "ForLoop") {
553
- linkLines.push(
554
- ` ${step.mmname}-.->${WorkflowStep.mmescape(
555
- step.configuration.loop_body_initial_step
556
- )}`
557
- );
558
- }
559
- if (step.action_name === "EndForLoop") {
560
- // TODO this is not correct. improve.
561
- let forStep;
562
- for (let i = step_ix; i >= 0; i -= 1) {
563
- if (steps[i].action_name === "ForLoop") {
564
- forStep = steps[i];
565
- break;
566
- }
567
- }
568
- if (forStep) linkLines.push(` ${step.mmname} --> ${forStep.mmname}`);
569
- }
570
- step_ix += 1;
571
- }
572
- if (!steps.length || !steps.find((s) => s.initial_step)) {
573
- linkLines.push(` _Start --> _End`);
574
- nodeLines.push(
575
- ` _End:::wfaddstart@{ shape: circle, label: "<i class='fas fa-plus with-link'></i>" }`
576
- );
577
- }
578
- const fc =
579
- "flowchart TD\n" + nodeLines.join("\n") + "\n" + linkLines.join("\n");
580
- //console.log(fc);
581
-
582
- return fc;
583
- }
584
-
585
508
  const getWorkflowConfig = async (req, id, table, trigger) => {
586
509
  let steps = await WorkflowStep.find(
587
510
  { trigger_id: trigger.id },
@@ -627,7 +550,7 @@ const getWorkflowConfig = async (req, id, table, trigger) => {
627
550
  }
628
551
  return (
629
552
  copilot_form +
630
- pre({ class: "mermaid" }, genWorkflowDiagram(steps)) +
553
+ pre({ class: "mermaid" }, WorkflowStep.generate_diagram(steps)) +
631
554
  script(
632
555
  { defer: "defer" },
633
556
  `function tryAddWFNodes() {
@@ -1174,7 +1097,7 @@ router.post(
1174
1097
  fields: cfgFields,
1175
1098
  });
1176
1099
  }
1177
- form.validate(req.body);
1100
+ form.validate(req.body || {});
1178
1101
  if (form.hasErrors) {
1179
1102
  if (req.xhr) {
1180
1103
  res.status(400).json({ error: form.errorSummary });
@@ -1381,7 +1304,7 @@ router.post(
1381
1304
  * @function
1382
1305
  */
1383
1306
  router.get(
1384
- "/stepedit/:trigger_id/:step_id?",
1307
+ "/stepedit/:trigger_id{/:step_id}",
1385
1308
  isAdminOrHasConfigMinRole("min_role_edit_triggers"),
1386
1309
  error_catcher(async (req, res) => {
1387
1310
  const { trigger_id, step_id } = req.params;
@@ -1429,7 +1352,7 @@ router.post(
1429
1352
  const { trigger_id } = req.params;
1430
1353
  const trigger = await Trigger.findOne({ id: trigger_id });
1431
1354
  const form = await getWorkflowStepForm(trigger, req);
1432
- form.validate(req.body);
1355
+ form.validate(req.body || {});
1433
1356
  if (form.hasErrors) {
1434
1357
  if (req.xhr) {
1435
1358
  res.json({ error: form.errorSummary });
@@ -1528,7 +1451,7 @@ router.post(
1528
1451
  const { trigger_id } = req.params;
1529
1452
  const trigger = await Trigger.findOne({ id: trigger_id });
1530
1453
  await WorkflowStep.deleteForTrigger(trigger.id);
1531
- const description = req.body.description;
1454
+ const description = (req.body || {}).description;
1532
1455
  await Trigger.update(trigger.id, { description });
1533
1456
  const steps = await getState().functions.copilot_generate_workflow.run(
1534
1457
  description,
@@ -1700,7 +1623,7 @@ router.get(
1700
1623
  req,
1701
1624
  active_sub: "Workflow runs",
1702
1625
  page_title: req.__(`Workflow runs`),
1703
- sub2_page: trigger.name,
1626
+ sub2_page: trigger?.name,
1704
1627
  contents: {
1705
1628
  above: [
1706
1629
  {
@@ -1712,15 +1635,16 @@ router.get(
1712
1635
  { class: "table table-condensed w-unset" },
1713
1636
  tbody(
1714
1637
  tr(th("Run ID"), td(run.id)),
1715
- tr(
1716
- th("Trigger"),
1717
- td(
1718
- a(
1719
- { href: `/actions/configure/${trigger.id}` },
1720
- trigger.name
1638
+ trigger &&
1639
+ tr(
1640
+ th("Trigger"),
1641
+ td(
1642
+ a(
1643
+ { href: `/actions/configure/${trigger.id}` },
1644
+ trigger.name
1645
+ )
1721
1646
  )
1722
- )
1723
- ),
1647
+ ),
1724
1648
  tr(th("Started at"), td(localeDateTime(run.started_at))),
1725
1649
  tr(th("Started by user"), td(run.started_by)),
1726
1650
  tr(th("Status"), td(run.status)),
@@ -1850,7 +1774,7 @@ router.post(
1850
1774
  });
1851
1775
 
1852
1776
  const form = await getWorkflowStepUserForm(run, trigger, step, req);
1853
- form.validate(req.body);
1777
+ form.validate(req.body || {});
1854
1778
  if (form.hasErrors) {
1855
1779
  const title = "Fill form";
1856
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,
package/routes/api.js CHANGED
@@ -30,6 +30,7 @@ const Trigger = require("@saltcorn/data/models/trigger");
30
30
  const File = require("@saltcorn/data/models/file");
31
31
  //const load_plugins = require("../load_plugins");
32
32
  const passport = require("passport");
33
+ const path = require("path");
33
34
 
34
35
  const {
35
36
  readState,
@@ -164,7 +165,7 @@ router.post(
164
165
  ) {
165
166
  const queries = view.queries(false, req);
166
167
  if (Object.prototype.hasOwnProperty.call(queries, queryName)) {
167
- const { args } = req.body;
168
+ const { args } = req.body || {};
168
169
  const resp = await queries[queryName](...args, true);
169
170
  res.json({ success: resp, alerts: getFlashes(req) });
170
171
  } else {
@@ -192,7 +193,7 @@ router.post(
192
193
  );
193
194
 
194
195
  router.get(
195
- "/serve-files/*",
196
+ "/serve-files/*serve_path",
196
197
  //passport.authenticate("api-bearer", { session: false }),
197
198
  error_catcher(async (req, res, next) => {
198
199
  await passport.authenticate(
@@ -201,7 +202,7 @@ router.get(
201
202
  async function (err, user, info) {
202
203
  const role = req?.user?.role_id || user?.role_id || 100;
203
204
  const user_id = req?.user?.id || user?.id;
204
- const serve_path = req.params[0];
205
+ const serve_path = path.join(...req.params.serve_path);
205
206
  const file = await File.findOne(serve_path);
206
207
  if (
207
208
  file &&
@@ -214,7 +215,7 @@ router.get(
214
215
  res.set("Cache-Control", `${cacheability}, max-age=${maxAge}`);
215
216
  if (file.s3_store)
216
217
  res.status(404).json({ error: req.__("Not found") });
217
- else res.sendFile(file.location);
218
+ else res.sendFile(file.location, { dotfiles: "allow" });
218
219
  } else {
219
220
  res.status(404).json({ error: req.__("Not found") });
220
221
  }
@@ -466,7 +467,7 @@ router.all(
466
467
  if (accessAllowed(req, user, trigger)) {
467
468
  try {
468
469
  let resp;
469
- const row = req.method === "GET" ? req.query : req.body;
470
+ const row = req.method === "GET" ? req.query : req.body || {};
470
471
  if (trigger.action === "Workflow") {
471
472
  resp = await trigger.runWithoutRow({
472
473
  req,
@@ -479,7 +480,7 @@ router.all(
479
480
  const action = getState().actions[trigger.action];
480
481
  resp = await action.run({
481
482
  configuration: trigger.configuration,
482
- body: req.body,
483
+ body: req.body || {},
483
484
  row,
484
485
  req,
485
486
  user: user || req.user,
@@ -533,7 +534,7 @@ router.post(
533
534
  { session: false },
534
535
  async function (err, user, info) {
535
536
  if (accessAllowedWrite(req, user, table)) {
536
- const { _versions, ...row } = req.body;
537
+ const { _versions, ...row } = req.body || {};
537
538
  const fields = table.getFields();
538
539
  readState(row, fields, req);
539
540
  const errors = await prepare_insert_row(row, fields);
@@ -588,7 +589,7 @@ router.post(
588
589
  if (id === "undefined") {
589
590
  const pk_name = table.pk_name;
590
591
  //const fields = table.getFields();
591
- const row = req.body;
592
+ const row = req.body || {};
592
593
  //readState(row, fields);
593
594
  await table.deleteRows(
594
595
  { [pk_name]: row[pk_name] },
@@ -635,7 +636,7 @@ router.post(
635
636
  { session: false },
636
637
  async function (err, user, info) {
637
638
  if (accessAllowedWrite(req, user, table)) {
638
- const { _versions, ...row } = req.body;
639
+ const { _versions, ...row } = req.body || {};
639
640
  const fields = table.getFields();
640
641
  readState(row, fields, req);
641
642
  const errors = await prepare_update_row(table, row, id);
@@ -692,7 +693,7 @@ router.delete(
692
693
  if (id === "undefined") {
693
694
  const pk_name = table.pk_name;
694
695
  //const fields = table.getFields();
695
- const row = req.body;
696
+ const row = req.body || {};
696
697
  //readState(row, fields);
697
698
  await table.deleteRows(
698
699
  { [pk_name]: row[pk_name] },
package/routes/config.js CHANGED
@@ -48,8 +48,12 @@ router.post(
48
48
  const validKeyName = (k) =>
49
49
  k !== "_csrf" && k !== "constructor" && k !== "__proto__";
50
50
 
51
- for (const [k, v] of Object.entries(req.body)) {
52
- if (!state.isFixedConfig(k) && typeof v !== "undefined" && validKeyName(k)) {
51
+ for (const [k, v] of Object.entries(req.body || {})) {
52
+ if (
53
+ !state.isFixedConfig(k) &&
54
+ typeof v !== "undefined" &&
55
+ validKeyName(k)
56
+ ) {
53
57
  //TODO read value from type
54
58
  await state.setConfig(k, v);
55
59
  }
@@ -64,7 +68,7 @@ router.post(
64
68
  ? boolcheck
65
69
  : [boolcheck];
66
70
  for (const k of boolchecks) {
67
- if (typeof req.body[k] === "undefined" && validKeyName(k))
71
+ if (typeof (req.body || {})[k] === "undefined" && validKeyName(k))
68
72
  await state.setConfig(k, false);
69
73
  }
70
74
  res.json({ success: "ok" });
@@ -104,8 +104,8 @@ router.post(
104
104
  "/",
105
105
  error_catcher(async (req, res) => {
106
106
  const err = {
107
- stack: req.body.stack,
108
- message: `[JS] ${req.body.message}`,
107
+ stack: (req.body || {}).stack,
108
+ message: `[JS] ${(req.body || {}).message}`,
109
109
  };
110
110
  await Crash.create(err, req);
111
111
  res.json({});
@@ -298,7 +298,7 @@ router.post(
298
298
  isAdmin,
299
299
  error_catcher(async (req, res) => {
300
300
  const form = await customEventForm(req);
301
- form.validate(req.body);
301
+ form.validate(req.body || {});
302
302
  if (form.hasErrors) {
303
303
  send_events_page({
304
304
  res,
@@ -329,7 +329,7 @@ router.post(
329
329
  * @function
330
330
  */
331
331
  router.post(
332
- "/custom/delete/:name?",
332
+ "/custom/delete{/:name}",
333
333
  isAdmin,
334
334
  error_catcher(async (req, res) => {
335
335
  let { name } = req.params;
@@ -356,7 +356,7 @@ router.post(
356
356
  isAdmin,
357
357
  error_catcher(async (req, res) => {
358
358
  const form = await logSettingsForm(req);
359
- form.validate(req.body);
359
+ form.validate(req.body || {});
360
360
  if (form.hasErrors) {
361
361
  send_events_page({
362
362
  res,
package/routes/fields.js CHANGED
@@ -902,7 +902,7 @@ router.post(
902
902
  isAdminOrHasConfigMinRole("min_role_edit_tables"),
903
903
  error_catcher(async (req, res) => {
904
904
  const wf = fieldFlow(req);
905
- const wfres = await wf.run(req.body, req);
905
+ const wfres = await wf.run(req.body || {}, req);
906
906
  if (wfres.renderForm) {
907
907
  const table = Table.findOne({ id: wfres.context.table_id });
908
908
  res.sendWrap(req.__(`Field attributes`), {
@@ -953,7 +953,7 @@ router.post(
953
953
  "min_role_inspect_tables",
954
954
  ]),
955
955
  error_catcher(async (req, res) => {
956
- let { formula, tablename, stored } = req.body;
956
+ let { formula, tablename, stored } = req.body || {};
957
957
  if (stored === "false") stored = false;
958
958
 
959
959
  const table = Table.findOne({ name: tablename });
@@ -1017,7 +1017,7 @@ router.post(
1017
1017
  );
1018
1018
 
1019
1019
  const fields = table.getFields();
1020
- let row = { ...req.body };
1020
+ let row = { ...(req.body || {}) };
1021
1021
  if (row && Object.keys(row).length > 0) readState(row, fields);
1022
1022
 
1023
1023
  //need to get join fields from ownership into row
@@ -1280,7 +1280,7 @@ router.post(
1280
1280
  value = row && row[fieldName];
1281
1281
  }
1282
1282
 
1283
- const configuration = req.body.configuration;
1283
+ const configuration = (req.body || {}).configuration;
1284
1284
  if (!field) {
1285
1285
  res.send("");
1286
1286
  return;
@@ -1379,7 +1379,7 @@ router.post(
1379
1379
  agg_fieldview,
1380
1380
  agg_field,
1381
1381
  _columndef,
1382
- } = req.body;
1382
+ } = req.body || {};
1383
1383
  const table = Table.findOne({ name: tableName });
1384
1384
  if (agg_outcome_type && agg_fieldview) {
1385
1385
  const type = getState().types[agg_outcome_type];