@saltcorn/server 0.6.1-beta.0 → 0.6.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.
Files changed (53) hide show
  1. package/app.js +7 -0
  2. package/auth/admin.js +120 -5
  3. package/auth/index.js +7 -0
  4. package/auth/resetpw.js +22 -0
  5. package/auth/roleadmin.js +52 -0
  6. package/auth/routes.js +211 -2
  7. package/auth/testhelp.js +69 -0
  8. package/errors.js +14 -1
  9. package/fixture_persons.js +14 -0
  10. package/index.js +6 -0
  11. package/load_plugins.js +4 -3
  12. package/locales/en.json +7 -1
  13. package/markup/admin.js +97 -1
  14. package/markup/blockly.js +15 -0
  15. package/markup/expression_blurb.js +45 -0
  16. package/markup/forms.js +24 -0
  17. package/markup/index.js +7 -0
  18. package/markup/plugin-store.js +36 -0
  19. package/package.json +6 -6
  20. package/public/saltcorn-builder.css +1 -0
  21. package/public/saltcorn.js +5 -1
  22. package/routes/actions.js +53 -1
  23. package/routes/admin.js +97 -1
  24. package/routes/api.js +45 -10
  25. package/routes/config.js +18 -0
  26. package/routes/crashlog.js +31 -0
  27. package/routes/delete.js +19 -0
  28. package/routes/edit.js +19 -0
  29. package/routes/eventlog.js +65 -1
  30. package/routes/events.js +19 -0
  31. package/routes/fields.js +88 -0
  32. package/routes/files.js +62 -0
  33. package/routes/homepage.js +175 -80
  34. package/routes/index.js +7 -1
  35. package/routes/infoarch.js +56 -0
  36. package/routes/library.js +32 -0
  37. package/routes/list.js +28 -1
  38. package/routes/menu.js +45 -0
  39. package/routes/packs.js +53 -0
  40. package/routes/page.js +26 -0
  41. package/routes/pageedit.js +129 -3
  42. package/routes/plugins.js +156 -5
  43. package/routes/scapi.js +79 -23
  44. package/routes/search.js +51 -0
  45. package/routes/settings.js +27 -0
  46. package/routes/tables.js +148 -19
  47. package/routes/tenant.js +123 -31
  48. package/routes/utils.js +60 -1
  49. package/routes/view.js +37 -0
  50. package/routes/viewedit.js +114 -1
  51. package/serve.js +138 -88
  52. package/systemd.js +18 -1
  53. package/wrapper.js +4 -0
package/routes/tenant.js CHANGED
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @category server
3
+ * @module routes/tenant
4
+ * @subcategory routes
5
+ */
6
+
1
7
  const Router = require("express-promise-router");
2
8
  const Form = require("@saltcorn/data/models/form");
3
9
  const { getState, create_tenant } = require("@saltcorn/data/db/state");
@@ -28,7 +34,7 @@ const {
28
34
  } = require("@saltcorn/markup/tags");
29
35
  const db = require("@saltcorn/data/db");
30
36
  const url = require("url");
31
- const { loadAllPlugins } = require("../load_plugins");
37
+ const { loadAllPlugins, loadAndSaveNewPlugin } = require("../load_plugins");
32
38
  const { setTenant, isAdmin, error_catcher } = require("./utils.js");
33
39
  const User = require("@saltcorn/data/models/user");
34
40
  const File = require("@saltcorn/data/models/file");
@@ -40,12 +46,21 @@ const {
40
46
  } = require("../markup/admin.js");
41
47
  const { getConfig } = require("@saltcorn/data/models/config");
42
48
 
49
+ /**
50
+ * @type {object}
51
+ * @const
52
+ * @namespace tenantRouter
53
+ * @category server
54
+ * @subcategory routes
55
+ */
43
56
  const router = new Router();
44
57
  module.exports = router;
58
+
45
59
  /**
46
60
  * Declare Form to create Tenant
47
- * @param req - Request
61
+ * @param {object} req - Request
48
62
  * @returns {Form} - Saltcorn Form Declaration
63
+ * @category server
49
64
  */
