@saltcorn/server 0.6.1-beta.2 → 0.6.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/auth/index.js CHANGED
@@ -2,6 +2,19 @@
2
2
  /**
3
3
  *
4
4
  * @category server
5
- * @module auth/inde
5
+ * @module auth/index
6
6
  * @subcategory auth
7
- */
7
+ */
8
+
9
+ /**
10
+ * All files in the auth module.
11
+ * @namespace auth_overview
12
+ * @property {module:auth/admin} admin
13
+ * @property {module:auth/resetpw} resetpw
14
+ * @property {module:auth/roleadmin} roleadmin
15
+ * @property {module:auth/routes} routes
16
+ * @property {module:auth/testhelp} testhelp
17
+ *
18
+ * @category server
19
+ * @subcategory auth
20
+ */
package/index.js CHANGED
@@ -3,4 +3,20 @@
3
3
  *
4
4
  * @category server
5
5
  * @module server/index
6
- */
6
+ */
7
+
8
+ /**
9
+ * All files and sub-modules in the saltcorn-markup package.
10
+ * @namespace server_overview
11
+ * @property {module:auth/index~auth_overview} auth
12
+ * @property {module:markup/index~markup_overview} markup
13
+ * @property {module:routes/index~routes_overview} routes
14
+ *
15
+ * @property {module:app} app
16
+ * @property {module:errors} errors
17
+ * @property {module:load_plugins} load_plugins
18
+ * @property {module:serve} serve
19
+ * @property {module:systemd} systemd
20
+ * @property {module:wrapper} wrapper
21
+ * @category server
22
+ */
package/markup/index.js CHANGED
@@ -4,4 +4,17 @@
4
4
  * @category server
5
5
  * @module markup/index
6
6
  * @subcategory markup
7
- */
7
+ */
8
+
9
+ /**
10
+ * All files in the auth module.
11
+ * @namespace markup_overview
12
+ * @property {module:markup/admin} admin
13
+ * @property {module:markup/blockly} blockly
14
+ * @property {module:markup/expression_blurb} expression_blurb
15
+ * @property {module:markup/forms} forms
16
+ * @property {module:markup/plugin-store} plugin-store
17
+ *
18
+ * @category server
19
+ * @subcategory markup
20
+ */
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.6.1-beta.2",
3
+ "version": "0.6.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
- "@saltcorn/base-plugin": "0.6.1-beta.2",
10
- "@saltcorn/builder": "0.6.1-beta.2",
11
- "@saltcorn/data": "0.6.1-beta.2",
9
+ "@saltcorn/base-plugin": "0.6.2-beta.1",
10
+ "@saltcorn/builder": "0.6.2-beta.1",
11
+ "@saltcorn/data": "0.6.2-beta.1",
12
12
  "greenlock-express": "^4.0.3",
13
- "@saltcorn/markup": "0.6.1-beta.2",
14
- "@saltcorn/sbadmin2": "0.6.1-beta.2",
13
+ "@saltcorn/markup": "0.6.2-beta.1",
14
+ "@saltcorn/sbadmin2": "0.6.2-beta.1",
15
15
  "@socket.io/cluster-adapter": "^0.1.0",
16
16
  "@socket.io/sticky": "^1.0.1",
17
17
  "connect-flash": "^0.1.1",
@@ -33,6 +33,7 @@
33
33
  "live-plugin-manager": "^0.16.0",
34
34
  "moment": "^2.27.0",
35
35
  "node-fetch": "2.6.2",
36
+ "node-watch": "^0.7.2",
36
37
  "passport": "^0.4.1",
37
38
  "passport-custom": "^1.1.1",
38
39
  "passport-http-bearer": "^1.0.1",
@@ -51,7 +52,9 @@
51
52
  },
52
53
  "scripts": {
53
54
  "dev": "nodemon index.js",
54
- "test": "jest --runInBand"
55
+ "test": "jest --runInBand",
56
+ "tsc": "echo \"Error: no TypeScript support yet\"",
57
+ "clean": "echo \"Error: no TypeScript support yet\""
55
58
  },
