@saltcorn/server 0.8.1-beta.5 → 0.8.1-rc.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 +21 -10
- package/auth/routes.js +1 -0
- package/auth/testhelp.js +5 -3
- package/locales/da.json +4 -1
- package/locales/en.json +8 -1
- package/markup/admin.js +4 -2
- package/package.json +8 -8
- package/public/saltcorn-common.js +27 -0
- package/public/saltcorn.js +17 -1
- package/routes/actions.js +4 -0
- package/routes/admin.js +25 -14
- package/routes/eventlog.js +1 -0
- package/routes/fields.js +53 -9
- package/routes/files.js +9 -0
- package/routes/list.js +11 -0
- package/routes/menu.js +9 -2
- package/routes/pageedit.js +2 -1
- package/routes/plugins.js +17 -4
- package/routes/search.js +1 -0
- package/routes/tables.js +15 -13
- package/routes/tenant.js +1 -0
- package/routes/utils.js +12 -3
- package/routes/view.js +2 -0
- package/routes/viewedit.js +11 -3
- package/serve.js +1 -1
- package/wrapper.js +4 -38
package/auth/admin.js
CHANGED
|
@@ -336,6 +336,7 @@ const http_settings_form = async (req) =>
|
|
|
336
336
|
"cookie_duration",
|
|
337
337
|
"cookie_duration_remember",
|
|
338
338
|
"cookie_sessions",
|
|
339
|
+
"public_cache_maxage",
|
|
339
340
|
"custom_http_headers",
|
|
340
341
|
],
|
|
341
342
|
action: "/useradmin/http",
|
|
@@ -376,6 +377,7 @@ router.get(
|
|
|
376
377
|
active_sub: "Login and Signup",
|
|
377
378
|
contents: {
|
|
378
379
|
type: "card",
|
|
380
|
+
titleAjaxIndicator: true,
|
|
379
381
|
title: req.__("Authentication settings"),
|
|
380
382
|
contents: [renderForm(form, req.csrfToken())],
|
|
381
383
|
},
|
|
@@ -408,9 +410,10 @@ router.post(
|
|
|
408
410
|
});
|
|
409
411
|
} else {
|
|
410
412
|
await save_config_from_form(form);
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
413
|
+
if (!req.xhr) {
|
|
414
|
+
req.flash("success", req.__("Authentication settings updated"));
|
|
415
|
+
res.redirect("/useradmin/settings");
|
|
416
|
+
} else res.json({ success: "ok" });
|
|
414
417
|
}
|
|
415
418
|
})
|
|
416
419
|
);
|
|
@@ -432,6 +435,7 @@ router.get(
|
|
|
432
435
|
active_sub: "HTTP",
|
|
433
436
|
contents: {
|
|
434
437
|
type: "card",
|
|
438
|
+
titleAjaxIndicator: true,
|
|
435
439
|
title: req.__("HTTP settings"),
|
|
436
440
|
contents: [renderForm(form, req.csrfToken())],
|
|
437
441
|
},
|
|
@@ -464,9 +468,11 @@ router.post(
|
|
|
464
468
|
});
|
|
465
469
|
} else {
|
|
466
470
|
await save_config_from_form(form);
|
|
467
|
-
|
|
468
|
-
if (!req.xhr)
|
|
469
|
-
|
|
471
|
+
|
|
472
|
+
if (!req.xhr) {
|
|
473
|
+
req.flash("success", req.__("HTTP settings updated"));
|
|
474
|
+
res.redirect("/useradmin/http");
|
|
475
|
+
} else res.json({ success: "ok" });
|
|
470
476
|
}
|
|
471
477
|
})
|
|
472
478
|
);
|
|
@@ -488,6 +494,7 @@ router.get(
|
|
|
488
494
|
active_sub: "Permissions",
|
|
489
495
|
contents: {
|
|
490
496
|
type: "card",
|
|
497
|
+
titleAjaxIndicator: true,
|
|
491
498
|
title: req.__("Permissions settings"),
|
|
492
499
|
contents: [renderForm(form, req.csrfToken())],
|
|
493
500
|
},
|
|
@@ -514,15 +521,17 @@ router.post(
|
|
|
514
521
|
active_sub: "Permissions",
|
|
515
522
|
contents: {
|
|
516
523
|
type: "card",
|
|
524
|
+
titleAjaxIndicator: true,
|
|
517
525
|
title: req.__("Permissions settings"),
|
|
518
526
|
contents: [renderForm(form, req.csrfToken())],
|
|
519
527
|
},
|
|
520
528
|
});
|
|
521
529
|
} else {
|
|
522
530
|
await save_config_from_form(form);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
531
|
+
if (!req.xhr) {
|
|
532
|
+
req.flash("success", req.__("Permissions settings updated"));
|
|
533
|
+
res.redirect("/useradmin/permissions");
|
|
534
|
+
} else res.json({ success: "ok" });
|
|
526
535
|
}
|
|
527
536
|
})
|
|
528
537
|
);
|
|
@@ -677,8 +686,9 @@ router.get(
|
|
|
677
686
|
active_sub: "SSL",
|
|
678
687
|
contents: {
|
|
679
688
|
type: "card",
|
|
680
|
-
title: req.__("
|
|
689
|
+
title: req.__("Custom SSL certificates"),
|
|
681
690
|
sub2_page: req.__("Custom SSL certificates"),
|
|
691
|
+
titleAjaxIndicator: true,
|
|
682
692
|
contents: [renderForm(form, req.csrfToken())],
|
|
683
693
|
},
|
|
684
694
|
});
|
|
@@ -817,6 +827,7 @@ router.get(
|
|
|
817
827
|
contents: {
|
|
818
828
|
type: "card",
|
|
819
829
|
title: req.__("Table access"),
|
|
830
|
+
titleAjaxIndicator: true,
|
|
820
831
|
contents,
|
|
821
832
|
},
|
|
822
833
|
});
|
package/auth/routes.js
CHANGED
|
@@ -1061,6 +1061,7 @@ router.post(
|
|
|
1061
1061
|
else req.session.cookie.expires = false;
|
|
1062
1062
|
}
|
|
1063
1063
|
Trigger.emitEvent("Login", null, req.user);
|
|
1064
|
+
res?.cookie?.("loggedin", "true");
|
|
1064
1065
|
req.flash("success", req.__("Welcome, %s!", req.user.email));
|
|
1065
1066
|
if (req.smr) {
|
|
1066
1067
|
const dbUser = await User.findOne({ id: req.user.id });
|
package/auth/testhelp.js
CHANGED
|
@@ -88,6 +88,9 @@ const toNotInclude =
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
const resToLoginCookie = (res) =>
|
|
92
|
+
res.headers["set-cookie"].find((c) => c.includes("connect.sid"));
|
|
93
|
+
|
|
91
94
|
/**
|
|
92
95
|
*
|
|
93
96
|
* @returns {Promise<void>}
|
|
@@ -99,7 +102,7 @@ const getStaffLoginCookie = async () => {
|
|
|
99
102
|
.send("email=staff@foo.com")
|
|
100
103
|
.send("password=ghrarhr54hg");
|
|
101
104
|
if (res.statusCode !== 302) console.log(res.text);
|
|
102
|
-
return res
|
|
105
|
+
return resToLoginCookie(res);
|
|
103
106
|
};
|
|
104
107
|
|
|
105
108
|
/**
|
|
@@ -113,8 +116,7 @@ const getAdminLoginCookie = async () => {
|
|
|
113
116
|
.send("email=admin@foo.com")
|
|
114
117
|
.send("password=AhGGr6rhu45");
|
|
115
118
|
if (res.statusCode !== 302) console.log(res.text);
|
|
116
|
-
|
|
117
|
-
return res.headers["set-cookie"][0];
|
|
119
|
+
return resToLoginCookie(res);
|
|
118
120
|
};
|
|
119
121
|
|
|
120
122
|
/**
|
package/locales/da.json
CHANGED
|
@@ -555,5 +555,8 @@
|
|
|
555
555
|
"Create database view": "Create database view",
|
|
556
556
|
"Create an SQL view in the database with the fields in this list": "Create an SQL view in the database with the fields in this list",
|
|
557
557
|
"Rows per page": "Rows per page",
|
|
558
|
-
"List options": "List options"
|
|
558
|
+
"List options": "List options",
|
|
559
|
+
"Modules": "Modules",
|
|
560
|
+
"File not found": "File not found",
|
|
561
|
+
"Welcome, %s!": "Welcome, %s!"
|
|
559
562
|
}
|
package/locales/en.json
CHANGED
|
@@ -1073,5 +1073,12 @@
|
|
|
1073
1073
|
"Become user": "Become user",
|
|
1074
1074
|
"Your are now logged in as %s. Logout and login again to assume your usual identity": "Your are now logged in as %s. Logout and login again to assume your usual identity",
|
|
1075
1075
|
"Done": "Done",
|
|
1076
|
-
"Configure trigger %s": "Configure trigger %s"
|
|
1076
|
+
"Configure trigger %s": "Configure trigger %s",
|
|
1077
|
+
"Saved 2FA policy for role": "Saved 2FA policy for role",
|
|
1078
|
+
"HTTP settings updated": "HTTP settings updated",
|
|
1079
|
+
"%s configuration": "%s configuration",
|
|
1080
|
+
"Save indicator": "Save indicator",
|
|
1081
|
+
"Public cache TTL (minutes)": "Public cache TTL (minutes)",
|
|
1082
|
+
"Cache-control max-age for public views and pages. 0 to disable": "Cache-control max-age for public views and pages. 0 to disable",
|
|
1083
|
+
"Files accept filter": "Files accept filter"
|
|
1077
1084
|
}
|
package/markup/admin.js
CHANGED
|
@@ -135,6 +135,7 @@ const send_settings_page = ({
|
|
|
135
135
|
headers,
|
|
136
136
|
no_nav_pills,
|
|
137
137
|
sub2_page,
|
|
138
|
+
page_title,
|
|
138
139
|
}) => {
|
|
139
140
|
const pillCard = no_nav_pills
|
|
140
141
|
? []
|
|
@@ -163,12 +164,13 @@ const send_settings_page = ({
|
|
|
163
164
|
},
|
|
164
165
|
];
|
|
165
166
|
// headers
|
|
167
|
+
const pg_title = page_title || req.__(active_sub);
|
|
166
168
|
const title = headers
|
|
167
169
|
? {
|
|
168
|
-
title:
|
|
170
|
+
title: pg_title,
|
|
169
171
|
headers,
|
|
170
172
|
}
|
|
171
|
-
:
|
|
173
|
+
: pg_title;
|
|
172
174
|
res.sendWrap(title, {
|
|
173
175
|
above: [
|
|
174
176
|
{
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.8.1-
|
|
3
|
+
"version": "0.8.1-rc.3",
|
|
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.8.1-
|
|
10
|
-
"@saltcorn/builder": "0.8.1-
|
|
11
|
-
"@saltcorn/data": "0.8.1-
|
|
12
|
-
"@saltcorn/admin-models": "0.8.1-
|
|
13
|
-
"@saltcorn/filemanager": "0.8.1-
|
|
14
|
-
"@saltcorn/markup": "0.8.1-
|
|
15
|
-
"@saltcorn/sbadmin2": "0.8.1-
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.1-rc.3",
|
|
10
|
+
"@saltcorn/builder": "0.8.1-rc.3",
|
|
11
|
+
"@saltcorn/data": "0.8.1-rc.3",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.1-rc.3",
|
|
13
|
+
"@saltcorn/filemanager": "0.8.1-rc.3",
|
|
14
|
+
"@saltcorn/markup": "0.8.1-rc.3",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.1-rc.3",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"aws-sdk": "^2.1037.0",
|
|
@@ -523,6 +523,33 @@ function initialize_page() {
|
|
|
523
523
|
|
|
524
524
|
$(initialize_page);
|
|
525
525
|
|
|
526
|
+
function ajax_indicator(show, e) {
|
|
527
|
+
const $ind = e
|
|
528
|
+
? $(e).closest(".card,.modal").find(".sc-ajax-indicator")
|
|
529
|
+
: $(".sc-ajax-indicator");
|
|
530
|
+
$ind.find("svg").attr("data-icon", "save");
|
|
531
|
+
$ind.find("i").removeClass("fa-exclamation-triangle").addClass("fa-save");
|
|
532
|
+
$ind.css("color", "");
|
|
533
|
+
$ind.removeAttr("title");
|
|
534
|
+
if (show) $ind.show();
|
|
535
|
+
else $ind.fadeOut();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function ajax_indicate_error(e, resp) {
|
|
539
|
+
//console.error("ind error", resp);
|
|
540
|
+
const $ind = e
|
|
541
|
+
? $(e).closest(".card,.modal").find(".sc-ajax-indicator")
|
|
542
|
+
: $(".sc-ajax-indicator");
|
|
543
|
+
$ind.css("color", "#e74a3b");
|
|
544
|
+
$ind.find("svg").attr("data-icon", "exclamation-triangle");
|
|
545
|
+
$ind.find("i").removeClass("fa-save").addClass("fa-exclamation-triangle");
|
|
546
|
+
$ind.attr(
|
|
547
|
+
"title",
|
|
548
|
+
"Save error: " + (resp ? resp.responseText || resp.statusText : "unknown")
|
|
549
|
+
);
|
|
550
|
+
$ind.show();
|
|
551
|
+
}
|
|
552
|
+
|
|
526
553
|
function enable_codemirror(f) {
|
|
527
554
|
$("<link/>", {
|
|
528
555
|
rel: "stylesheet",
|
package/public/saltcorn.js
CHANGED
|
@@ -246,6 +246,9 @@ function ensure_modal_exists_and_closed() {
|
|
|
246
246
|
<div class="modal-content">
|
|
247
247
|
<div class="modal-header">
|
|
248
248
|
<h5 class="modal-title">Modal title</h5>
|
|
249
|
+
<span class="sc-ajax-indicator-wrapper">
|
|
250
|
+
<span class="sc-ajax-indicator ms-2" style="display: none;"><i class="fas fa-save"></i></span>
|
|
251
|
+
</span>
|
|
249
252
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
250
253
|
</button>
|
|
251
254
|
</div>
|
|
@@ -280,6 +283,11 @@ function ajax_modal(url, opts = {}) {
|
|
|
280
283
|
success: function (res, textStatus, request) {
|
|
281
284
|
var title = request.getResponseHeader("Page-Title");
|
|
282
285
|
var width = request.getResponseHeader("SaltcornModalWidth");
|
|
286
|
+
var saveIndicate = !!request.getResponseHeader(
|
|
287
|
+
"SaltcornModalSaveIndicator"
|
|
288
|
+
);
|
|
289
|
+
if (saveIndicate) $(".sc-ajax-indicator-wrapper").show();
|
|
290
|
+
else $(".sc-ajax-indicator-wrapper").hide();
|
|
283
291
|
if (width) $(".modal-dialog").css("max-width", width);
|
|
284
292
|
else $(".modal-dialog").css("max-width", "");
|
|
285
293
|
if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
|
|
@@ -303,6 +311,7 @@ function saveAndContinue(e, k) {
|
|
|
303
311
|
submitWithEmptyAction(form[0]);
|
|
304
312
|
var url = form.attr("action");
|
|
305
313
|
var form_data = form.serialize();
|
|
314
|
+
ajax_indicator(true, e);
|
|
306
315
|
$.ajax(url, {
|
|
307
316
|
type: "POST",
|
|
308
317
|
headers: {
|
|
@@ -310,6 +319,7 @@ function saveAndContinue(e, k) {
|
|
|
310
319
|
},
|
|
311
320
|
data: form_data,
|
|
312
321
|
success: function (res) {
|
|
322
|
+
ajax_indicator(false);
|
|
313
323
|
if (res.id && form.find("input[name=id")) {
|
|
314
324
|
form.append(
|
|
315
325
|
`<input type="hidden" class="form-control " name="id" value="${res.id}">`
|
|
@@ -318,6 +328,7 @@ function saveAndContinue(e, k) {
|
|
|
318
328
|
},
|
|
319
329
|
error: function (request) {
|
|
320
330
|
$("#page-inner-content").html(request.responseText);
|
|
331
|
+
ajax_indicate_error(e, request);
|
|
321
332
|
initialize_page();
|
|
322
333
|
},
|
|
323
334
|
complete: function () {
|
|
@@ -335,6 +346,7 @@ function applyViewConfig(e, url, k) {
|
|
|
335
346
|
form_data.forEach((item) => {
|
|
336
347
|
cfg[item.name] = item.value;
|
|
337
348
|
});
|
|
349
|
+
ajax_indicator(true, e);
|
|
338
350
|
$.ajax(url, {
|
|
339
351
|
type: "POST",
|
|
340
352
|
dataType: "json",
|
|
@@ -343,11 +355,15 @@ function applyViewConfig(e, url, k) {
|
|
|
343
355
|
"CSRF-Token": _sc_globalCsrf,
|
|
344
356
|
},
|
|
345
357
|
data: JSON.stringify(cfg),
|
|
346
|
-
error: function (request) {
|
|
358
|
+
error: function (request) {
|
|
359
|
+
ajax_indicate_error(e, request);
|
|
360
|
+
},
|
|
347
361
|
success: function (res) {
|
|
362
|
+
ajax_indicator(false);
|
|
348
363
|
k && k(res);
|
|
349
364
|
!k && updateViewPreview();
|
|
350
365
|
},
|
|
366
|
+
complete: () => {},
|
|
351
367
|
});
|
|
352
368
|
|
|
353
369
|
return false;
|
package/routes/actions.js
CHANGED
|
@@ -399,8 +399,10 @@ router.get(
|
|
|
399
399
|
req,
|
|
400
400
|
active_sub: "Triggers",
|
|
401
401
|
sub2_page: "Configure",
|
|
402
|
+
page_title: trigger.name,
|
|
402
403
|
contents: {
|
|
403
404
|
type: "card",
|
|
405
|
+
titleAjaxIndicator: true,
|
|
404
406
|
title: req.__("Configure trigger %s", trigger.name),
|
|
405
407
|
contents: {
|
|
406
408
|
widths: [8, 4],
|
|
@@ -468,8 +470,10 @@ router.get(
|
|
|
468
470
|
req,
|
|
469
471
|
active_sub: "Triggers",
|
|
470
472
|
sub2_page: "Configure",
|
|
473
|
+
page_title: req.__(`%s configuration`, trigger.name),
|
|
471
474
|
contents: {
|
|
472
475
|
type: "card",
|
|
476
|
+
titleAjaxIndicator: true,
|
|
473
477
|
title: req.__("Configure trigger %s", trigger.name),
|
|
474
478
|
contents: renderForm(form, req.csrfToken()),
|
|
475
479
|
},
|
package/routes/admin.js
CHANGED
|
@@ -97,6 +97,7 @@ const View = require("@saltcorn/data/models/view");
|
|
|
97
97
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
98
98
|
const os = require("os");
|
|
99
99
|
const Page = require("@saltcorn/data/models/page");
|
|
100
|
+
const { getSafeSaltcornCmd } = require("@saltcorn/data/utils");
|
|
100
101
|
|
|
101
102
|
const router = new Router();
|
|
102
103
|
module.exports = router;
|
|
@@ -194,6 +195,7 @@ router.get(
|
|
|
194
195
|
contents: {
|
|
195
196
|
type: "card",
|
|
196
197
|
title: req.__("Site identity settings"),
|
|
198
|
+
titleAjaxIndicator: true,
|
|
197
199
|
contents: [renderForm(form, req.csrfToken())],
|
|
198
200
|
},
|
|
199
201
|
});
|
|
@@ -251,6 +253,7 @@ router.get(
|
|
|
251
253
|
contents: {
|
|
252
254
|
type: "card",
|
|
253
255
|
title: req.__("Email settings"),
|
|
256
|
+
titleAjaxIndicator: true,
|
|
254
257
|
contents: [
|
|
255
258
|
renderForm(form, req.csrfToken()),
|
|
256
259
|
a(
|
|
@@ -321,9 +324,10 @@ router.post(
|
|
|
321
324
|
});
|
|
322
325
|
} else {
|
|
323
326
|
await save_config_from_form(form);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
if (!req.xhr) {
|
|
328
|
+
req.flash("success", req.__("Email settings updated"));
|
|
329
|
+
res.redirect("/admin/email");
|
|
330
|
+
} else res.json({ success: "ok" });
|
|
327
331
|
}
|
|
328
332
|
})
|
|
329
333
|
);
|
|
@@ -391,6 +395,7 @@ router.get(
|
|
|
391
395
|
? {
|
|
392
396
|
type: "card",
|
|
393
397
|
title: req.__("Automated backup"),
|
|
398
|
+
titleAjaxIndicator: true,
|
|
394
399
|
contents: div(
|
|
395
400
|
renderForm(backupForm, req.csrfToken()),
|
|
396
401
|
a(
|
|
@@ -408,6 +413,7 @@ router.get(
|
|
|
408
413
|
{
|
|
409
414
|
type: "card",
|
|
410
415
|
title: req.__("Snapshots"),
|
|
416
|
+
titleAjaxIndicator: true,
|
|
411
417
|
contents: div(
|
|
412
418
|
p(
|
|
413
419
|
i(
|
|
@@ -708,9 +714,11 @@ router.post(
|
|
|
708
714
|
form.validate(req.body);
|
|
709
715
|
|
|
710
716
|
await save_config_from_form(form);
|
|
711
|
-
|
|
712
|
-
if (!req.xhr)
|
|
713
|
-
|
|
717
|
+
|
|
718
|
+
if (!req.xhr) {
|
|
719
|
+
req.flash("success", req.__("Snapshot settings updated"));
|
|
720
|
+
res.redirect("/admin/backup");
|
|
721
|
+
} else res.json({ success: "ok" });
|
|
714
722
|
})
|
|
715
723
|
);
|
|
716
724
|
router.post(
|
|
@@ -732,9 +740,10 @@ router.post(
|
|
|
732
740
|
});
|
|
733
741
|
} else {
|
|
734
742
|
await save_config_from_form(form);
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
743
|
+
if (!req.xhr) {
|
|
744
|
+
req.flash("success", req.__("Backup settings updated"));
|
|
745
|
+
res.redirect("/admin/backup");
|
|
746
|
+
} else res.json({ success: "ok" });
|
|
738
747
|
}
|
|
739
748
|
})
|
|
740
749
|
);
|
|
@@ -1306,7 +1315,7 @@ const buildDialogScript = () => {
|
|
|
1306
1315
|
}
|
|
1307
1316
|
|
|
1308
1317
|
function handleMessages() {
|
|
1309
|
-
notifyAlert("
|
|
1318
|
+
notifyAlert("Building the app, please wait.")
|
|
1310
1319
|
${
|
|
1311
1320
|
getState().getConfig("apple_team_id") &&
|
|
1312
1321
|
getState().getConfig("apple_team_id") !== "null"
|
|
@@ -1651,7 +1660,7 @@ router.post(
|
|
|
1651
1660
|
// end http call, return the out directory name
|
|
1652
1661
|
// the gui polls for results
|
|
1653
1662
|
res.json({ build_dir_name: outDirName });
|
|
1654
|
-
const child = spawn(
|
|
1663
|
+
const child = spawn(getSafeSaltcornCmd(), spawnParams, {
|
|
1655
1664
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1656
1665
|
cwd: ".",
|
|
1657
1666
|
});
|
|
@@ -1858,6 +1867,7 @@ router.get(
|
|
|
1858
1867
|
contents: {
|
|
1859
1868
|
type: "card",
|
|
1860
1869
|
title: req.__("Development settings"),
|
|
1870
|
+
titleAjaxIndicator: true,
|
|
1861
1871
|
contents: [
|
|
1862
1872
|
renderForm(form, req.csrfToken()) /*,
|
|
1863
1873
|
a(
|
|
@@ -1899,9 +1909,10 @@ router.post(
|
|
|
1899
1909
|
});
|
|
1900
1910
|
} else {
|
|
1901
1911
|
await save_config_from_form(form);
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1912
|
+
if (!req.xhr) {
|
|
1913
|
+
req.flash("success", req.__("Development mode settings updated"));
|
|
1914
|
+
res.redirect("/admin/dev");
|
|
1915
|
+
} else res.json({ success: "ok" });
|
|
1905
1916
|
}
|
|
1906
1917
|
})
|
|
1907
1918
|
);
|
package/routes/eventlog.js
CHANGED
package/routes/fields.js
CHANGED
|
@@ -711,20 +711,54 @@ router.post(
|
|
|
711
711
|
const fields = await table.getFields();
|
|
712
712
|
let row = { ...req.body };
|
|
713
713
|
if (row && Object.keys(row).length > 0) readState(row, fields);
|
|
714
|
+
|
|
715
|
+
//need to get join fields from ownership into row
|
|
716
|
+
const joinFields = {};
|
|
717
|
+
if (table.ownership_formula && role > table.min_role_read) {
|
|
718
|
+
const freeVars = freeVariables(table.ownership_formula);
|
|
719
|
+
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
720
|
+
}
|
|
721
|
+
//console.log(joinFields, row);
|
|
714
722
|
const id = req.query.id || row.id;
|
|
715
723
|
if (id) {
|
|
716
|
-
let dbrow = await table.
|
|
724
|
+
let [dbrow] = await table.getJoinedRows({ where: { id }, joinFields });
|
|
717
725
|
row = { ...dbrow, ...row };
|
|
718
726
|
//prevent overwriting ownership field
|
|
719
727
|
if (table.ownership_field_id) {
|
|
720
728
|
const ofield = fields.find((f) => f.id === table.ownership_field_id);
|
|
721
729
|
row[ofield.name] = dbrow[ofield.name];
|
|
722
730
|
}
|
|
731
|
+
} else {
|
|
732
|
+
//may need to add joinfields
|
|
733
|
+
for (const { ref } of Object.values(joinFields)) {
|
|
734
|
+
if (row[ref]) {
|
|
735
|
+
const field = fields.find((f) => f.name === ref);
|
|
736
|
+
const reftable = await Table.findOne({ name: field.reftable_name });
|
|
737
|
+
const refFields = await reftable.getFields();
|
|
738
|
+
|
|
739
|
+
const joinFields = {};
|
|
740
|
+
if (reftable.ownership_formula && role > reftable.min_role_read) {
|
|
741
|
+
const freeVars = freeVariables(reftable.ownership_formula);
|
|
742
|
+
add_free_variables_to_joinfields(freeVars, joinFields, refFields);
|
|
743
|
+
}
|
|
744
|
+
const [refRow] = await reftable.getJoinedRows({
|
|
745
|
+
where: { id: row[ref] },
|
|
746
|
+
joinFields,
|
|
747
|
+
});
|
|
748
|
+
if (
|
|
749
|
+
role <= reftable.min_role_read ||
|
|
750
|
+
(req.user && reftable.is_owner(req.user, refRow))
|
|
751
|
+
) {
|
|
752
|
+
row[ref] = refRow;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
723
756
|
}
|
|
724
757
|
if (
|
|
725
758
|
role > table.min_role_read &&
|
|
726
759
|
!(req.user && table.is_owner(req.user, row))
|
|
727
760
|
) {
|
|
761
|
+
//console.log("not owner", row, table.is_owner(req.user, row));
|
|
728
762
|
res.status(401).send("");
|
|
729
763
|
return;
|
|
730
764
|
}
|
|
@@ -734,16 +768,25 @@ router.post(
|
|
|
734
768
|
if (kpath.length === 2 && row[kpath[0]]) {
|
|
735
769
|
const field = fields.find((f) => f.name === kpath[0]);
|
|
736
770
|
const reftable = await Table.findOne({ name: field.reftable_name });
|
|
737
|
-
|
|
771
|
+
const refFields = await reftable.getFields();
|
|
772
|
+
const targetField = refFields.find((f) => f.name === kpath[1]);
|
|
773
|
+
//console.log({ kpath, fieldview, targetField });
|
|
774
|
+
const q = { [reftable.pk_name]: row[kpath[0]] };
|
|
775
|
+
const joinFields = {};
|
|
776
|
+
if (reftable.ownership_formula && role > reftable.min_role_read) {
|
|
777
|
+
const freeVars = freeVariables(reftable.ownership_formula);
|
|
778
|
+
add_free_variables_to_joinfields(freeVars, joinFields, refFields);
|
|
779
|
+
}
|
|
780
|
+
const [refRow] = await reftable.getJoinedRows({ where: q, joinFields });
|
|
781
|
+
if (
|
|
782
|
+
role > reftable.min_role_read &&
|
|
783
|
+
!(req.user && reftable.is_owner(req.user, refRow))
|
|
784
|
+
) {
|
|
785
|
+
//console.log("not jointable owner", refRow);
|
|
786
|
+
|
|
738
787
|
res.status(401).send("");
|
|
739
788
|
return;
|
|
740
789
|
}
|
|
741
|
-
const targetField = (await reftable.getFields()).find(
|
|
742
|
-
(f) => f.name === kpath[1]
|
|
743
|
-
);
|
|
744
|
-
//console.log({ kpath, fieldview, targetField });
|
|
745
|
-
const q = { [reftable.pk_name]: row[kpath[0]] };
|
|
746
|
-
const refRow = await reftable.getRow(q);
|
|
747
790
|
let fv;
|
|
748
791
|
if (targetField.type === "Key") {
|
|
749
792
|
fv = getState().keyFieldviews[fieldview];
|
|
@@ -953,7 +996,8 @@ router.post(
|
|
|
953
996
|
formStyle: "vert",
|
|
954
997
|
fields: formFields,
|
|
955
998
|
});
|
|
956
|
-
if (_columndef
|
|
999
|
+
if (_columndef && _columndef !== "undefined")
|
|
1000
|
+
form.values = JSON.parse(_columndef);
|
|
957
1001
|
res.send(mkFormContentNoLayout(form));
|
|
958
1002
|
})
|
|
959
1003
|
);
|
package/routes/files.js
CHANGED
|
@@ -393,6 +393,13 @@ router.post(
|
|
|
393
393
|
f.s3_store ? s3storage.unlinkObject : undefined
|
|
394
394
|
);
|
|
395
395
|
if (result && result.error) {
|
|
396
|
+
if (req.xhr) {
|
|
397
|
+
const root = path.join(db.connectObj.file_store, db.getTenantSchema());
|
|
398
|
+
res.json({
|
|
399
|
+
error: result.error.replaceAll(root, ""),
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
396
403
|
req.flash("error", result.error);
|
|
397
404
|
}
|
|
398
405
|
res.redirect(`/files?dir=${encodeURIComponent(f.current_folder)}`);
|
|
@@ -438,6 +445,7 @@ router.get(
|
|
|
438
445
|
contents: {
|
|
439
446
|
type: "card",
|
|
440
447
|
title: req.__("Storage settings"),
|
|
448
|
+
titleAjaxIndicator: true,
|
|
441
449
|
contents: [renderForm(form, req.csrfToken())],
|
|
442
450
|
},
|
|
443
451
|
});
|
|
@@ -512,6 +520,7 @@ router.get(
|
|
|
512
520
|
active_sub: "Settings",
|
|
513
521
|
contents: {
|
|
514
522
|
type: "card",
|
|
523
|
+
titleAjaxIndicator: true,
|
|
515
524
|
title: req.__("Files settings"),
|
|
516
525
|
contents: [renderForm(form, req.csrfToken())],
|
|
517
526
|
},
|
package/routes/list.js
CHANGED
|
@@ -308,6 +308,13 @@ router.get(
|
|
|
308
308
|
],
|
|
309
309
|
right: div(
|
|
310
310
|
{ class: "d-flex" },
|
|
311
|
+
div(
|
|
312
|
+
{
|
|
313
|
+
class: "sc-ajax-indicator me-2",
|
|
314
|
+
style: { display: "none" },
|
|
315
|
+
},
|
|
316
|
+
i({ class: "fas fa-save" })
|
|
317
|
+
),
|
|
311
318
|
button(
|
|
312
319
|
{
|
|
313
320
|
class: "btn btn-sm btn-primary me-2",
|
|
@@ -391,6 +398,7 @@ router.get(
|
|
|
391
398
|
});
|
|
392
399
|
window.tabulator_table.on("cellEdited", function(cell){
|
|
393
400
|
const row = cell.getRow().getData()
|
|
401
|
+
ajax_indicator(true);
|
|
394
402
|
$.ajax({
|
|
395
403
|
type: "POST",
|
|
396
404
|
url: "/api/${table.name}/" + (row.id||""),
|
|
@@ -400,12 +408,15 @@ router.get(
|
|
|
400
408
|
},
|
|
401
409
|
error: tabulator_error_handler,
|
|
402
410
|
}).done(function (resp) {
|
|
411
|
+
ajax_indicator(false);
|
|
403
412
|
//if (item._versions) item._versions = +item._versions + 1;
|
|
404
413
|
//data.resolve(fixKeys(item));
|
|
405
414
|
if(resp.success &&typeof resp.success ==="number" && !row.id) {
|
|
406
415
|
window.tabulator_table.updateRow(cell.getRow(), {id: resp.success});
|
|
407
416
|
}
|
|
408
417
|
|
|
418
|
+
}).fail(function (resp) {
|
|
419
|
+
ajax_indicate_error(undefined, resp);
|
|
409
420
|
});
|
|
410
421
|
});
|
|
411
422
|
window.tabulator_table_name="${table.name}";`)
|
package/routes/menu.js
CHANGED
|
@@ -309,8 +309,14 @@ const menuEditorScript = (menu_items) => `
|
|
|
309
309
|
const s = editor.getString()
|
|
310
310
|
if(s===lastState && !skip_check) return;
|
|
311
311
|
lastState=s;
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
ajax_indicator(true);
|
|
313
|
+
ajax_post('/menu', {
|
|
314
|
+
data: s,
|
|
315
|
+
success: ()=>{ ajax_indicator(false)},
|
|
316
|
+
dataType : 'json',
|
|
317
|
+
contentType: 'application/json;charset=UTF-8',
|
|
318
|
+
error: (r) => {ajax_indicate_error(undefined, r); }
|
|
319
|
+
})
|
|
314
320
|
}
|
|
315
321
|
var sortableListOptions = {
|
|
316
322
|
placeholderCss: {'background-color': "#cccccc"},
|
|
@@ -395,6 +401,7 @@ router.get(
|
|
|
395
401
|
contents: {
|
|
396
402
|
type: "card",
|
|
397
403
|
title: req.__(`Menu editor`),
|
|
404
|
+
titleAjaxIndicator: true,
|
|
398
405
|
contents: {
|
|
399
406
|
above: [
|
|
400
407
|
{
|
package/routes/pageedit.js
CHANGED
|
@@ -251,6 +251,7 @@ router.get(
|
|
|
251
251
|
{
|
|
252
252
|
type: "card",
|
|
253
253
|
title: req.__("Root pages"),
|
|
254
|
+
titleAjaxIndicator: true,
|
|
254
255
|
contents: renderForm(
|
|
255
256
|
getRootPageForm(pages, roles, req),
|
|
256
257
|
req.csrfToken()
|
|
@@ -402,7 +403,7 @@ router.get(
|
|
|
402
403
|
version_tag: db.connectObj.version_tag,
|
|
403
404
|
};
|
|
404
405
|
res.sendWrap(
|
|
405
|
-
req.__(
|
|
406
|
+
req.__(`%s configuration`, page.name),
|
|
406
407
|
wrap(renderBuilder(builderData, req.csrfToken()), true, req, page)
|
|
407
408
|
);
|
|
408
409
|
}
|
package/routes/plugins.js
CHANGED
|
@@ -583,10 +583,23 @@ router.get(
|
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
586
|
+
above: [
|
|
587
|
+
{
|
|
588
|
+
type: "breadcrumbs",
|
|
589
|
+
crumbs: [
|
|
590
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
591
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
592
|
+
{ text: plugin.name },
|
|
593
|
+
],
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
type: "card",
|
|
597
|
+
class: "mt-0",
|
|
598
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
599
|
+
titleAjaxIndicator: true,
|
|
600
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
601
|
+
},
|
|
602
|
+
],
|
|
590
603
|
});
|
|
591
604
|
})
|
|
592
605
|
);
|
package/routes/search.js
CHANGED
package/routes/tables.js
CHANGED
|
@@ -828,6 +828,7 @@ router.get(
|
|
|
828
828
|
{
|
|
829
829
|
type: "card",
|
|
830
830
|
title: req.__("Edit table properties"),
|
|
831
|
+
titleAjaxIndicator: true,
|
|
831
832
|
contents: renderForm(tblForm, req.csrfToken()),
|
|
832
833
|
},
|
|
833
834
|
],
|
|
@@ -899,20 +900,21 @@ router.post(
|
|
|
899
900
|
}
|
|
900
901
|
} else rest.ownership_formula = null;
|
|
901
902
|
await table.update(rest);
|
|
902
|
-
if (!old_versioned && rest.versioned)
|
|
903
|
-
req.flash(
|
|
904
|
-
"success",
|
|
905
|
-
req.__("Table saved with version history enabled")
|
|
906
|
-
);
|
|
907
|
-
else if (old_versioned && !rest.versioned)
|
|
908
|
-
req.flash(
|
|
909
|
-
"success",
|
|
910
|
-
req.__("Table saved with version history disabled")
|
|
911
|
-
);
|
|
912
|
-
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
913
903
|
|
|
914
|
-
if (!req.xhr)
|
|
915
|
-
|
|
904
|
+
if (!req.xhr) {
|
|
905
|
+
if (!old_versioned && rest.versioned)
|
|
906
|
+
req.flash(
|
|
907
|
+
"success",
|
|
908
|
+
req.__("Table saved with version history enabled")
|
|
909
|
+
);
|
|
910
|
+
else if (old_versioned && !rest.versioned)
|
|
911
|
+
req.flash(
|
|
912
|
+
"success",
|
|
913
|
+
req.__("Table saved with version history disabled")
|
|
914
|
+
);
|
|
915
|
+
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
916
|
+
res.redirect(`/table/${id}`);
|
|
917
|
+
} else res.json({ success: "ok", notify });
|
|
916
918
|
}
|
|
917
919
|
})
|
|
918
920
|
);
|
package/routes/tenant.js
CHANGED
package/routes/utils.js
CHANGED
|
@@ -76,7 +76,7 @@ const setLanguage = (req, res, state) => {
|
|
|
76
76
|
} else if (req.cookies?.lang) {
|
|
77
77
|
req.setLocale(req.cookies?.lang);
|
|
78
78
|
}
|
|
79
|
-
set_custom_http_headers(res, state);
|
|
79
|
+
set_custom_http_headers(res, req, state);
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -85,8 +85,17 @@ const setLanguage = (req, res, state) => {
|
|
|
85
85
|
* @param {string} state
|
|
86
86
|
* @returns {void}
|
|
87
87
|
*/
|
|
88
|
-
const set_custom_http_headers = (res, state) => {
|
|
89
|
-
const
|
|
88
|
+
const set_custom_http_headers = (res, req, state) => {
|
|
89
|
+
const state1 = state || getState();
|
|
90
|
+
const hdrs = state1.getConfig("custom_http_headers");
|
|
91
|
+
if (!req.user) {
|
|
92
|
+
const public_cache_maxage = +state1.getConfig("public_cache_maxage", 0);
|
|
93
|
+
if (public_cache_maxage)
|
|
94
|
+
res.header(
|
|
95
|
+
"Cache-Control",
|
|
96
|
+
`public, max-age=${public_cache_maxage * 60}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
90
99
|
if (!hdrs) return;
|
|
91
100
|
for (const ln of hdrs.split("\n")) {
|
|
92
101
|
const [k, v] = ln.split(":");
|
package/routes/view.js
CHANGED
package/routes/viewedit.js
CHANGED
|
@@ -239,6 +239,13 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
239
239
|
options: ["px", "%", "vw", "em", "rem"],
|
|
240
240
|
},
|
|
241
241
|
},
|
|
242
|
+
{
|
|
243
|
+
name: "popup_save_indicator",
|
|
244
|
+
label: req.__("Save indicator"),
|
|
245
|
+
type: "Bool",
|
|
246
|
+
parent_field: "attributes",
|
|
247
|
+
tab: "Popup settings",
|
|
248
|
+
},
|
|
242
249
|
...(isEdit
|
|
243
250
|
? [
|
|
244
251
|
new Field({
|
|
@@ -457,7 +464,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
457
464
|
type: "breadcrumbs",
|
|
458
465
|
crumbs: [
|
|
459
466
|
{ text: req.__("Views"), href: "/viewedit" },
|
|
460
|
-
{ href: `/
|
|
467
|
+
{ href: `/view/${view.name}`, text: view.name },
|
|
461
468
|
{ workflow: wf, step: wfres },
|
|
462
469
|
],
|
|
463
470
|
},
|
|
@@ -465,6 +472,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
465
472
|
type: noCard ? "container" : "card",
|
|
466
473
|
class: !noCard && "mt-0",
|
|
467
474
|
title: wfres.title,
|
|
475
|
+
titleAjaxIndicator: true,
|
|
468
476
|
contents,
|
|
469
477
|
},
|
|
470
478
|
...(previewURL
|
|
@@ -485,7 +493,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
485
493
|
if (wfres.renderForm)
|
|
486
494
|
res.sendWrap(
|
|
487
495
|
{
|
|
488
|
-
title: req.__(
|
|
496
|
+
title: req.__(`%s configuration`, view.name),
|
|
489
497
|
headers: [
|
|
490
498
|
{
|
|
491
499
|
script: `/static_assets/${db.connectObj.version_tag}/jquery-menu-editor.min.js`,
|
|
@@ -510,7 +518,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
510
518
|
else if (wfres.renderBuilder) {
|
|
511
519
|
wfres.renderBuilder.options.view_id = view.id;
|
|
512
520
|
res.sendWrap(
|
|
513
|
-
req.__(
|
|
521
|
+
req.__(`%s configuration`, view.name),
|
|
514
522
|
wrap(renderBuilder(wfres.renderBuilder, req.csrfToken()), true)
|
|
515
523
|
);
|
|
516
524
|
} else res.redirect(wfres.redirect);
|
package/serve.js
CHANGED
|
@@ -377,7 +377,7 @@ const setupSocket = (...servers) => {
|
|
|
377
377
|
const view = View.findOne({ name: viewname });
|
|
378
378
|
if (view.viewtemplateObj.authorize_join) {
|
|
379
379
|
view.viewtemplateObj
|
|
380
|
-
.authorize_join(view
|
|
380
|
+
.authorize_join(view, room_id, socket.request.user)
|
|
381
381
|
.then((authorized) => {
|
|
382
382
|
if (authorized) socket.join(`${ten}_${viewname}_${room_id}`);
|
|
383
383
|
});
|
package/wrapper.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @module wrapper
|
|
4
4
|
*/
|
|
5
5
|
const { getState } = require("@saltcorn/data/db/state");
|
|
6
|
+
const { get_extra_menu } = require("@saltcorn/data/web-mobile-commons");
|
|
6
7
|
//const db = require("@saltcorn/data/db");
|
|
7
8
|
const { h3, div, small } = require("@saltcorn/markup/tags");
|
|
8
9
|
const { renderForm, link } = require("@saltcorn/markup");
|
|
@@ -18,43 +19,6 @@ const getFlashes = (req) =>
|
|
|
18
19
|
return { type, msg: req.flash(type) };
|
|
19
20
|
})
|
|
20
21
|
.filter((a) => a.msg && a.msg.length && a.msg.length > 0);
|
|
21
|
-
/**
|
|
22
|
-
* Get extra menu
|
|
23
|
-
* @param role
|
|
24
|
-
* @param state
|
|
25
|
-
* @param req
|
|
26
|
-
* @returns {*}
|
|
27
|
-
*/
|
|
28
|
-
const get_extra_menu = (role, state, req) => {
|
|
29
|
-
let cfg = getState().getConfig("unrolled_menu_items", []);
|
|
30
|
-
if (!cfg || cfg.length === 0) {
|
|
31
|
-
cfg = getState().getConfig("menu_items", []);
|
|
32
|
-
}
|
|
33
|
-
const locale = req.getLocale();
|
|
34
|
-
const __ = (s) => state.i18n.__({ phrase: s, locale }) || s;
|
|
35
|
-
const transform = (items) =>
|
|
36
|
-
items
|
|
37
|
-
.filter((item) => role <= +item.min_role)
|
|
38
|
-
.map((item) => ({
|
|
39
|
-
label: __(item.label),
|
|
40
|
-
icon: item.icon,
|
|
41
|
-
location: item.location,
|
|
42
|
-
style: item.style || "",
|
|
43
|
-
type: item.type,
|
|
44
|
-
link:
|
|
45
|
-
item.type === "Link"
|
|
46
|
-
? item.url
|
|
47
|
-
: item.type === "Action"
|
|
48
|
-
? `javascript:ajax_post_json('/menu/runaction/${item.action_name}')`
|
|
49
|
-
: item.type === "View"
|
|
50
|
-
? `/view/${encodeURIComponent(item.viewname)}`
|
|
51
|
-
: item.type === "Page"
|
|
52
|
-
? `/page/${encodeURIComponent(item.pagename)}`
|
|
53
|
-
: undefined,
|
|
54
|
-
...(item.subitems ? { subitems: transform(item.subitems) } : {}),
|
|
55
|
-
}));
|
|
56
|
-
return transform(cfg);
|
|
57
|
-
};
|
|
58
22
|
/**
|
|
59
23
|
* Get menu
|
|
60
24
|
* @param req
|
|
@@ -67,7 +31,9 @@ const get_menu = (req) => {
|
|
|
67
31
|
|
|
68
32
|
const allow_signup = state.getConfig("allow_signup");
|
|
69
33
|
const login_menu = state.getConfig("login_menu");
|
|
70
|
-
const
|
|
34
|
+
const locale = req.getLocale();
|
|
35
|
+
const __ = (s) => state.i18n.__({ phrase: s, locale }) || s;
|
|
36
|
+
const extra_menu = get_extra_menu(role, __);
|
|
71
37
|
const authItems = isAuth
|
|
72
38
|
? [
|
|
73
39
|
{
|