50
65
  // TBD add form field email for tenant admin
51
66
  const tenant_form = (req) =>
@@ -65,12 +80,13 @@ const tenant_form = (req) =>
65
80
  },
66
81
  ],
67
82
  });
83
+
68
84
  /**
69
85
  * Check that user has role that allowed to create tenants
70
86
  * By default Admin role (id is 10) has rights to create tenants.
71
87
  * You can specify config variable "role_to_create_tenant" to overwrite this.
72
88
  * Note that only one role currently can have such rights simultaneously.
73
- * @param req - Request
89
+ * @param {object} req - Request
74
90
  * @returns {boolean} true if role has righs to create tenant
75
91
  */
76
92
  // TBD To allow few roles to create tenants - currently only one role has such rights simultaneously
@@ -79,10 +95,11 @@ const create_tenant_allowed = (req) => {
79
95
  const user_role = req.user ? req.user.role_id : 10;
80
96
  return user_role <= required_role;
81
97
  };
98
+
82
99
  /**
83
100
  * Check that String is IPv4 address
84
- * @param hostname
85
- * @returns {boolean|this is string[]}
101
+ * @param {string} hostname
102
+ * @returns {boolean|string[]}
86
103
  */
87
104
  // TBD not sure that false is correct return if type of is not string
88
105
  // TBD Add IPv6 support
@@ -90,6 +107,12 @@ const is_ip_address = (hostname) => {
90
107
  if (typeof hostname !== "string") return false;
91
108
  return hostname.split(".").every((s) => +s >= 0 && +s <= 255);
92
109
  };
110
+
111
+ /**
112
+ * @name get/create
113
+ * @function
114
+ * @memberof module:routes/tenant~tenantRouter
115
+ */
93
116
  router.get(
94
117
  "/create",
95
118
  setTenant,
@@ -116,31 +139,37 @@ router.get(
116
139
  "You are trying to create a tenant while connecting via an IP address rather than a domain. This will probably not work."
117
140
  )
118
141
  );
142
+ let create_tenant_warning = "";
119
143
  if (getState().getConfig("create_tenant_warning"))
