@saltcorn/server 0.8.0-beta.2 → 0.8.0-beta.3
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/admin.js +3 -19
- package/locales/en.json +10 -2
- package/locales/it.json +1 -1
- package/locales/ru.json +25 -1
- package/markup/admin.js +3 -11
- package/markup/forms.js +27 -23
- package/package.json +8 -8
- package/public/saltcorn.js +2 -2
- package/routes/admin.js +136 -142
- package/routes/fields.js +15 -4
- package/routes/files.js +83 -22
- package/routes/homepage.js +70 -64
- package/routes/infoarch.js +10 -7
- package/routes/menu.js +22 -12
- package/routes/page.js +6 -1
- package/routes/plugins.js +112 -106
- package/routes/tables.js +3 -1
- package/routes/utils.js +3 -2
- package/s3storage.js +24 -11
package/routes/plugins.js
CHANGED
|
@@ -89,10 +89,10 @@ const pluginForm = (req, plugin) => {
|
|
|
89
89
|
attributes: { options: "npm,local,github,git" },
|
|
90
90
|
sublabel: req.__(
|
|
91
91
|
"Source of module for install. Few options:" +
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
"npm - download from npm repository," +
|
|
93
|
+
"local - get from local file system," +
|
|
94
|
+
"github - download from github," +
|
|
95
|
+
"git - get from git"
|
|
96
96
|
),
|
|
97
97
|
}),
|
|
98
98
|
new Field({
|
|
@@ -101,19 +101,19 @@ const pluginForm = (req, plugin) => {
|
|
|
101
101
|
input_type: "text",
|
|
102
102
|
sublabel: req.__(
|
|
103
103
|
"For npm - name of npm package, e.g. @saltcorn/html or saltcorn-gantt, check at npmjs.com, " +
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
"for local - absolute path to module folder in file system, e.g. C:\\gitsrc\\any-bootstrap-theme\\, " +
|
|
105
|
+
"for github - name of github project."
|
|
106
106
|
),
|
|
107
107
|
}),
|
|
108
108
|
...(schema === db.connectObj.default_schema
|
|
109
109
|
? [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
new Field({
|
|
111
|
+
label: req.__("Version"),
|
|
112
|
+
name: "version",
|
|
113
|
+
input_type: "text",
|
|
114
|
+
sublabel: req.__("Version of module, latest is default value"),
|
|
115
|
+
}),
|
|
116
|
+
]
|
|
117
117
|
: []),
|
|
118
118
|
new Field({
|
|
119
119
|
label: req.__("Private SSH key"),
|
|
@@ -269,72 +269,72 @@ const store_item_html = (req) => (item) => ({
|
|
|
269
269
|
div(item.description || ""),
|
|
270
270
|
item.documentation_link
|
|
271
271
|
? div(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
a(
|
|
273
|
+
{ href: item.documentation_link, target: "_blank" },
|
|
274
|
+
req.__("Documentation")
|
|
275
|
+
)
|
|
275
276
|
)
|
|
276
|
-
)
|
|
277
277
|
: ""
|
|
278
278
|
),
|
|
279
279
|
footer: div(
|
|
280
280
|
div(
|
|
281
281
|
!item.installed &&
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
282
|
+
item.plugin &&
|
|
283
|
+
post_btn(
|
|
284
|
+
`/plugins/install/${encodeURIComponent(item.name)}`,
|
|
285
|
+
req.__("Install"),
|
|
286
|
+
req.csrfToken(),
|
|
287
|
+
{
|
|
288
|
+
klass: "store-install",
|
|
289
|
+
small: true,
|
|
290
|
+
onClick: "press_store_button(this)",
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
293
|
!item.installed &&
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
294
|
+
item.pack &&
|
|
295
|
+
post_btn(
|
|
296
|
+
`/packs/install-named/${encodeURIComponent(item.name)}`,
|
|
297
|
+
req.__("Install"),
|
|
298
|
+
req.csrfToken(),
|
|
299
|
+
{
|
|
300
|
+
klass: "store-install",
|
|
301
|
+
small: true,
|
|
302
|
+
onClick: "press_store_button(this)",
|
|
303
|
+
}
|
|
304
|
+
),
|
|
305
305
|
|
|
306
306
|
item.installed && item.plugin && cfg_link(req, item),
|
|
307
307
|
item.installed && item.plugin && info_link(req, item),
|
|
308
308
|
|
|
309
309
|
item.installed &&
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
310
|
+
item.pack &&
|
|
311
|
+
post_btn(
|
|
312
|
+
`/packs/uninstall/${encodeURIComponent(item.name)}`,
|
|
313
|
+
req.__("Uninstall"),
|
|
314
|
+
req.csrfToken(),
|
|
315
|
+
{
|
|
316
|
+
klass: "store-install",
|
|
317
|
+
small: true,
|
|
318
|
+
btnClass: "btn-danger",
|
|
319
|
+
formClass: "d-inline",
|
|
320
|
+
onClick: "press_store_button(this)",
|
|
321
|
+
}
|
|
322
|
+
),
|
|
323
323
|
item.installed &&
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
324
|
+
item.plugin &&
|
|
325
|
+
item.name !== "base" &&
|
|
326
|
+
post_btn(
|
|
327
|
+
`/plugins/delete/${encodeURIComponent(item.name)}`,
|
|
328
|
+
req.__("Remove"),
|
|
329
|
+
req.csrfToken(),
|
|
330
|
+
{
|
|
331
|
+
klass: "store-install",
|
|
332
|
+
small: true,
|
|
333
|
+
btnClass: "btn-danger",
|
|
334
|
+
formClass: "d-inline",
|
|
335
|
+
onClick: "press_store_button(this)",
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
338
|
)
|
|
339
339
|
),
|
|
340
340
|
});
|
|
@@ -354,7 +354,7 @@ const storeNavPills = (req) => {
|
|
|
354
354
|
"nav-link",
|
|
355
355
|
(req.query.set === txt.toLowerCase() ||
|
|
356
356
|
(txt === "All" && !req.query.set)) &&
|
|
357
|
-
|
|
357
|
+
"active",
|
|
358
358
|
],
|
|
359
359
|
},
|
|
360
360
|
req.__(txt)
|
|
@@ -451,23 +451,23 @@ const store_actions_dropdown = (req) =>
|
|
|
451
451
|
'<i class="fas fa-sync"></i> ' + req.__("Refresh")
|
|
452
452
|
),
|
|
453
453
|
db.getTenantSchema() === db.connectObj.default_schema &&
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
454
|
+
a(
|
|
455
|
+
{
|
|
456
|
+
class: "dropdown-item",
|
|
457
|
+
href: `/plugins/upgrade`,
|
|
458
|
+
onClick: `notifyAlert('${req.__("Upgrading modules...")}', true)`,
|
|
459
|
+
},
|
|
460
|
+
'<i class="far fa-arrow-alt-circle-up"></i> ' +
|
|
461
|
+
req.__("Upgrade installed modules")
|
|
462
|
+
),
|
|
463
463
|
db.getTenantSchema() === db.connectObj.default_schema &&
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
464
|
+
a(
|
|
465
|
+
{
|
|
466
|
+
class: "dropdown-item",
|
|
467
|
+
href: `/plugins/new`,
|
|
468
|
+
},
|
|
469
|
+
'<i class="fas fa-plus"></i> ' + req.__("Add another module")
|
|
470
|
+
),
|
|
471
471
|
|
|
472
472
|
a(
|
|
473
473
|
{
|
|
@@ -534,7 +534,10 @@ router.get(
|
|
|
534
534
|
error_catcher(async (req, res) => {
|
|
535
535
|
const items = await get_store_items();
|
|
536
536
|
const relevant_items = filter_items(items, req.query);
|
|
537
|
-
res.sendWrap(
|
|
537
|
+
res.sendWrap(
|
|
538
|
+
req.__("Module store"),
|
|
539
|
+
plugin_store_html(relevant_items, req)
|
|
540
|
+
);
|
|
538
541
|
})
|
|
539
542
|
);
|
|
540
543
|
|
|
@@ -574,8 +577,9 @@ router.get(
|
|
|
574
577
|
onclick: "location.reload()",
|
|
575
578
|
},
|
|
576
579
|
];
|
|
577
|
-
wfres.renderForm.onChange = `${
|
|
578
|
-
|
|
580
|
+
wfres.renderForm.onChange = `${
|
|
581
|
+
wfres.renderForm.onChange || ""
|
|
582
|
+
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
579
583
|
}
|
|
580
584
|
|
|
581
585
|
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
@@ -619,8 +623,9 @@ router.post(
|
|
|
619
623
|
onclick: "location.reload()",
|
|
620
624
|
},
|
|
621
625
|
];
|
|
622
|
-
wfres.renderForm.onChange = `${
|
|
623
|
-
|
|
626
|
+
wfres.renderForm.onChange = `${
|
|
627
|
+
wfres.renderForm.onChange || ""
|
|
628
|
+
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
624
629
|
}
|
|
625
630
|
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
626
631
|
type: "card",
|
|
@@ -785,7 +790,8 @@ router.get(
|
|
|
785
790
|
db.getTenantSchema() === db.connectObj.default_schema &&
|
|
786
791
|
plugin_db.source === "npm";
|
|
787
792
|
const latest =
|
|
788
|
-
update_permitted &&
|
|
793
|
+
update_permitted &&
|
|
794
|
+
(await get_latest_npm_version(plugin_db.location, 1000));
|
|
789
795
|
const can_update = update_permitted && latest && mod.version !== latest;
|
|
790
796
|
let pkgjson;
|
|
791
797
|
if (mod.location && fs.existsSync(path.join(mod.location, "package.json")))
|
|
@@ -806,35 +812,35 @@ router.get(
|
|
|
806
812
|
latest || "",
|
|
807
813
|
can_update
|
|
808
814
|
? a(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
+
{
|
|
816
|
+
href: `/plugins/upgrade-plugin/${plugin_db.name}`,
|
|
817
|
+
class: "btn btn-primary btn-sm ms-2",
|
|
818
|
+
},
|
|
819
|
+
req.__("Upgrade")
|
|
820
|
+
)
|
|
815
821
|
: ""
|
|
816
822
|
)
|
|
817
823
|
),
|
|
818
824
|
mod.plugin_module.dependencies
|
|
819
825
|
? tr(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
826
|
+
th(req.__("Module dependencies")),
|
|
827
|
+
td(
|
|
828
|
+
mod.plugin_module.dependencies.map((d) =>
|
|
829
|
+
span({ class: "badge bg-primary me-1" }, d)
|
|
830
|
+
)
|
|
824
831
|
)
|
|
825
832
|
)
|
|
826
|
-
)
|
|
827
833
|
: null,
|
|
828
834
|
store_item && store_item.documentation_link
|
|
829
835
|
? tr(
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
836
|
+
th(req.__("Documentation")),
|
|
837
|
+
td(
|
|
838
|
+
link(
|
|
839
|
+
store_item.documentation_link,
|
|
840
|
+
store_item.documentation_link
|
|
841
|
+
)
|
|
835
842
|
)
|
|
836
843
|
)
|
|
837
|
-
)
|
|
838
844
|
: null,
|
|
839
845
|
pkgjson && pkgjson.repository
|
|
840
846
|
? tr(th(req.__("Repository")), td(showRepository(pkgjson.repository)))
|
package/routes/tables.js
CHANGED
|
@@ -328,6 +328,7 @@ router.get(
|
|
|
328
328
|
name: "name",
|
|
329
329
|
input_type: "text",
|
|
330
330
|
},
|
|
331
|
+
// todo implement file mask filter like , accept: "text/csv"
|
|
331
332
|
{ label: req.__("File"), name: "file", input_type: "file" },
|
|
332
333
|
],
|
|
333
334
|
}),
|
|
@@ -353,7 +354,7 @@ router.get(
|
|
|
353
354
|
*/
|
|
354
355
|
router.post(
|
|
355
356
|
"/create-from-csv",
|
|
356
|
-
setTenant,
|
|
357
|
+
setTenant,
|
|
357
358
|
isAdmin,
|
|
358
359
|
error_catcher(async (req, res) => {
|
|
359
360
|
if (req.body.name && req.files && req.files.file) {
|
|
@@ -727,6 +728,7 @@ router.get(
|
|
|
727
728
|
method: "post",
|
|
728
729
|
action: `/table/upload_to_table/${table.name}`,
|
|
729
730
|
encType: "multipart/form-data",
|
|
731
|
+
acceptCharset: "UTF-8",
|
|
730
732
|
},
|
|
731
733
|
input({ type: "hidden", name: "_csrf", value: req.csrfToken() }),
|
|
732
734
|
label(
|
package/routes/utils.js
CHANGED
|
@@ -73,6 +73,8 @@ function isAdmin(req, res, next) {
|
|
|
73
73
|
const setLanguage = (req, res, state) => {
|
|
74
74
|
if (req.user && req.user.language) {
|
|
75
75
|
req.setLocale(req.user.language);
|
|
76
|
+
} else if (req.cookies?.lang) {
|
|
77
|
+
req.setLocale(req.cookies?.lang);
|
|
76
78
|
}
|
|
77
79
|
set_custom_http_headers(res, state);
|
|
78
80
|
};
|
|
@@ -144,8 +146,7 @@ const setTenant = (req, res, next) => {
|
|
|
144
146
|
next();
|
|
145
147
|
});
|
|
146
148
|
}
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
+
} else {
|
|
149
150
|
setLanguage(req, res);
|
|
150
151
|
next();
|
|
151
152
|
}
|
package/s3storage.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var multerS3 = require("multer-s3");
|
|
1
|
+
const aws = require("aws-sdk");
|
|
2
|
+
const multer = require("multer");
|
|
3
|
+
const multerS3 = require("multer-s3");
|
|
5
4
|
const { getState } = require("@saltcorn/data/db/state");
|
|
6
5
|
const fileUpload = require("express-fileupload");
|
|
7
6
|
const { v4: uuidv4 } = require("uuid");
|
|
8
|
-
const
|
|
9
|
-
var contentDisposition = require("content-disposition");
|
|
7
|
+
const contentDisposition = require("content-disposition");
|
|
10
8
|
|
|
11
9
|
function createS3Client() {
|
|
12
10
|
return new aws.S3({
|
|
@@ -47,11 +45,26 @@ module.exports = {
|
|
|
47
45
|
|
|
48
46
|
s3upload(req, res, next);
|
|
49
47
|
} else {
|
|
50
|
-
// Use regular file upload
|
|
48
|
+
// Use regular file upload https://www.npmjs.com/package/express-fileupload
|
|
49
|
+
const fileSizeLimit = getState().getConfig("file_upload_limit", 0);
|
|
51
50
|
fileUpload({
|
|
52
51
|
useTempFiles: true,
|
|
53
52
|
createParentPath: true,
|
|
54
53
|
tempFileDir: "/tmp/",
|
|
54
|
+
// set to true - if you want to have debug
|
|
55
|
+
debug: getState().getConfig("file_upload_debug",false),
|
|
56
|
+
//uriDecodeFileNames: true,
|
|
57
|
+
//safeFileNames: true,
|
|
58
|
+
defCharset: 'utf8',
|
|
59
|
+
defParamCharset: 'utf8',
|
|
60
|
+
// 0 - means no upload limit check
|
|
61
|
+
limits: {
|
|
62
|
+
fileSize: fileSizeLimit,
|
|
63
|
+
},
|
|
64
|
+
abortOnLimit: fileSizeLimit !== 0,
|
|
65
|
+
// 0 - means no upload limit check
|
|
66
|
+
uploadTimeout: getState().getConfig("file_upload_timeout",0),
|
|
67
|
+
|
|
55
68
|
})(req, res, next);
|
|
56
69
|
}
|
|
57
70
|
},
|
|
@@ -73,7 +86,7 @@ module.exports = {
|
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
// Create S3 object
|
|
76
|
-
|
|
89
|
+
const s3 = createS3Client();
|
|
77
90
|
const bucket = getState().getConfig("storage_s3_bucket");
|
|
78
91
|
|
|
79
92
|
let newFileObject = {};
|
|
@@ -122,10 +135,10 @@ module.exports = {
|
|
|
122
135
|
*/
|
|
123
136
|
serveObject: function (file, res, download) {
|
|
124
137
|
if (file.s3_store) {
|
|
125
|
-
|
|
138
|
+
const s3 = createS3Client();
|
|
126
139
|
const bucket = getState().getConfig("storage_s3_bucket");
|
|
127
140
|
|
|
128
|
-
|
|
141
|
+
const params = {
|
|
129
142
|
Bucket: bucket,
|
|
130
143
|
Key: file.location,
|
|
131
144
|
};
|
|
@@ -147,7 +160,7 @@ module.exports = {
|
|
|
147
160
|
|
|
148
161
|
unlinkObject: function (file) {
|
|
149
162
|
if (file.s3_store) {
|
|
150
|
-
|
|
163
|
+
const s3 = createS3Client();
|
|
151
164
|
return new Promise((resolve, reject) => {
|
|
152
165
|
s3.deleteObject(
|
|
153
166
|
{
|