@saltcorn/server 0.6.1-beta.3 → 0.6.2-beta.2
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/app.js +9 -13
- package/auth/admin.js +5 -22
- package/auth/index.js +15 -2
- package/auth/roleadmin.js +6 -16
- package/auth/routes.js +23 -52
- package/index.js +17 -1
- package/locales/en.json +20 -1
- package/markup/admin.js +17 -0
- package/markup/index.js +14 -1
- package/package.json +22 -9
- package/public/saltcorn.css +2 -2
- package/public/saltcorn.js +12 -0
- package/restart_watcher.js +155 -0
- package/routes/actions.js +1 -15
- package/routes/admin.js +6 -23
- package/routes/api.js +51 -60
- package/routes/config.js +0 -1
- package/routes/crashlog.js +1 -4
- package/routes/delete.js +1 -2
- package/routes/edit.js +1 -2
- package/routes/eventlog.js +2 -15
- package/routes/events.js +0 -1
- package/routes/fields.js +14 -22
- package/routes/files.js +144 -64
- package/routes/homepage.js +131 -105
- package/routes/index.js +37 -0
- package/routes/infoarch.js +12 -19
- package/routes/library.js +1 -4
- package/routes/list.js +2 -5
- package/routes/menu.js +6 -8
- package/routes/packs.js +2 -8
- package/routes/page.js +7 -8
- package/routes/pageedit.js +18 -30
- package/routes/plugins.js +21 -34
- package/routes/scapi.js +155 -184
- package/routes/search.js +6 -10
- package/routes/settings.js +1 -2
- package/routes/tables.js +1 -22
- package/routes/tenant.js +1 -9
- package/routes/utils.js +19 -19
- package/routes/view.js +11 -9
- package/routes/viewedit.js +18 -29
- package/s3storage.js +167 -0
- package/serve.js +10 -2
- package/wrapper.js +3 -0
package/routes/files.js
CHANGED
|
@@ -8,6 +8,7 @@ const Router = require("express-promise-router");
|
|
|
8
8
|
const File = require("@saltcorn/data/models/file");
|
|
9
9
|
const User = require("@saltcorn/data/models/user");
|
|
10
10
|
const { getState } = require("@saltcorn/data/db/state");
|
|
11
|
+
const s3storage = require("../s3storage");
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
14
|
mkTable,
|
|
@@ -16,7 +17,7 @@ const {
|
|
|
16
17
|
post_btn,
|
|
17
18
|
post_delete_btn,
|
|
18
19
|
} = require("@saltcorn/markup");
|
|
19
|
-
const {
|
|
20
|
+
const { isAdmin, error_catcher } = require("./utils.js");
|
|
20
21
|
const {
|
|
21
22
|
span,
|
|
22
23
|
h5,
|
|
@@ -37,6 +38,11 @@ const {
|
|
|
37
38
|
const { csrfField } = require("./utils");
|
|
38
39
|
const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
|
|
39
40
|
const { strictParseInt } = require("@saltcorn/data/plugin-helper");
|
|
41
|
+
const {
|
|
42
|
+
send_files_page,
|
|
43
|
+
config_fields_form,
|
|
44
|
+
save_config_from_form,
|
|
45
|
+
} = require("../markup/admin");
|
|
40
46
|
|
|
41
47
|
/**
|
|
42
48
|
* @type {object}
|
|
@@ -49,9 +55,9 @@ const router = new Router();
|
|
|
49
55
|
module.exports = router;
|
|
50
56
|
|
|
51
57
|
/**
|
|
52
|
-
* @param {*} file
|
|
53
|
-
* @param {*} roles
|
|
54
|
-
* @param {*} req
|
|
58
|
+
* @param {*} file
|
|
59
|
+
* @param {*} roles
|
|
60
|
+
* @param {*} req
|
|
55
61
|
* @returns {Form}
|
|
56
62
|
*/
|
|
57
63
|
const editFileRoleForm = (file, roles, req) =>
|
|
@@ -70,61 +76,53 @@ const editFileRoleForm = (file, roles, req) =>
|
|
|
70
76
|
*/
|
|
71
77
|
router.get(
|
|
72
78
|
"/",
|
|
73
|
-
setTenant,
|
|
74
79
|
isAdmin,
|
|
75
80
|
error_catcher(async (req, res) => {
|
|
76
81
|
const rows = await File.find({}, { orderBy: "filename" });
|
|
77
82
|
const roles = await User.get_roles();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
rows,
|
|
122
|
-
{ hover: true }
|
|
123
|
-
),
|
|
124
|
-
fileUploadForm(req),
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
],
|
|
83
|
+
send_files_page({
|
|
84
|
+
res,
|
|
85
|
+
req,
|
|
86
|
+
active_sub: "Files",
|
|
87
|
+
contents: {
|
|
88
|
+
type: "card",
|
|
89
|
+
contents: [
|
|
90
|
+
mkTable(
|
|
91
|
+
[
|
|
92
|
+
{
|
|
93
|
+
label: req.__("Filename"),
|
|
94
|
+
key: (r) =>
|
|
95
|
+
div(
|
|
96
|
+
{ "data-inline-edit-dest-url": `/files/setname/${r.id}` },
|
|
97
|
+
r.filename
|
|
98
|
+
),
|
|
99
|
+
},
|
|
100
|
+
{ label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
|
|
101
|
+
{ label: req.__("Media type"), key: (r) => r.mimetype },
|
|
102
|
+
{
|
|
103
|
+
label: req.__("Role to access"),
|
|
104
|
+
key: (r) => editFileRoleForm(r, roles, req),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
label: req.__("Link"),
|
|
108
|
+
key: (r) => link(`/files/serve/${r.id}`, req.__("Link")),
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: req.__("Download"),
|
|
112
|
+
key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
label: req.__("Delete"),
|
|
116
|
+
key: (r) =>
|
|
117
|
+
post_delete_btn(`/files/delete/${r.id}`, req, r.filename),
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
rows,
|
|
121
|
+
{ hover: true }
|
|
122
|
+
),
|
|
123
|
+
fileUploadForm(req),
|
|
124
|
+
],
|
|
125
|
+
},
|
|
128
126
|
});
|
|
129
127
|
})
|
|
130
128
|
);
|
|
@@ -137,7 +135,6 @@ router.get(
|
|
|
137
135
|
*/
|
|
138
136
|
router.get(
|
|
139
137
|
"/download/:id",
|
|
140
|
-
setTenant,
|
|
141
138
|
error_catcher(async (req, res) => {
|
|
142
139
|
const role = req.isAuthenticated() ? req.user.role_id : 10;
|
|
143
140
|
const user_id = req.user && req.user.id;
|
|
@@ -145,7 +142,8 @@ router.get(
|
|
|
145
142
|
const file = await File.findOne({ id });
|
|
146
143
|
if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
|
|
147
144
|
res.type(file.mimetype);
|
|
148
|
-
|
|
145
|
+
if (file.s3_store) s3storage.serveObject(file, res, true);
|
|
146
|
+
else res.download(file.location, file.filename);
|
|
149
147
|
} else {
|
|
150
148
|
req.flash("warning", req.__("Not authorized"));
|
|
151
149
|
res.redirect("/");
|
|
@@ -161,7 +159,6 @@ router.get(
|
|
|
161
159
|
*/
|
|
162
160
|
router.get(
|
|
163
161
|
"/serve/:id",
|
|
164
|
-
setTenant,
|
|
165
162
|
error_catcher(async (req, res) => {
|
|
166
163
|
const role = req.isAuthenticated() ? req.user.role_id : 10;
|
|
167
164
|
const user_id = req.user && req.user.id;
|
|
@@ -181,7 +178,8 @@ router.get(
|
|
|
181
178
|
res.type(file.mimetype);
|
|
182
179
|
const cacheability = file.min_role_read === 10 ? "public" : "private";
|
|
183
180
|
res.set("Cache-Control", `${cacheability}, max-age=86400`);
|
|
184
|
-
|
|
181
|
+
if (file.s3_store) s3storage.serveObject(file, res, false);
|
|
182
|
+
else res.sendFile(file.location);
|
|
185
183
|
} else {
|
|
186
184
|
req.flash("warning", req.__("Not authorized"));
|
|
187
185
|
res.redirect("/");
|
|
@@ -197,7 +195,6 @@ router.get(
|
|
|
197
195
|
*/
|
|
198
196
|
router.post(
|
|
199
197
|
"/setrole/:id",
|
|
200
|
-
setTenant,
|
|
201
198
|
isAdmin,
|
|
202
199
|
error_catcher(async (req, res) => {
|
|
203
200
|
const { id } = req.params;
|
|
@@ -225,7 +222,6 @@ router.post(
|
|
|
225
222
|
*/
|
|
226
223
|
router.post(
|
|
227
224
|
"/setname/:id",
|
|
228
|
-
setTenant,
|
|
229
225
|
isAdmin,
|
|
230
226
|
error_catcher(async (req, res) => {
|
|
231
227
|
const { id } = req.params;
|
|
@@ -244,7 +240,6 @@ router.post(
|
|
|
244
240
|
*/
|
|
245
241
|
router.post(
|
|
246
242
|
"/upload",
|
|
247
|
-
setTenant,
|
|
248
243
|
error_catcher(async (req, res) => {
|
|
249
244
|
let jsonResp = {};
|
|
250
245
|
const min_role_upload = getState().getConfig("min_role_upload", 1);
|
|
@@ -297,12 +292,18 @@ router.post(
|
|
|
297
292
|
*/
|
|
298
293
|
router.post(
|
|
299
294
|
"/delete/:id",
|
|
300
|
-
setTenant,
|
|
301
295
|
isAdmin,
|
|
302
296
|
error_catcher(async (req, res) => {
|
|
303
297
|
const { id } = req.params;
|
|
304
298
|
const f = await File.findOne({ id });
|
|
305
|
-
|
|
299
|
+
if (!f) {
|
|
300
|
+
req.flash("error", "File not found");
|
|
301
|
+
res.redirect("/files");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const result = await f.delete(
|
|
305
|
+
f.s3_store ? s3storage.unlinkObject : undefined
|
|
306
|
+
);
|
|
306
307
|
if (result && result.error) {
|
|
307
308
|
req.flash("error", result.error);
|
|
308
309
|
} else {
|
|
@@ -311,3 +312,82 @@ router.post(
|
|
|
311
312
|
res.redirect(`/files`);
|
|
312
313
|
})
|
|
313
314
|
);
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Storage settings form definition
|
|
318
|
+
* @param {object} req request
|
|
319
|
+
* @returns {Promise<Form>} form
|
|
320
|
+
*/
|
|
321
|
+
const storage_form = async (req) => {
|
|
322
|
+
const form = await config_fields_form({
|
|
323
|
+
req,
|
|
324
|
+
field_names: [
|
|
325
|
+
"storage_s3_enabled",
|
|
326
|
+
"storage_s3_bucket",
|
|
327
|
+
"storage_s3_path_prefix",
|
|
328
|
+
"storage_s3_endpoint",
|
|
329
|
+
"storage_s3_region",
|
|
330
|
+
"storage_s3_access_key",
|
|
331
|
+
"storage_s3_access_secret",
|
|
332
|
+
"storage_s3_secure",
|
|
333
|
+
],
|
|
334
|
+
action: "/files/storage",
|
|
335
|
+
});
|
|
336
|
+
form.submitButtonClass = "btn-outline-primary";
|
|
337
|
+
form.submitLabel = req.__("Save");
|
|
338
|
+
form.onChange = "remove_outline(this)";
|
|
339
|
+
return form;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* @name get/storage
|
|
344
|
+
* @function
|
|
345
|
+
* @memberof module:routes/admin~routes/adminRouter
|
|
346
|
+
*/
|
|
347
|
+
router.get(
|
|
348
|
+
"/storage",
|
|
349
|
+
isAdmin,
|
|
350
|
+
error_catcher(async (req, res) => {
|
|
351
|
+
const form = await storage_form(req);
|
|
352
|
+
send_files_page({
|
|
353
|
+
res,
|
|
354
|
+
req,
|
|
355
|
+
active_sub: "Storage",
|
|
356
|
+
contents: {
|
|
357
|
+
type: "card",
|
|
358
|
+
title: req.__("Storage settings"),
|
|
359
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* @name post/email
|
|
367
|
+
* @function
|
|
368
|
+
* @memberof module:routes/admin~routes/adminRouter
|
|
369
|
+
*/
|
|
370
|
+
router.post(
|
|
371
|
+
"/storage",
|
|
372
|
+
isAdmin,
|
|
373
|
+
error_catcher(async (req, res) => {
|
|
374
|
+
const form = await storage_form(req);
|
|
375
|
+
form.validate(req.body);
|
|
376
|
+
if (form.hasErrors) {
|
|
377
|
+
send_admin_page({
|
|
378
|
+
res,
|
|
379
|
+
req,
|
|
380
|
+
active_sub: "Storage",
|
|
381
|
+
contents: {
|
|
382
|
+
type: "card",
|
|
383
|
+
title: req.__("Storage settings"),
|
|
384
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
} else {
|
|
388
|
+
await save_config_from_form(form);
|
|
389
|
+
req.flash("success", req.__("Storage settings updated"));
|
|
390
|
+
res.redirect("/files/storage");
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
);
|
package/routes/homepage.js
CHANGED
|
@@ -19,10 +19,11 @@ const { get_latest_npm_version } = require("@saltcorn/data/models/config");
|
|
|
19
19
|
const packagejson = require("../package.json");
|
|
20
20
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
21
21
|
const { fileUploadForm } = require("../markup/forms");
|
|
22
|
+
const { get_base_url } = require("./utils.js");
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
* @param {*} tables
|
|
25
|
-
* @param {object} req
|
|
25
|
+
* @param {*} tables
|
|
26
|
+
* @param {object} req
|
|
26
27
|
* @returns {Table}
|
|
27
28
|
*/
|
|
28
29
|
const tableTable = (tables, req) =>
|
|
@@ -38,8 +39,8 @@ const tableTable = (tables, req) =>
|
|
|
38
39
|
);
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
|
-
* @param {*} tables
|
|
42
|
-
* @param {object} req
|
|
42
|
+
* @param {*} tables
|
|
43
|
+
* @param {object} req
|
|
43
44
|
* @returns {object}
|
|
44
45
|
*/
|
|
45
46
|
const tableCard = (tables, req) => ({
|
|
@@ -67,8 +68,8 @@ const tableCard = (tables, req) => ({
|
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
|
-
* @param {*} views
|
|
71
|
-
* @param {object} req
|
|
71
|
+
* @param {*} views
|
|
72
|
+
* @param {object} req
|
|
72
73
|
* @returns {Table}
|
|
73
74
|
*/
|
|
74
75
|
const viewTable = (views, req) =>
|
|
@@ -88,8 +89,8 @@ const viewTable = (views, req) =>
|
|
|
88
89
|
);
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
|
-
* @param {*} views
|
|
92
|
-
* @param {object} req
|
|
92
|
+
* @param {*} views
|
|
93
|
+
* @param {object} req
|
|
93
94
|
* @returns {object}
|
|
94
95
|
*/
|
|
95
96
|
const viewCard = (views, req) => ({
|
|
@@ -119,8 +120,8 @@ const viewCard = (views, req) => ({
|
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
|
-
* @param {*} pages
|
|
123
|
-
* @param {object} req
|
|
123
|
+
* @param {*} pages
|
|
124
|
+
* @param {object} req
|
|
124
125
|
* @returns {Table}
|
|
125
126
|
*/
|
|
126
127
|
const pageTable = (pages, req) =>
|
|
@@ -140,8 +141,8 @@ const pageTable = (pages, req) =>
|
|
|
140
141
|
);
|
|
141
142
|
|
|
142
143
|
/**
|
|
143
|
-
* @param {*} pages
|
|
144
|
-
* @param {object} req
|
|
144
|
+
* @param {*} pages
|
|
145
|
+
* @param {object} req
|
|
145
146
|
* @returns {object}
|
|
146
147
|
*/
|
|
147
148
|
const pageCard = (pages, req) => ({
|
|
@@ -172,11 +173,11 @@ const pageCard = (pages, req) => ({
|
|
|
172
173
|
});
|
|
173
174
|
|
|
174
175
|
/**
|
|
175
|
-
* @param {object} req
|
|
176
|
+
* @param {object} req
|
|
176
177
|
* @returns {Promise<div>}
|
|
177
178
|
*/
|
|
178
179
|
const filesTab = async (req) => {
|
|
179
|
-
const files = await File.find({}, { orderBy: "filename" });
|
|
180
|
+
const files = await File.find({}, { orderBy: "filename", cached: true });
|
|
180
181
|
return div(
|
|
181
182
|
files.length == 0
|
|
182
183
|
? p(req.__("No files"))
|
|
@@ -196,16 +197,10 @@ const filesTab = async (req) => {
|
|
|
196
197
|
};
|
|
197
198
|
|
|
198
199
|
/**
|
|
199
|
-
* @param {object} req
|
|
200
|
+
* @param {object} req
|
|
200
201
|
* @returns {Promise<div>}
|
|
201
202
|
*/
|
|
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
|
-
});
|
|
203
|
+
const usersTab = async (req, users, roleMap) => {
|
|
209
204
|
return div(
|
|
210
205
|
mkTable(
|
|
211
206
|
[
|
|
@@ -219,20 +214,21 @@ const usersTab = async (req) => {
|
|
|
219
214
|
users
|
|
220
215
|
),
|
|
221
216
|
a(
|
|
222
|
-
{ href: `/useradmin/new`, class: "btn btn-secondary" },
|
|
217
|
+
{ href: `/useradmin/new`, class: "btn btn-secondary my-3" },
|
|
223
218
|
req.__("Create user")
|
|
224
219
|
)
|
|
225
220
|
);
|
|
226
221
|
};
|
|
227
222
|
|
|
228
223
|
/**
|
|
229
|
-
* @param {object} req
|
|
224
|
+
* @param {object} req
|
|
230
225
|
* @returns {Promise<div>}
|
|
231
226
|
*/
|
|
232
|
-
const actionsTab = async (req) => {
|
|
233
|
-
const
|
|
227
|
+
const actionsTab = async (req, triggers) => {
|
|
228
|
+
const base_url = get_base_url(req);
|
|
234
229
|
|
|
235
230
|
return div(
|
|
231
|
+
{ class: "pb-3" },
|
|
236
232
|
triggers.length <= 1 &&
|
|
237
233
|
p(
|
|
238
234
|
{ class: "mt-2 pr-2" },
|
|
@@ -259,14 +255,73 @@ const actionsTab = async (req) => {
|
|
|
259
255
|
triggers
|
|
260
256
|
),
|
|
261
257
|
a(
|
|
262
|
-
{ href: "/actions/new", class: "btn btn-secondary
|
|
258
|
+
{ href: "/actions/new", class: "btn btn-secondary my-3" },
|
|
263
259
|
req.__("Add trigger")
|
|
264
260
|
)
|
|
265
261
|
);
|
|
266
262
|
};
|
|
263
|
+
const packTab = (req, packlist) =>
|
|
264
|
+
div(
|
|
265
|
+
{ class: "pb-3 pt-2 pr-4" },
|
|
266
|
+
p(req.__("Instead of building, get up and running in no time with packs")),
|
|
267
|
+
p(
|
|
268
|
+
{ class: "font-italic" },
|
|
269
|
+
req.__(
|
|
270
|
+
"Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
|
|
271
|
+
)
|
|
272
|
+
),
|
|
273
|
+
mkTable(
|
|
274
|
+
[
|
|
275
|
+
{ label: req.__("Name"), key: "name" },
|
|
276
|
+
{
|
|
277
|
+
label: req.__("Description"),
|
|
278
|
+
key: "description",
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
packlist,
|
|
282
|
+
{ noHeader: true }
|
|
283
|
+
),
|
|
284
|
+
a(
|
|
285
|
+
{ href: `/plugins?set=packs`, class: "btn btn-primary" },
|
|
286
|
+
req.__("Go to pack store »")
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const helpCard = (req) =>
|
|
291
|
+
div(
|
|
292
|
+
{ class: "pb-3 pt-2 pr-4" },
|
|
293
|
+
p(req.__("Confused?")),
|
|
294
|
+
p(
|
|
295
|
+
req.__(
|
|
296
|
+
"The Wiki contains the documentation and tutorials on installing and using Saltcorn"
|
|
297
|
+
)
|
|
298
|
+
),
|
|
299
|
+
a(
|
|
300
|
+
{
|
|
301
|
+
href: `https://wiki.saltcorn.com/`,
|
|
302
|
+
class: "btn btn-primary",
|
|
303
|
+
},
|
|
304
|
+
req.__("Go to Wiki »")
|
|
305
|
+
),
|
|
306
|
+
p(req.__("The YouTube channel has some video tutorials")),
|
|
307
|
+
a(
|
|
308
|
+
{
|
|
309
|
+
href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
|
|
310
|
+
class: "btn btn-secondary",
|
|
311
|
+
},
|
|
312
|
+
req.__("Go to YouTube »")
|
|
313
|
+
),
|
|
314
|
+
div(
|
|
315
|
+
{ class: "mt-3" },
|
|
316
|
+
a(
|
|
317
|
+
{ href: `https://blog.saltcorn.com/` },
|
|
318
|
+
req.__("What's new? Read the blog »")
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
);
|
|
267
322
|
|
|
268
323
|
/**
|
|
269
|
-
* @param {object} req
|
|
324
|
+
* @param {object} req
|
|
270
325
|
* @returns {Promise<object>}
|
|
271
326
|
*/
|
|
272
327
|
const welcome_page = async (req) => {
|
|
@@ -275,10 +330,16 @@ const welcome_page = async (req) => {
|
|
|
275
330
|
...packs_available.slice(0, 5),
|
|
276
331
|
{ name: req.__("More..."), description: "" },
|
|
277
332
|
];
|
|
278
|
-
const tables = await Table.find({}, {
|
|
279
|
-
const views = await View.find({});
|
|
280
|
-
const pages = await Page.find({});
|
|
281
|
-
|
|
333
|
+
const tables = await Table.find({}, { cached: true });
|
|
334
|
+
const views = await View.find({}, { cached: true });
|
|
335
|
+
const pages = await Page.find({}, { cached: true });
|
|
336
|
+
const triggers = await Trigger.findAllWithTableName();
|
|
337
|
+
const users = await User.find({}, { orderBy: "id" });
|
|
338
|
+
const roles = await User.get_roles();
|
|
339
|
+
let roleMap = {};
|
|
340
|
+
roles.forEach((r) => {
|
|
341
|
+
roleMap[r.id] = r.role;
|
|
342
|
+
});
|
|
282
343
|
return {
|
|
283
344
|
above: [
|
|
284
345
|
{
|
|
@@ -293,75 +354,37 @@ const welcome_page = async (req) => {
|
|
|
293
354
|
{
|
|
294
355
|
type: "card",
|
|
295
356
|
//title: req.__("Install pack"),
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
},
|
|
357
|
+
bodyClass: "py-0 pr-0",
|
|
358
|
+
class: "welcome-page-entity-list",
|
|
359
|
+
|
|
360
|
+
tabContents:
|
|
361
|
+
triggers.length > 0
|
|
362
|
+
? {
|
|
363
|
+
Triggers: await actionsTab(req, triggers),
|
|
364
|
+
Files: await filesTab(req),
|
|
365
|
+
Packs: packTab(req, packlist),
|
|
366
|
+
}
|
|
367
|
+
: {
|
|
368
|
+
Packs: packTab(req, packlist),
|
|
369
|
+
Triggers: await actionsTab(req, triggers),
|
|
370
|
+
Files: await filesTab(req),
|
|
371
|
+
},
|
|
328
372
|
},
|
|
329
373
|
{
|
|
330
374
|
type: "card",
|
|
331
375
|
//title: req.__("Learn"),
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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",
|
|
376
|
+
bodyClass: "py-0 pr-0",
|
|
377
|
+
class: "welcome-page-entity-list",
|
|
378
|
+
tabContents:
|
|
379
|
+
users.length > 4
|
|
380
|
+
? {
|
|
381
|
+
Users: await usersTab(req, users, roleMap),
|
|
382
|
+
Help: helpCard(req),
|
|
383
|
+
}
|
|
384
|
+
: {
|
|
385
|
+
Help: helpCard(req),
|
|
386
|
+
Users: await usersTab(req, users, roleMap),
|
|
352
387
|
},
|
|
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
388
|
},
|
|
366
389
|
],
|
|
367
390
|
},
|
|
@@ -370,8 +393,8 @@ const welcome_page = async (req) => {
|
|
|
370
393
|
};
|
|
371
394
|
|
|
372
395
|
/**
|
|
373
|
-
* @param {object} req
|
|
374
|
-
* @param {object} res
|
|
396
|
+
* @param {object} req
|
|
397
|
+
* @param {object} res
|
|
375
398
|
* @returns {Promise<void>}
|
|
376
399
|
*/
|
|
377
400
|
const no_views_logged_in = async (req, res) => {
|
|
@@ -400,9 +423,9 @@ const no_views_logged_in = async (req, res) => {
|
|
|
400
423
|
};
|
|
401
424
|
|
|
402
425
|
/**
|
|
403
|
-
* @param {number} role_id
|
|
404
|
-
* @param {object} res
|
|
405
|
-
* @param {object} req
|
|
426
|
+
* @param {number} role_id
|
|
427
|
+
* @param {object} res
|
|
428
|
+
* @param {object} req
|
|
406
429
|
* @returns {Promise<boolean>}
|
|
407
430
|
*/
|
|
408
431
|
const get_config_response = async (role_id, res, req) => {
|
|
@@ -420,8 +443,11 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
420
443
|
const contents = await db_page.run(req.query, { res, req });
|
|
421
444
|
|
|
422
445
|
res.sendWrap(
|
|
423
|
-
{
|
|
424
|
-
|
|
446
|
+
{
|
|
447
|
+
title: db_page.title,
|
|
448
|
+
description: db_page.description,
|
|
449
|
+
bodyClass: "page_" + db.sqlsanitize(homeCfg),
|
|
450
|
+
} || `${pagename} page`,
|
|
425
451
|
contents
|
|
426
452
|
);
|
|
427
453
|
} else res.redirect(homeCfg);
|
|
@@ -431,8 +457,8 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
431
457
|
|
|
432
458
|
/**
|
|
433
459
|
* Function assigned to 'module.exports'.
|
|
434
|
-
* @param {object} req
|
|
435
|
-
* @param {object} res
|
|
460
|
+
* @param {object} req
|
|
461
|
+
* @param {object} res
|
|
436
462
|
* @returns {Promise<void>}
|
|
437
463
|
*/
|
|
438
464
|
module.exports = async (req, res) => {
|