120
- req.flash(
121
- "warning",
122
- h4(req.__("Warning")) +
123
- p(
144
+ create_tenant_warning = div(
145
+ {
146
+ class: "alert alert-warning alert-dismissible fade show mt-5",
147
+ role: "alert",
148
+ },
149
+ h4(req.__("Warning")),
150
+ p(
151
+ req.__(
152
+ "Hosting on this site is provided for free and with no guarantee of availability or security of your application. "
153
+ ) +
154
+ " " +
124
155
  req.__(
125
- "Hosting on this site is provided for free and with no guarantee of availability or security of your application. "
156
+ "This facility is intended solely for you to evaluate the suitability of Saltcorn. "
126
157
  ) +
127
- " " +
128
- req.__(
129
- "This facility is intended solely for you to evaluate the suitability of Saltcorn. "
130
- ) +
131
- " " +
132
- req.__(
133
- "If you would like to store private information that needs to be secure, please use self-hosted Saltcorn. "
134
- ) +
135
- " " +
136
- req.__(
137
- 'See <a href="https://github.com/saltcorn/saltcorn">GitHub repository</a> for instructions<p>'
138
- )
139
- )
158
+ " " +
159
+ req.__(
160
+ "If you would like to store private information that needs to be secure, please use self-hosted Saltcorn. "
161
+ ) +
162
+ " " +
163
+ req.__(
164
+ 'See <a href="https://github.com/saltcorn/saltcorn">GitHub repository</a> for instructions<p>'
165
+ )
166
+ )
140
167
  );
168
+
141
169
  res.sendWrap(
142
170
  req.__("Create application"),
143
- renderForm(tenant_form(req), req.csrfToken()) +
171
+ create_tenant_warning +
172
+ renderForm(tenant_form(req), req.csrfToken()) +
144
173
  p(
145
174
  { class: "mt-2" },
146
175
  req.__("To login to a previously created application, go to: "),
@@ -153,8 +182,8 @@ router.get(
153
182
  );
154
183
  /**
155
184
  * Return URL of new Tenant
156
- * @param req - Request
157
- * @param subdomain - Tenant Subdomain name string
185
+ * @param {object} req - Request
186
+ * @param {string} subdomain - Tenant Subdomain name string
158
187
  * @returns {string}
159
188
  */
160
189
  const getNewURL = (req, subdomain) => {
@@ -169,8 +198,12 @@ const getNewURL = (req, subdomain) => {
169
198
 
170
199
  return newurl;
171
200
  };
201
+
172
202
  /**
173
203
  * Create Tenant UI Main logic
204
+ * @name post/create
205
+ * @function
206
+ * @memberof module:routes/tenant~tenantRouter
174
207
  */
175
208
  router.post(
176
209
  "/create",
@@ -218,7 +251,19 @@ router.post(
218
251
  );
219
252
  } else {
220
253
  const newurl = getNewURL(req, subdomain);
221
- await create_tenant(subdomain, loadAllPlugins, newurl);
254
+ await create_tenant(
255
+ subdomain,
256
+ loadAllPlugins,
257
+ newurl,
258
+ false,
259
+ loadAndSaveNewPlugin
260
+ );
261
+ let new_url_create = newurl;
262
+ const hasTemplate = getState().getConfig("tenant_template");
263
+ if (hasTemplate) {
264
+ new_url_create += "auth/create_first_user";
265
+ }
266
+
222
267
  res.sendWrap(
223
268
  req.__("Create application"),
224
269
  div(
@@ -226,14 +271,25 @@ router.post(
226
271
 
227
272
  div(
228
273
  { class: "my-3", style: "font-size: 22px" },
229
- a({ href: newurl, class: "new-tenant-link" }, newurl)
274
+ a(
275
+ { href: new_url_create, class: "new-tenant-link" },
276
+ new_url_create
277
+ )
230
278
  ),
231
279
  p(
232
280
  req.__(
233
281
  "Please click the above link now to create the first user."
234
282
  ) +
235
283
  " " +
236
- req.__("Use this link to revisit your application at any time.")
284
+ hasTemplate
285
+ ? req.__(
286
+ 'Use this link: <a href="%s">%s</a> to revisit your application at any time.',
287
+ newurl,
288
+ newurl
289
+ )
290
+ : req.__(
291
+ "Use this link to revisit your application at any time."
292
+ )
237
293
  )
238
294
  )
239
295
  );
@@ -241,8 +297,12 @@ router.post(
241
297
  }
242
298
  })
243
299
  );
300
+
244
301
  /**
245
302
  * List tenants HTTP GET Web UI
303
+ * @name get/list
304
+ * @function
305
+ * @memberof module:routes/tenant~tenantRouter
246
306
  */
247
307
  router.get(
248
308
  "/list",
@@ -306,14 +366,28 @@ router.get(
306
366
  });
307
367
  })
308
368
  );
369
+
370
+ /**
371
+ * @param {object} req
372
+ * @returns {Form}
373
+ */
309
374
  const tenant_settings_form = (req) =>
310
375
  config_fields_form({
311
376
  req,
312
- field_names: ["role_to_create_tenant", "create_tenant_warning"],
377
+ field_names: [
378
+ "role_to_create_tenant",
379
+ "create_tenant_warning",
380
+ "tenant_template",
381
+ ],
313
382
  action: "/tenant/settings",
314
383
  submitLabel: req.__("Save"),
315
384
  });
316
385
 
386
+ /**
387
+ * @name get/settings
388
+ * @function
389
+ * @memberof module:routes/tenant~tenantRouter
390
+ */
317
391
  router.get(
318
392
  "/settings",
319
393
  setTenant,
@@ -343,6 +417,12 @@ router.get(
343
417
  });
344
418
  })
345
419
  );