56
59
  "jest": {
57
60
  "testEnvironment": "node",
@@ -62,7 +65,10 @@
62
65
  "coveragePathIgnorePatterns": [
63
66
  "/node_modules/",
64
67
  "/plugin_packages/"
65
- ]
68
+ ],
69
+ "moduleNameMapper": {
70
+ "@saltcorn/sqlite/(.*)": "@saltcorn/sqlite/dist/$1"
71
+ }
66
72
  },
67
73
  "publishConfig": {
68
74
  "access": "public"
@@ -306,6 +306,18 @@ function select_id(id) {
306
306
  function set_state_field(key, value) {
307
307
  pjax_to(updateQueryStringParameter(window.location.href, key, value));
308
308
  }
309
+
310
+ function check_state_field(that) {
311
+ const checked = that.checked;
312
+ const name = that.name;
313
+ const value = that.value;
314
+ var separator = window.location.href.indexOf("?") !== -1 ? "&" : "?";
315
+ let dest;
316
+ if (checked) dest = window.location.href + `${separator}${name}=${value}`;
317
+ else dest = window.location.href.replace(`${name}=${value}`, "");
318
+ pjax_to(dest.replace("&&", "&").replace("?&", "?"));
319
+ }
320
+
309
321
  function set_state_fields(kvs) {
310
322
  var newhref = window.location.href;
311
323
  Object.entries(kvs).forEach((kv) => {
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @category server
3
+ * @module restart_watcher
4
+ */
5
+
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+ const watch = require("node-watch");
9
+ const Plugin = require("@saltcorn/data/models/plugin");
10
+
11
+ /**
12
+ * packages that should trigger a server re-start
13
+ */
14
+ const relevantPackages = [
15
+ "db-common",
16
+ "postgres",
17
+ "saltcorn-data",
18
+ "saltcorn-markup",
19
+ "server",
20
+ "sqlite",
21
+ ];
22
+
23
+ /**
24
+ * excluded directories or file name patterns
25
+ */
26
+ const excludePatterns = [
27
+ /\/node_modules/,
28
+ /\/public/,
29
+ /\.git/,
30
+ /\.docs/,
31
+ /\.docs/,
32
+ /\migrations/,
33
+ /.*test.js/,
34
+ ];
35
+
36
+ /**
37
+ * get the root directory of the saltcorn project
38
+ * @returns {string} project root path
39
+ */
40
+ const getProjectRoot = () => {
41
+ return path.normalize(`${__dirname}/../../`);
42
+ };
43
+
44
+ /**
45
+ * get the packages directory of the saltcorn project
46
+ * @returns {string} packages path
47
+ */
48
+ const getPackagesDirectory = () => {
49
+ return `${getProjectRoot()}/packages`;
50
+ };
51
+
52
+ /**
53
+ * get all package directories that should trigger a server re-start
54
+ * @returns {string[]} list of paths to relevant directories
55
+ */
56
+ const getRelevantPackages = () => {
57
+ const packagesDir = getPackagesDirectory();
58
+ return relevantPackages.map((packageName) => `${packagesDir}/${packageName}`);
59
+ };
60
+
61
+ /**
62
+ * get all plugin directories that should trigger a server re-start
63
+ * @returns {string[]} list of paths to relevant directories
64
+ */
65
+ const getPluginDirectories = async () => {
66
+ const local_plugins = await Plugin.find({ source: "local" });
67
+ return local_plugins.map((p) => p.location);
68
+ };
69
+
70
+ const projectRoot = getProjectRoot();
71
+
72
+ const watchCfg = {
73
+ recursive: true,
74
+ filter(file, skip) {
75
+ for (const excludePattern of excludePatterns) {
76
+ if (excludePattern.test(file)) return skip;
77
+ }
78
+ return /(\.js|\.ts)$/.test(file);
79
+ },
80
+ };
81
+
82
+ let activeWatchers = [];
83
+
84
+ /**
85
+ * close all open file watchers
86
+ */
87
+ const closeWatchers = () => {
88
+ for (const activeWatcher of activeWatchers) {
89
+ if (!activeWatcher.isClosed()) {
90
+ activeWatcher.close();
91
+ }
92
+ }
93
+ };
94
+
95
+ /**
96
+ * register many file change listener and do re-starts on changes
97
+ * The listener calls process.exit() and assumes
98
+ * that pm2 does the actual re-start.
99
+ * @param {string[]} projectDirs package paths that should trigger re-starts.
100
+ * @param {string[]} pluginDirs plugin paths that should trigger re-starts.
101
+ */
102
+ const listenForChanges = (projectDirs, pluginDirs) => {
103
+ // watch project dirs
104
+ for (const projectDir of projectDirs) {
105
+ activeWatchers.push(
106
+ watch(
107
+ projectDir,
108
+ watchCfg,
109
+ // event is either 'update' or 'remove'
110
+ (event, file) => {
111
+ console.log("'%s' changed \n re-starting now", file);
112
+ closeWatchers();
113
+ spawnSync("npm", ["run", "tsc"], {
114
+ stdio: "inherit",
115
+ cwd: projectRoot,
116
+ });
117
+ process.exit();
118
+ }
119
+ )
120
+ );
121
+ }
122
+ // watch plugin dirs
123
+ for (const pluginDir of pluginDirs) {
124
+ activeWatchers.push(
125
+ watch(
126
+ pluginDir,
127
+ watchCfg,
128
+ // event is either 'update' or 'remove'
129
+ (event, file) => {
130
+ console.log("'%s' changed \n re-starting now", file);
131
+ closeWatchers();
132
+ process.exit();
133
+ }
134
+ )
135
+ );
136
+ }
137
+ };
138
+
139
+ module.exports = {
140
+ listenForChanges,
141
+ getProjectRoot,
142
+ getPackagesDirectory,
143
+ getRelevantPackages,
144
+ getPluginDirectories,
145
+ closeWatchers,
146
+ };
package/routes/fields.js CHANGED
@@ -38,11 +38,11 @@ const router = new Router();
38
38
  module.exports = router;
39
39
 
40
40
  /**
41
- * @param {object} req
42
- * @param {*} fkey_opts
43
- * @param {*} existing_names
44
- * @param {*} id
45
- * @param {*} hasData
41
+ * @param {object} req
42
+ * @param {*} fkey_opts
43
+ * @param {*} existing_names
44
+ * @param {*} id
45
+ * @param {*} hasData
46
46
  * @returns {Promise<Form>}
47
47
  */
48
48
  const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
@@ -137,7 +137,7 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
137
137
  };
138
138
 
139
139
  /**
140
- * @param {string} ctxType
140
+ * @param {string} ctxType
141
141
  * @returns {object}
142
142
  */
143
143
  const calcFieldType = (ctxType) =>
@@ -146,8 +146,8 @@ const calcFieldType = (ctxType) =>
146
146
  : { type: ctxType };
147
147
 
148
148
  /**
149
- * @param {*} attrs
150
- * @param {object} req
149
+ * @param {*} attrs
150
+ * @param {object} req
151
151
  * @returns {*}
152
152
  */
153
153
  const translateAttributes = (attrs, req) =>
@@ -156,8 +156,8 @@ const translateAttributes = (attrs, req) =>
156
156
  : attrs;
157
157
 
158
158
  /**
159
- * @param {*} attr
160
- * @param {*} req
159
+ * @param {*} attr
160
+ * @param {*} req
161
161
  * @returns {object}
162
162
  */
163
163
  const translateAttribute = (attr, req) => {
@@ -167,7 +167,7 @@ const translateAttribute = (attr, req) => {
167
167
  };
168
168
 
169
169
  /**
170
- * @param {*} req
170
+ * @param {*} req
171
171
  * @returns {Workflow}
172
172
  */
173
173
  const fieldFlow = (req) =>
@@ -697,12 +697,12 @@ router.post(
697
697
  if (!fv && field.type === "Key" && fieldview === "select")
698
698
  res.send("<select disabled></select>");
699
699
  else if (!fv) res.send("");
700
- else if (fv.isEdit)
700
+ else if (fv.isEdit || fv.isFilter)
701
701
  res.send(
702
702
  fv.run(
703
703
  field.name,
704
704
  undefined,
705
- { disabled: true, ...configuration },
705
+ { disabled: true, ...configuration, ...(field.attributes || {}) },
706
706
  "",
707
707
  false,
708
708
  field
@@ -21,8 +21,8 @@ const Trigger = require("@saltcorn/data/models/trigger");
21
21
  const { fileUploadForm } = require("../markup/forms");
22
22
 
23
23
  /**
24
- * @param {*} tables
25
- * @param {object} req
24
+ * @param {*} tables
25
+ * @param {object} req
26
26
  * @returns {Table}
27
27
  */
28
28
  const tableTable = (tables, req) =>
@@ -38,8 +38,8 @@ const tableTable = (tables, req) =>
38
38
  );
39
39
 
40
40
  /**
41
- * @param {*} tables
42
- * @param {object} req
41
+ * @param {*} tables
42
+ * @param {object} req
43
43
  * @returns {object}
44
44
  */
45
45
  const tableCard = (tables, req) => ({
@@ -67,8 +67,8 @@ const tableCard = (tables, req) => ({
67
67
  });
68
68
 
69
69
  /**
70
- * @param {*} views
71
- * @param {object} req
70
+ * @param {*} views
71
+ * @param {object} req
72
72
  * @returns {Table}
73
73
  */
74
74
  const viewTable = (views, req) =>
@@ -88,8 +88,8 @@ const viewTable = (views, req) =>
88
88
  );
89
89
 
90
90
  /**
91
- * @param {*} views
92
- * @param {object} req
91
+ * @param {*} views
92
+ * @param {object} req
93
93
  * @returns {object}
94
94
  */
95
95
  const viewCard = (views, req) => ({
@@ -119,8 +119,8 @@ const viewCard = (views, req) => ({
119
119
  });
120
120
 
121
121
  /**
122
- * @param {*} pages
123
- * @param {object} req
122
+ * @param {*} pages
123
+ * @param {object} req
124
124
  * @returns {Table}
125
125
  */
126
126
  const pageTable = (pages, req) =>
@@ -140,8 +140,8 @@ const pageTable = (pages, req) =>
140
140
  );
141
141
 
142
142
  /**
143
- * @param {*} pages
144
- * @param {object} req
143
+ * @param {*} pages
144
+ * @param {object} req
145
145
  * @returns {object}
146
146
  */
147
147
  const pageCard = (pages, req) => ({
@@ -172,11 +172,11 @@ const pageCard = (pages, req) => ({
172
172
  });
173
173
 
174
174
  /**
175
- * @param {object} req
175
+ * @param {object} req
176
176
  * @returns {Promise<div>}
177
177
  */
178
178
  const filesTab = async (req) => {
179
- const files = await File.find({}, { orderBy: "filename" });
179
+ const files = await File.find({}, { orderBy: "filename", cached: true });
180
180
  return div(
181
181
  files.length == 0
182
182
  ? p(req.__("No files"))
@@ -196,16 +196,10 @@ const filesTab = async (req) => {
196
196
  };
197
197
 
198
198
  /**
199
- * @param {object} req
199
+ * @param {object} req
200
200
  * @returns {Promise<div>}
201
201
  */
202
- const usersTab = async (req) => {
203
- const users = await User.find({}, { orderBy: "id" });
204
- const roles = await User.get_roles();
205
- var roleMap = {};
206
- roles.forEach((r) => {
207
- roleMap[r.id] = r.role;
208
- });
202
+ const usersTab = async (req, users, roleMap) => {
209
203
  return div(
210
204
  mkTable(
211
205
  [
@@ -219,20 +213,19 @@ const usersTab = async (req) => {
219
213
  users
220
214
  ),
221
215
  a(
222
- { href: `/useradmin/new`, class: "btn btn-secondary" },
216
+ { href: `/useradmin/new`, class: "btn btn-secondary my-3" },
223
217
  req.__("Create user")
224
218
  )
225
219
  );
226
220
  };
227
221
 
228
222
  /**
229
- * @param {object} req
223
+ * @param {object} req
230
224
  * @returns {Promise<div>}
231
225
  */
232
- const actionsTab = async (req) => {
233
- const triggers = await Trigger.findAllWithTableName();
234
-
226
+ const actionsTab = async (req, triggers) => {
235
227
  return div(
228
+ { class: "pb-3" },
236
229
  triggers.length <= 1 &&
237
230
  p(
238
231
  { class: "mt-2 pr-2" },
@@ -259,14 +252,73 @@ const actionsTab = async (req) => {
259
252
  triggers
260
253
  ),
261
254
  a(
262
- { href: "/actions/new", class: "btn btn-secondary btn-smj" },
255
+ { href: "/actions/new", class: "btn btn-secondary my-3" },
263
256
  req.__("Add trigger")
264
257
  )
265
258
  );
266
259
  };
260
+ const packTab = (req, packlist) =>
261
+ div(
262
+ { class: "pb-3 pt-2 pr-4" },
263
+ p(req.__("Instead of building, get up and running in no time with packs")),
264
+ p(
265
+ { class: "font-italic" },
266
+ req.__(
267
+ "Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
268
+ )
269
+ ),
270
+ mkTable(
271
+ [
272
+ { label: req.__("Name"), key: "name" },
273
+ {
274
+ label: req.__("Description"),
275
+ key: "description",
276
+ },
277
+ ],
278
+ packlist,
279
+ { noHeader: true }
280
+ ),
281
+ a(
282
+ { href: `/plugins?set=packs`, class: "btn btn-primary" },
283
+ req.__("Go to pack store »")
284
+ )
285
+ );
286
+
287
+ const helpCard = (req) =>
288
+ div(
289
+ { class: "pb-3 pt-2 pr-4" },
290
+ p(req.__("Confused?")),
291
+ p(
292
+ req.__(
293
+ "The Wiki contains the documentation and tutorials on installing and using Saltcorn"
294
+ )
295
+ ),
296
+ a(
297
+ {
298
+ href: `https://wiki.saltcorn.com/`,
299
+ class: "btn btn-primary",
300
+ },
301
+ req.__("Go to Wiki »")
302
+ ),
303
+ p(req.__("The YouTube channel has some video tutorials")),
304
+ a(
305
+ {
306
+ href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
307
+ class: "btn btn-secondary",
308
+ },
309
+ req.__("Go to YouTube »")
310
+ ),
311
+ div(
312
+ { class: "mt-3" },
313
+ a(
314
+ { href: `https://blog.saltcorn.com/` },
315
+ req.__("What's new? Read the blog »")
316
+ )
317
+ )
318
+ );
267
319
 
268
320
  /**
269
- * @param {object} req
321
+ * @param {object} req
270
322
  * @returns {Promise<object>}
271
323
  */
272
324
  const welcome_page = async (req) => {
@@ -275,10 +327,16 @@ const welcome_page = async (req) => {
275
327
  ...packs_available.slice(0, 5),
276
328
  { name: req.__("More..."), description: "" },
277
329
  ];
278
- const tables = await Table.find({}, { orderBy: "name" });
279
- const views = await View.find({});
280
- const pages = await Page.find({});
281
-
330
+ const tables = await Table.find({}, { cached: true });
331
+ const views = await View.find({}, { cached: true });
332
+ const pages = await Page.find({}, { cached: true });
333
+ const triggers = await Trigger.findAllWithTableName();
334
+ const users = await User.find({}, { orderBy: "id" });
335
+ const roles = await User.get_roles();
336
+ let roleMap = {};
337
+ roles.forEach((r) => {
338
+ roleMap[r.id] = r.role;
339
+ });
282
340
  return {
283
341
  above: [
284
342
  {
@@ -293,75 +351,37 @@ const welcome_page = async (req) => {
293
351
  {
294
352
  type: "card",
295
353
  //title: req.__("Install pack"),
296
- tabContents: {
297
- Packs: div(
298
- p(
299
- req.__(
300
- "Instead of building, get up and running in no time with packs"
301
- )
302
- ),
303
- p(
304
- { class: "font-italic" },
305
- req.__(
306
- "Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
307
- )
308
- ),
309
- mkTable(
310
- [
311
- { label: req.__("Name"), key: "name" },
312
- {
313
- label: req.__("Description"),
314
- key: "description",
315
- },
316
- ],
317
- packlist,
318
- { noHeader: true }
319
- ),
320
- a(
321
- { href: `/plugins?set=packs`, class: "btn btn-primary" },
322
- req.__("Go to pack store »")
323
- )
324
- ),
325
- Triggers: await actionsTab(req),
326
- Files: await filesTab(req),
327
- },
354
+ bodyClass: "py-0 pr-0",
355
+ class: "welcome-page-entity-list",
356
+
357
+ tabContents:
358
+ triggers.length > 0
359
+ ? {
360
+ Triggers: await actionsTab(req, triggers),
361
+ Files: await filesTab(req),
362
+ Packs: packTab(req, packlist),
363
+ }
364
+ : {
365
+ Packs: packTab(req, packlist),
366
+ Triggers: await actionsTab(req, triggers),
367
+ Files: await filesTab(req),
368
+ },
328
369
  },
329
370
  {
330
371
  type: "card",
331
372
  //title: req.__("Learn"),
332
- tabContents: {
333
- Help: div(
334
- p(req.__("Confused?")),
335
- p(
336
- req.__(
337
- "The Wiki contains the documentation and tutorials on installing and using Saltcorn"
338
- )
339
- ),
340
- a(
341
- {
342
- href: `https://wiki.saltcorn.com/`,
343
- class: "btn btn-primary",
344
- },
345
- req.__("Go to Wiki »")
346
- ),
347
- p(req.__("The YouTube channel has some video tutorials")),
348
- a(
349
- {
350
- href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
351
- class: "btn btn-secondary",
373
+ bodyClass: "py-0 pr-0",
374
+ class: "welcome-page-entity-list",
375
+ tabContents:
376
+ users.length > 4
377
+ ? {
378
+ Users: await usersTab(req, users, roleMap),
379
+ Help: helpCard(req),
380
+ }
381
+ : {
382
+ Help: helpCard(req),
383
+ Users: await usersTab(req, users, roleMap),
352
384
  },
353
- req.__("Go to YouTube »")
354
- ),
355
- div(
356
- { class: "mt-3" },
357
- a(
358
- { href: `https://blog.saltcorn.com/` },
359
- req.__("What's new? Read the blog »")
360
- )
361
- )
362
- ),
363
- Users: await usersTab(req),
364
- },
365
385
  },
366
386
  ],
367
387
  },
@@ -370,8 +390,8 @@ const welcome_page = async (req) => {
370
390
  };
371
391
 
372
392
  /**
373
- * @param {object} req
374
- * @param {object} res
393
+ * @param {object} req
394
+ * @param {object} res
375
395
  * @returns {Promise<void>}
376
396
  */
377
397
  const no_views_logged_in = async (req, res) => {
@@ -400,9 +420,9 @@ const no_views_logged_in = async (req, res) => {
400
420
  };
401
421
 
402
422
  /**
403
- * @param {number} role_id
404
- * @param {object} res
405
- * @param {object} req
423
+ * @param {number} role_id
424
+ * @param {object} res
425
+ * @param {object} req
406
426
  * @returns {Promise<boolean>}
407
427
  */
408
428
  const get_config_response = async (role_id, res, req) => {
@@ -420,8 +440,11 @@ const get_config_response = async (role_id, res, req) => {
420
440
  const contents = await db_page.run(req.query, { res, req });
421
441
 
422
442
  res.sendWrap(
423
- { title: db_page.title, description: db_page.description } ||
424
- `${pagename} page`,
443
+ {
444
+ title: db_page.title,
445
+ description: db_page.description,
446
+ bodyClass: "page_" + db.sqlsanitize(homeCfg),
447
+ } || `${pagename} page`,
425
448
  contents
426
449
  );
427
450
  } else res.redirect(homeCfg);
@@ -431,8 +454,8 @@ const get_config_response = async (role_id, res, req) => {
431
454
 
432
455
  /**
433
456
  * Function assigned to 'module.exports'.
434
- * @param {object} req
435
- * @param {object} res
457
+ * @param {object} req
458
+ * @param {object} res
436
459
  * @returns {Promise<void>}
437
460
  */
438
461
  module.exports = async (req, res) => {
package/routes/index.js CHANGED
@@ -4,6 +4,43 @@
4
4
  * @module routes/index
5
5
  * @subcategory routes
6
6
  */
7
+
8
+ /**
9
+ * All files in the routes module.
10
+ * @namespace routes_overview
11
+ * @property {module:routes/actions} actions
12
+ * @property {module:routes/admin} admin
13
+ * @property {module:routes/api} api
14
+ * @property {module:routes/config} config
15
+ * @property {module:routes/crashlog} crashlog
16
+ * @property {module:routes/delete} delete
17
+ * @property {module:routes/edit} edit
18
+ * @property {module:routes/eventlog} eventlog
19
+ * @property {module:routes/events} events
20
+ * @property {module:routes/fields} fields
21
+ * @property {module:routes/files} files
22
+ * @property {module:routes/homepage} homepage
23
+ * @property {module:routes/infoarch} infoarch
24
+ * @property {module:routes/library} library
25
+ * @property {module:routes/list} list
26
+ * @property {module:routes/menu} menu
27
+ * @property {module:routes/packs} packs
28
+ * @property {module:routes/page} page
29
+ * @property {module:routes/pageedit} pageedit
30
+ * @property {module:routes/plugins} plugins
31
+ * @property {module:routes/scapi} scapi
32
+ * @property {module:routes/search} search
33
+ * @property {module:routes/settings} settings
34
+ * @property {module:routes/tables} tables
35
+ * @property {module:routes/tenant} tenant
36
+ * @property {module:routes/utils} utils
37
+ * @property {module:routes/view} view
38
+ * @property {module:routes/viewedit} viewedit
39
+ *
40
+ * @category server
41
+ * @subcategory routes
42
+ */
43
+
7
44
  const table = require("./tables");
8
45
  const field = require("./fields");
9
46
  const list = require("./list");
package/routes/page.js CHANGED
@@ -18,6 +18,7 @@ const {
18
18
  const { add_edit_bar } = require("../markup/admin.js");
19
19
  const { traverseSync } = require("@saltcorn/data/models/layout");
20
20
  const { run_action_column } = require("@saltcorn/data/plugin-helper");
21
+ const db = require("@saltcorn/data/db");
21
22
 
22
23
  /**
23
24
  * @type {object}
@@ -47,7 +48,11 @@ router.get(
47
48
  const contents = await db_page.run(req.query, { res, req });
48
49
  const title = scan_for_page_title(contents, db_page.title);
49
50
  res.sendWrap(
50
- { title, description: db_page.description } || `${pagename} page`,
51
+ {
52
+ title,
53
+ description: db_page.description,
54
+ bodyClass: "page_" + db.sqlsanitize(pagename),
55
+ } || `${pagename} page`,
51
56
  add_edit_bar({
52
57
  role,
53
58
  title: db_page.name,
package/routes/view.js CHANGED
@@ -68,7 +68,7 @@ router.get(
68
68
  what: req.__("View"),
69
69
  url: `/viewedit/edit/${encodeURIComponent(view.name)}`,
70
70
  contents,
71
- req
71
+ req,
72
72
  })
73
73
  );
74
74
  })
@@ -92,18 +92,18 @@ router.post(
92
92
  res.send("");
93
93
  return;
94
94
  }
95
- let query = {};
95
+ let query = req.body || {};
96
96
  let row;
97
97
  let table;
98
98
  const sfs = await view.get_state_fields();
99
99
  for (const sf of sfs) {
100
- if (sf.required) {
100
+ if (sf.required && !query[sf.name]) {
101
101
  if (!row) {
102
102
  if (!table)
103
103
  table = await Table.findOne(view.table_id || view.exttable_name);
104
104
  row = await table.getRow({});
105
105
  }
106
- if(row) query[sf.name] = row[sf.name];
106
+ if (row) query[sf.name] = row[sf.name];
107
107
  }
108
108
  }
109
109
  const contents = await view.run(query, { req, res });
package/serve.js CHANGED
@@ -33,11 +33,14 @@ const { setTenant, getSessionStore } = require("./routes/utils");
33
33
  const passport = require("passport");
34
34
  const { authenticate } = require("passport");
35
35
  const View = require("@saltcorn/data/models/view");
36
+ const {
37
+ listenForChanges,
38
+ getRelevantPackages,
39
+ getPluginDirectories,
40
+ } = require("./restart_watcher");
36
41
 
37
42
  // helpful https://gist.github.com/jpoehls/2232358
38
-
39
43
  /**
40
- * @param {object} opts
41
44
  * @param {object} opts
42
45
  * @param {boolean} opts.disableMigrate
43
46
  * @param {boolean} [useClusterAdaptor = true]
@@ -146,6 +149,7 @@ module.exports =
146
149
  * @param {boolean} opts.watchReaper
147
150
  * @param {boolean} opts.disableScheduler
148
151
  * @param {number} opts.defaultNCPUs
152
+ * @param {boolean} opts.dev
149
153
  * @param {...*} opts.appargs
150
154
  * @returns {Promise<void>}
151
155
  */
@@ -154,8 +158,12 @@ module.exports =
154
158
  watchReaper,
155
159
  disableScheduler,
156
160
  defaultNCPUs,
161
+ dev,
157
162
  ...appargs
158
163
  } = {}) => {
164
+ if (dev && cluster.isMaster) {
165
+ listenForChanges(getRelevantPackages(), await getPluginDirectories());
166
+ }
159
167
  const useNCpus = process.env.SALTCORN_NWORKERS
160
168
  ? +process.env.SALTCORN_NWORKERS
161
169
  : defaultNCPUs;
package/wrapper.js CHANGED
@@ -240,12 +240,14 @@ module.exports = (version_tag) =>
240
240
  headers: get_headers(req, version_tag),
241
241
  role,
242
242
  req,
243
+ bodyClass: "auth",
243
244
  })
244
245
  );
245
246
  }
246
247
  };
247
248
  res.sendWrap = function (opts, ...html) {
248
249
  const title = typeof opts === "string" ? opts : opts.title;
250
+ const bodyClass = opts.bodyClass || "";
249
251
  const alerts = getFlashes(req);
250
252
  const state = getState();
251
253
  const layout = state.getLayout(req.user);
@@ -282,6 +284,7 @@ module.exports = (version_tag) =>
282
284
  headers: get_headers(req, version_tag, opts.description, pageHeaders),
283
285
  role,
284
286
  req,
287
+ bodyClass,
285
288
  })
286
289
  );
287
290
  };