420
+
421
+ /**
422
+ * @name post/settings
423
+ * @function
424
+ * @memberof module:routes/tenant~tenantRouter
425
+ */
346
426
  router.post(
347
427
  "/settings",
348
428
  setTenant,
@@ -371,7 +451,7 @@ router.post(
371
451
  );
372
452
  /**
373
453
  * Get Tenant info
374
- * @param subdomain
454
+ * @param {string} subdomain
375
455
  * @returns {Promise<*>}
376
456
  */
377
457
  // TBD move this function data layer or just separate file(reengineering)
@@ -413,8 +493,12 @@ const get_tenant_info = async (subdomain) => {
413
493
  return info;
414
494
  });
415
495
  };
496
+
416
497
  /**
417
498
  * Tenant info
499
+ * @name get/info/:subdomain
500
+ * @function
501
+ * @memberof module:routes/tenant~tenantRouter
418
502
  */
419
503
  router.get(
420
504
  "/info/:subdomain",
@@ -554,9 +638,13 @@ router.get(
554
638
  });
555
639
  })
556
640
  );
641
+
557
642
  /**
558
643
  * Show Information about Tenant
559
644
  * /tenant/info
645
+ * @name post/info/:subdomain
646
+ * @function
647
+ * @memberof module:routes/tenant~tenantRouter
560
648
  */
561
649
  router.post(
562
650
  "/info/:subdomain",
@@ -583,8 +671,12 @@ router.post(
583
671
  res.redirect(`/tenant/info/${text(subdomain)}`);
584
672
  })
585
673
  );
674
+
586
675
  /**
587
676
  * Execute Delete of tenant
677
+ * @name post/delete/:sub
678
+ * @function
679
+ * @memberof module:routes/tenant~tenantRouter
588
680
  */
589
681
  router.post(
590
682
  "/delete/:sub",
package/routes/utils.js CHANGED
@@ -1,5 +1,11 @@
1
- const { sqlsanitize } = require("@saltcorn/data/db/internal.js");
1
+ /**
2
+ * @category server
3
+ * @module routes/utils
4
+ * @subcategory routes
5
+ */
6
+
2
7
  const db = require("@saltcorn/data/db");
8
+ const { sqlsanitize } = db;
3
9
  const {
4
10
  getState,
5
11
  getTenant,
@@ -13,6 +19,12 @@ const is = require("contractis/is");
13
19
  const { validateHeaderName, validateHeaderValue } = require("http");
14
20
  const Crash = require("@saltcorn/data/models/crash");
15
21
 
22
+ /**
23
+ * @param {object} req
24
+ * @param {object} res
25
+ * @param {function} next
26
+ * @returns {void}
27
+ */
16
28
  function loggedIn(req, res, next) {
17
29
  if (req.user && req.user.id && req.user.tenant === db.getTenantSchema()) {
18
30
  next();
@@ -22,6 +34,12 @@ function loggedIn(req, res, next) {
22
34
  }
23
35
  }
24
36
 
37
+ /**
38
+ * @param {object} req
39
+ * @param {object} res
40
+ * @param {function} next
41
+ * @returns {void}
42
+ */
25
43
  function isAdmin(req, res, next) {
26
44
  if (
27
45
  req.user &&
@@ -35,12 +53,24 @@ function isAdmin(req, res, next) {
35
53
  }
36
54
  }
37
55
 
56
+ /**
57
+ * @param {object} req
58
+ * @param {object} res
59
+ * @param {string} state
60
+ * @returns {void}
61
+ */
38
62
  const setLanguage = (req, res, state) => {
39
63
  if (req.user && req.user.language) {
40
64
  req.setLocale(req.user.language);
41
65
  }
42
66
  set_custom_http_headers(res, state);
43
67
  };
68
+
69
+ /**
70
+ * @param {object} res
71
+ * @param {string} state
72
+ * @returns {void}
73
+ */
44
74
  const set_custom_http_headers = (res, state) => {
45
75
  const hdrs = (state || getState()).getConfig("custom_http_headers");
46
76
  if (!hdrs) return;
@@ -59,6 +89,10 @@ const set_custom_http_headers = (res, state) => {
59
89
  }
60
90
  };
61
91
 
92
+ /**
93
+ * @param {object} req
94
+ * @returns {string}
95
+ */
62
96
  const get_tenant_from_req = (req) => {
63
97
  if (req.subdomains && req.subdomains.length > 0) return req.subdomains[0];
64
98
 
@@ -71,6 +105,11 @@ const get_tenant_from_req = (req) => {
71
105
  }
72
106
  };
73
107
 
108
+ /**
109
+ * @param {object} req
110
+ * @param {object} res
111
+ * @param {function} next
112
+ */
74
113
  const setTenant = (req, res, next) => {
75
114
  if (db.is_it_multi_tenant()) {
76
115
  const other_domain = get_other_domain_tenant(req.hostname);
@@ -101,6 +140,10 @@ const setTenant = (req, res, next) => {
101
140
  }
102
141
  };
103
142
 
143
+ /**
144
+ * @param {object} req
145
+ * @returns {input}
146
+ */
104
147
  const csrfField = (req) =>
105
148
  input({
106
149
  type: "hidden",
@@ -108,9 +151,19 @@ const csrfField = (req) =>
108
151
  value: req.csrfToken ? req.csrfToken() : req,
109
152
  });
110
153
 
154
+ /**
155
+ * @param {function} fn
156
+ * @returns {function}
157
+ */
111
158
  const error_catcher = (fn) => (request, response, next) => {
112
159
  Promise.resolve(fn(request, response, next)).catch(next);
113
160
  };
161
+
162
+ /**
163
+ * @param {string|object} contents
164
+ * @param {string} viewname
165
+ * @returns {string}
166
+ */
114
167
  const scan_for_page_title = (contents, viewname) => {
115
168
  let scanstr = "";
116
169
  try {
@@ -126,8 +179,14 @@ const scan_for_page_title = (contents, viewname) => {
126
179
  return viewname;
127
180
  };
128
181
 
182
+ /**
183
+ * @returns {string}
184
+ */
129
185
  const getGitRevision = () => db.connectObj.git_commit;
130
186
 
187
+ /**
188
+ * @returns {session|cookieSession}
189
+ */
131
190
  const getSessionStore = () => {
132
191
  if (getState().getConfig("cookie_sessions", false)) {
133
192
  return cookieSession({
package/routes/view.js CHANGED
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @category server
3
+ * @module routes/view
4
+ * @subcategory routes
5
+ */
6
+
1
7
  const Router = require("express-promise-router");
2
8
 
3
9
  const View = require("@saltcorn/data/models/view");
@@ -14,9 +20,22 @@ const {
14
20
  } = require("../routes/utils.js");
15
21
  const { add_edit_bar } = require("../markup/admin.js");
16
22
 
23
+ /**
24
+ * @type {object}
25
+ * @const
26
+ * @namespace viewRouter
27
+ * @category server
28
+ * @subcategory routes
29
+ */
17
30
  const router = new Router();
18
31
  module.exports = router;
19
32
 
33
+ /**
34
+ * @name get/:viewname
35
+ * @function
36
+ * @memberof module:routes/view~viewRouter
37
+ * @function
38
+ */
20
39
  router.get(
21
40
  "/:viewname",
22
41
  setTenant,
@@ -55,6 +74,12 @@ router.get(
55
74
  })
56
75
  );
57
76
 
77
+ /**
78
+ * @name post/:viewname/preview
79
+ * @function
80
+ * @memberof module:routes/view~viewRouter
81
+ * @function
82
+ */
58
83
  router.post(
59
84
  "/:viewname/preview",
60
85
  setTenant,
@@ -87,6 +112,12 @@ router.post(
87
112
  })
88
113
  );
89
114
 
115
+ /**
116
+ * @name post/:viewname/:route
117
+ * @function
118
+ * @memberof module:routes/view~viewRouter
119
+ * @function
120
+ */
90
121
  router.post(
91
122
  "/:viewname/:route",
92
123
  setTenant,
@@ -107,6 +138,12 @@ router.post(
107
138
  })
108
139
  );
109
140
 
141
+ /**
142
+ * @name post/:viewname
143
+ * @function
144
+ * @memberof module:routes/view~viewRouter
145
+ * @function
146
+ */
110
147
  router.post(
111
148
  "/:viewname",
112
149
  setTenant,