@saltcorn/server 0.7.3-beta.6 → 0.7.4-beta.0
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 +9 -5
- package/auth/routes.js +19 -9
- package/errors.js +51 -48
- package/locales/en.json +12 -1
- package/locales/zh.json +187 -187
- package/markup/admin.js +4 -3
- package/package.json +7 -7
- package/public/jquery-menu-editor.min.js +11 -1
- package/public/saltcorn-common.js +26 -10
- package/public/saltcorn.css +17 -0
- package/public/saltcorn.js +5 -1
- package/routes/admin.js +296 -23
- package/routes/api.js +9 -1
- package/routes/eventlog.js +24 -22
- package/routes/files.js +5 -5
- package/routes/infoarch.js +6 -3
- package/routes/page.js +5 -1
- package/routes/pageedit.js +9 -1
- package/routes/plugins.js +77 -11
- package/routes/search.js +4 -2
- package/routes/tables.js +4 -3
- package/routes/tenant.js +4 -2
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +16 -3
- package/serve.js +54 -38
- package/tests/api.test.js +17 -0
- package/tests/clientjs.test.js +11 -1
package/routes/pageedit.js
CHANGED
|
@@ -90,6 +90,13 @@ const page_dropdown = (page, req) =>
|
|
|
90
90
|
'<i class="far fa-copy"></i> ' + req.__("Duplicate"),
|
|
91
91
|
req
|
|
92
92
|
),
|
|
93
|
+
a(
|
|
94
|
+
{
|
|
95
|
+
class: "dropdown-item",
|
|
96
|
+
href: `javascript:ajax_modal('/admin/snapshot-restore/page/${page.name}')`,
|
|
97
|
+
},
|
|
98
|
+
'<i class="fas fa-undo-alt"></i> ' + req.__("Restore")
|
|
99
|
+
),
|
|
93
100
|
div({ class: "dropdown-divider" }),
|
|
94
101
|
post_dropdown_item(
|
|
95
102
|
`/pageedit/delete/${page.id}`,
|
|
@@ -180,7 +187,8 @@ const pageBuilderData = async (req, context) => {
|
|
|
180
187
|
const fixed_state_fields = {};
|
|
181
188
|
for (const view of views) {
|
|
182
189
|
fixed_state_fields[view.name] = [];
|
|
183
|
-
const table = Table.findOne(
|
|
190
|
+
const table = Table.findOne(view.table_id || view.exttable_name);
|
|
191
|
+
|
|
184
192
|
const fs = await view.get_state_fields();
|
|
185
193
|
for (const frec of fs) {
|
|
186
194
|
const f = new Field(frec);
|
package/routes/plugins.js
CHANGED
|
@@ -561,12 +561,30 @@ router.get(
|
|
|
561
561
|
}
|
|
562
562
|
const flow = module.configuration_workflow();
|
|
563
563
|
flow.action = `/plugins/configure/${encodeURIComponent(plugin.name)}`;
|
|
564
|
+
flow.autoSave = true;
|
|
565
|
+
flow.saveURL = `/plugins/saveconfig/${encodeURIComponent(plugin.name)}`;
|
|
564
566
|
const wfres = await flow.run(plugin.configuration || {});
|
|
567
|
+
if (module.layout) {
|
|
568
|
+
wfres.renderForm.additionalButtons = [
|
|
569
|
+
...(wfres.renderForm.additionalButtons || []),
|
|
570
|
+
{
|
|
571
|
+
label: "Reload page to see changes",
|
|
572
|
+
id: "btnReloadNow",
|
|
573
|
+
class: "btn btn-outline-secondary",
|
|
574
|
+
onclick: "location.reload()",
|
|
575
|
+
},
|
|
576
|
+
];
|
|
577
|
+
wfres.renderForm.onChange = `${
|
|
578
|
+
wfres.renderForm.onChange || ""
|
|
579
|
+
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
580
|
+
}
|
|
565
581
|
|
|
566
|
-
res.sendWrap(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
582
|
+
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
583
|
+
type: "card",
|
|
584
|
+
class: "mt-0",
|
|
585
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
586
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
587
|
+
});
|
|
570
588
|
})
|
|
571
589
|
);
|
|
572
590
|
|
|
@@ -588,13 +606,31 @@ router.post(
|
|
|
588
606
|
}
|
|
589
607
|
const flow = module.configuration_workflow();
|
|
590
608
|
flow.action = `/plugins/configure/${encodeURIComponent(plugin.name)}`;
|
|
609
|
+
flow.autoSave = true;
|
|
610
|
+
flow.saveURL = `/plugins/saveconfig/${encodeURIComponent(plugin.name)}`;
|
|
591
611
|
const wfres = await flow.run(req.body);
|
|
592
|
-
if (wfres.renderForm)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
612
|
+
if (wfres.renderForm) {
|
|
613
|
+
if (module.layout) {
|
|
614
|
+
wfres.renderForm.additionalButtons = [
|
|
615
|
+
...(wfres.renderForm.additionalButtons || []),
|
|
616
|
+
{
|
|
617
|
+
label: "Reload page to see changes",
|
|
618
|
+
id: "btnReloadNow",
|
|
619
|
+
class: "btn btn-outline-secondary",
|
|
620
|
+
onclick: "location.reload()",
|
|
621
|
+
},
|
|
622
|
+
];
|
|
623
|
+
wfres.renderForm.onChange = `${
|
|
624
|
+
wfres.renderForm.onChange || ""
|
|
625
|
+
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
626
|
+
}
|
|
627
|
+
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
628
|
+
type: "card",
|
|
629
|
+
class: "mt-0",
|
|
630
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
631
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
632
|
+
});
|
|
633
|
+
} else {
|
|
598
634
|
plugin.configuration = wfres;
|
|
599
635
|
await plugin.upsert();
|
|
600
636
|
await load_plugins.loadPlugin(plugin);
|
|
@@ -606,12 +642,42 @@ router.post(
|
|
|
606
642
|
refresh_plugin_cfg: plugin.name,
|
|
607
643
|
tenant: db.getTenantSchema(),
|
|
608
644
|
});
|
|
609
|
-
await sleep(500); // Allow other workers to reload this plugin
|
|
645
|
+
if (module.layout) await sleep(500); // Allow other workers to reload this plugin
|
|
610
646
|
res.redirect("/plugins");
|
|
611
647
|
}
|
|
612
648
|
})
|
|
613
649
|
);
|
|
614
650
|
|
|
651
|
+
router.post(
|
|
652
|
+
"/saveconfig/:name",
|
|
653
|
+
isAdmin,
|
|
654
|
+
error_catcher(async (req, res) => {
|
|
655
|
+
const { name } = req.params;
|
|
656
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
657
|
+
let module = getState().plugins[plugin.name];
|
|
658
|
+
if (!module) {
|
|
659
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
660
|
+
}
|
|
661
|
+
const flow = module.configuration_workflow();
|
|
662
|
+
const step = await flow.singleStepForm(req.body, req);
|
|
663
|
+
if (step?.renderForm) {
|
|
664
|
+
if (!step.renderForm.hasErrors) {
|
|
665
|
+
plugin.configuration = {
|
|
666
|
+
...plugin.configuration,
|
|
667
|
+
...step.renderForm.values,
|
|
668
|
+
};
|
|
669
|
+
await plugin.upsert();
|
|
670
|
+
await load_plugins.loadPlugin(plugin);
|
|
671
|
+
process.send &&
|
|
672
|
+
process.send({
|
|
673
|
+
refresh_plugin_cfg: plugin.name,
|
|
674
|
+
tenant: db.getTenantSchema(),
|
|
675
|
+
});
|
|
676
|
+
res.json({ success: "ok" });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
})
|
|
680
|
+
);
|
|
615
681
|
/**
|
|
616
682
|
* @name get/new
|
|
617
683
|
* @function
|
package/routes/search.js
CHANGED
|
@@ -55,7 +55,8 @@ const searchConfigForm = (tables, views, req) => {
|
|
|
55
55
|
);
|
|
56
56
|
return new Form({
|
|
57
57
|
action: "/search/config",
|
|
58
|
-
|
|
58
|
+
noSubmitButton: true,
|
|
59
|
+
onChange: `saveAndContinue(this)`,
|
|
59
60
|
blurb:
|
|
60
61
|
blurb1 +
|
|
61
62
|
(tbls_noviews.length > 0
|
|
@@ -111,7 +112,8 @@ router.post(
|
|
|
111
112
|
|
|
112
113
|
if (result.success) {
|
|
113
114
|
await getState().setConfig("globalSearch", result.success);
|
|
114
|
-
res.redirect("/search/config");
|
|
115
|
+
if (!req.xhr) res.redirect("/search/config");
|
|
116
|
+
else res.json({ success: "ok" });
|
|
115
117
|
} else {
|
|
116
118
|
send_infoarch_page({
|
|
117
119
|
res,
|
package/routes/tables.js
CHANGED
|
@@ -83,8 +83,8 @@ const tableForm = async (table, req) => {
|
|
|
83
83
|
.map((f) => ({ value: f.id, label: f.name }));
|
|
84
84
|
const form = new Form({
|
|
85
85
|
action: "/table",
|
|
86
|
-
|
|
87
|
-
onChange: "
|
|
86
|
+
noSubmitButton: true,
|
|
87
|
+
onChange: "saveAndContinue(this)",
|
|
88
88
|
fields: [
|
|
89
89
|
...(!table.external
|
|
90
90
|
? [
|
|
@@ -910,7 +910,8 @@ router.post(
|
|
|
910
910
|
);
|
|
911
911
|
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
912
912
|
|
|
913
|
-
res.redirect(`/table/${id}`);
|
|
913
|
+
if (!req.xhr) res.redirect(`/table/${id}`);
|
|
914
|
+
else res.json({ success: "ok" });
|
|
914
915
|
}
|
|
915
916
|
})
|
|
916
917
|
);
|
package/routes/tenant.js
CHANGED
|
@@ -452,8 +452,10 @@ router.post(
|
|
|
452
452
|
} else {
|
|
453
453
|
await save_config_from_form(form);
|
|
454
454
|
|
|
455
|
-
|
|
456
|
-
|
|
455
|
+
if (!req.xhr) {
|
|
456
|
+
req.flash("success", req.__("Tenant settings updated"));
|
|
457
|
+
res.redirect("/tenant/settings");
|
|
458
|
+
} else res.json({ success: "ok" });
|
|
457
459
|
}
|
|
458
460
|
})
|
|
459
461
|
);
|
package/routes/utils.js
CHANGED
|
@@ -128,6 +128,7 @@ const setTenant = (req, res, next) => {
|
|
|
128
128
|
} else {
|
|
129
129
|
db.runWithTenant(other_domain, () => {
|
|
130
130
|
setLanguage(req, res, state);
|
|
131
|
+
state.log(5, `${req.method} ${req.originalUrl}`);
|
|
131
132
|
next();
|
|
132
133
|
});
|
|
133
134
|
}
|
|
@@ -140,12 +141,14 @@ const setTenant = (req, res, next) => {
|
|
|
140
141
|
} else {
|
|
141
142
|
db.runWithTenant(ten, () => {
|
|
142
143
|
setLanguage(req, res, state);
|
|
144
|
+
state.log(5, `${req.method} ${req.originalUrl}`);
|
|
143
145
|
next();
|
|
144
146
|
});
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
} else {
|
|
148
150
|
setLanguage(req, res);
|
|
151
|
+
getState().log(5, `${req.method} ${req.originalUrl}`);
|
|
149
152
|
next();
|
|
150
153
|
}
|
|
151
154
|
};
|
|
@@ -240,4 +243,5 @@ module.exports = {
|
|
|
240
243
|
getGitRevision,
|
|
241
244
|
getSessionStore,
|
|
242
245
|
setTenant,
|
|
246
|
+
get_tenant_from_req
|
|
243
247
|
};
|
package/routes/view.js
CHANGED
|
@@ -20,6 +20,7 @@ const {
|
|
|
20
20
|
} = require("../routes/utils.js");
|
|
21
21
|
const { add_edit_bar } = require("../markup/admin.js");
|
|
22
22
|
const { InvalidConfiguration } = require("@saltcorn/data/utils");
|
|
23
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* @type {object}
|
|
@@ -44,8 +45,11 @@ router.get(
|
|
|
44
45
|
const query = { ...req.query };
|
|
45
46
|
const view = await View.findOne({ name: viewname });
|
|
46
47
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
48
|
+
const state = getState();
|
|
49
|
+
state.log(3, `Route /view/${viewname} user=${req.user?.id}`);
|
|
47
50
|
if (!view) {
|
|
48
51
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
52
|
+
state.log(2, `View ${viewname} not found`);
|
|
49
53
|
res.redirect("/");
|
|
50
54
|
return;
|
|
51
55
|
}
|
|
@@ -56,6 +60,7 @@ router.get(
|
|
|
56
60
|
!(await view.authorise_get({ query, req, ...view }))
|
|
57
61
|
) {
|
|
58
62
|
req.flash("danger", req.__("Not authorized"));
|
|
63
|
+
state.log(2, `View ${viewname} not authorized`);
|
|
59
64
|
res.redirect("/");
|
|
60
65
|
return;
|
|
61
66
|
}
|
|
@@ -123,13 +128,21 @@ router.post(
|
|
|
123
128
|
error_catcher(async (req, res) => {
|
|
124
129
|
const { viewname, route } = req.params;
|
|
125
130
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
131
|
+
const state = getState();
|
|
132
|
+
state.log(
|
|
133
|
+
3,
|
|
134
|
+
`Route /view/${viewname} viewroute ${route} user=${req.user?.id}`
|
|
135
|
+
);
|
|
126
136
|
|
|
127
137
|
const view = await View.findOne({ name: viewname });
|
|
128
138
|
if (!view) {
|
|
129
139
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
140
|
+
state.log(2, `View ${viewname} not found`);
|
|
130
141
|
res.redirect("/");
|
|
131
142
|
} else if (role > view.min_role) {
|
|
132
143
|
req.flash("danger", req.__("Not authorized"));
|
|
144
|
+
state.log(2, `View ${viewname} viewroute ${route} not authorized`);
|
|
145
|
+
|
|
133
146
|
res.redirect("/");
|
|
134
147
|
} else {
|
|
135
148
|
await view.runRoute(route, req.body, res, { res, req });
|
|
@@ -150,10 +163,12 @@ router.post(
|
|
|
150
163
|
const { viewname } = req.params;
|
|
151
164
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
152
165
|
const query = { ...req.query };
|
|
153
|
-
|
|
166
|
+
const state = getState();
|
|
167
|
+
state.log(3, `Route /view/${viewname} POST user=${req.user?.id}`);
|
|
154
168
|
const view = await View.findOne({ name: viewname });
|
|
155
169
|
if (!view) {
|
|
156
170
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
171
|
+
state.log(2, `View ${viewname} not found`);
|
|
157
172
|
res.redirect("/");
|
|
158
173
|
return;
|
|
159
174
|
}
|
|
@@ -164,6 +179,8 @@ router.post(
|
|
|
164
179
|
!(await view.authorise_post({ body: req.body, req, ...view }))
|
|
165
180
|
) {
|
|
166
181
|
req.flash("danger", req.__("Not authorized"));
|
|
182
|
+
state.log(2, `View ${viewname} POST not authorized`);
|
|
183
|
+
|
|
167
184
|
res.redirect("/");
|
|
168
185
|
} else if (!view.runPost) {
|
|
169
186
|
throw new InvalidConfiguration(
|
package/routes/viewedit.js
CHANGED
|
@@ -98,6 +98,13 @@ const view_dropdown = (view, req) =>
|
|
|
98
98
|
'<i class="far fa-copy"></i> ' + req.__("Duplicate"),
|
|
99
99
|
req
|
|
100
100
|
),
|
|
101
|
+
a(
|
|
102
|
+
{
|
|
103
|
+
class: "dropdown-item",
|
|
104
|
+
href: `javascript:ajax_modal('/admin/snapshot-restore/view/${view.name}')`,
|
|
105
|
+
},
|
|
106
|
+
'<i class="fas fa-undo-alt"></i> ' + req.__("Restore")
|
|
107
|
+
),
|
|
101
108
|
div({ class: "dropdown-divider" }),
|
|
102
109
|
post_dropdown_item(
|
|
103
110
|
`/viewedit/delete/${view.id}`,
|
|
@@ -355,7 +362,7 @@ router.get(
|
|
|
355
362
|
}
|
|
356
363
|
const tables = await Table.find_with_external();
|
|
357
364
|
const currentTable = tables.find(
|
|
358
|
-
(t) => t.id === viewrow.table_id || t.name === viewrow.exttable_name
|
|
365
|
+
(t) => (t.id && t.id === viewrow.table_id) || t.name === viewrow.exttable_name
|
|
359
366
|
);
|
|
360
367
|
viewrow.table_name = currentTable && currentTable.name;
|
|
361
368
|
if (viewrow.slug && currentTable) {
|
|
@@ -380,7 +387,12 @@ router.get(
|
|
|
380
387
|
{
|
|
381
388
|
type: "card",
|
|
382
389
|
class: "mt-0",
|
|
383
|
-
title: req.__(
|
|
390
|
+
title: req.__(
|
|
391
|
+
`%s view - %s on %s`,
|
|
392
|
+
viewname,
|
|
393
|
+
viewrow.viewtemplate,
|
|
394
|
+
viewrow.table_name
|
|
395
|
+
),
|
|
384
396
|
contents: renderForm(form, req.csrfToken()),
|
|
385
397
|
},
|
|
386
398
|
],
|
|
@@ -579,7 +591,7 @@ router.get(
|
|
|
579
591
|
isAdmin,
|
|
580
592
|
error_catcher(async (req, res) => {
|
|
581
593
|
const { name } = req.params;
|
|
582
|
-
|
|
594
|
+
const { step } = req.query;
|
|
583
595
|
const view = await View.findOne({ name });
|
|
584
596
|
if (!view) {
|
|
585
597
|
req.flash("error", `View not found: ${text(name)}`);
|
|
@@ -596,6 +608,7 @@ router.get(
|
|
|
596
608
|
table_id: view.table_id,
|
|
597
609
|
exttable_name: view.exttable_name,
|
|
598
610
|
viewname: name,
|
|
611
|
+
...(step ? { stepName: step } : {}),
|
|
599
612
|
},
|
|
600
613
|
req
|
|
601
614
|
);
|
package/serve.js
CHANGED
|
@@ -30,7 +30,7 @@ const { getConfig } = require("@saltcorn/data/models/config");
|
|
|
30
30
|
const { migrate } = require("@saltcorn/data/migrate");
|
|
31
31
|
const socketio = require("socket.io");
|
|
32
32
|
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
|
|
33
|
-
const { setTenant, getSessionStore } = require("./routes/utils");
|
|
33
|
+
const { setTenant, getSessionStore, get_tenant_from_req } = require("./routes/utils");
|
|
34
34
|
const passport = require("passport");
|
|
35
35
|
const { authenticate } = require("passport");
|
|
36
36
|
const View = require("@saltcorn/data/models/view");
|
|
@@ -44,6 +44,11 @@ const {
|
|
|
44
44
|
getAllTenants,
|
|
45
45
|
} = require("@saltcorn/admin-models/models/tenant");
|
|
46
46
|
const { auto_backup_now } = require("@saltcorn/admin-models/models/backup");
|
|
47
|
+
const Snapshot = require("@saltcorn/admin-models/models/snapshot");
|
|
48
|
+
|
|
49
|
+
const take_snapshot = async () => {
|
|
50
|
+
return await Snapshot.take_if_changed();
|
|
51
|
+
};
|
|
47
52
|
|
|
48
53
|
// helpful https://gist.github.com/jpoehls/2232358
|
|
49
54
|
/**
|
|
@@ -132,32 +137,33 @@ const workerDispatchMsg = ({ tenant, ...msg }) => {
|
|
|
132
137
|
*/
|
|
133
138
|
const onMessageFromWorker =
|
|
134
139
|
(masterState, { port, watchReaper, disableScheduler, pid }) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
140
|
+
(msg) => {
|
|
141
|
+
//console.log("worker msg", typeof msg, msg);
|
|
142
|
+
if (msg === "Start" && !masterState.started) {
|
|
143
|
+
masterState.started = true;
|
|
144
|
+
runScheduler({
|
|
145
|
+
port,
|
|
146
|
+
watchReaper,
|
|
147
|
+
disableScheduler,
|
|
148
|
+
eachTenant,
|
|
149
|
+
auto_backup_now,
|
|
150
|
+
take_snapshot,
|
|
151
|
+
});
|
|
152
|
+
require("./systemd")({ port });
|
|
153
|
+
return true;
|
|
154
|
+
} else if (msg === "RestartServer") {
|
|
155
|
+
process.exit(0);
|
|
156
|
+
return true;
|
|
157
|
+
} else if (msg.tenant || msg.createTenant) {
|
|
158
|
+
///ie from saltcorn
|
|
159
|
+
//broadcast
|
|
160
|
+
Object.entries(cluster.workers).forEach(([wpid, w]) => {
|
|
161
|
+
if (wpid !== pid) w.send(msg);
|
|
162
|
+
});
|
|
163
|
+
workerDispatchMsg(msg); //also master
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
161
167
|
|
|
162
168
|
module.exports =
|
|
163
169
|
/**
|
|
@@ -274,6 +280,7 @@ module.exports =
|
|
|
274
280
|
disableScheduler,
|
|
275
281
|
eachTenant,
|
|
276
282
|
auto_backup_now,
|
|
283
|
+
take_snapshot,
|
|
277
284
|
});
|
|
278
285
|
}
|
|
279
286
|
Trigger.emitEvent("Startup");
|
|
@@ -345,24 +352,33 @@ const setupSocket = (...servers) => {
|
|
|
345
352
|
io.attach(server);
|
|
346
353
|
}
|
|
347
354
|
|
|
348
|
-
io.use(wrap(setTenant));
|
|
355
|
+
//io.use(wrap(setTenant));
|
|
349
356
|
io.use(wrap(getSessionStore()));
|
|
350
357
|
io.use(wrap(passport.initialize()));
|
|
351
358
|
io.use(wrap(passport.authenticate(["jwt", "session"])));
|
|
352
359
|
if (process.send && !cluster.isMaster) io.adapter(createAdapter());
|
|
353
|
-
getState().setRoomEmitter((viewname, room_id, msg) => {
|
|
354
|
-
io.to(`${viewname}_${room_id}`).emit("message", msg);
|
|
360
|
+
getState().setRoomEmitter((tenant, viewname, room_id, msg) => {
|
|
361
|
+
io.to(`${tenant}_${viewname}_${room_id}`).emit("message", msg);
|
|
355
362
|
});
|
|
356
363
|
io.on("connection", (socket) => {
|
|
357
364
|
socket.on("join_room", ([viewname, room_id]) => {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.
|
|
362
|
-
.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
365
|
+
const ten = get_tenant_from_req(socket.request) || "public";
|
|
366
|
+
const f = () => {
|
|
367
|
+
try {
|
|
368
|
+
const view = View.findOne({ name: viewname });
|
|
369
|
+
if (view.viewtemplateObj.authorize_join) {
|
|
370
|
+
view.viewtemplateObj
|
|
371
|
+
.authorize_join(view.configuration, room_id, socket.request.user)
|
|
372
|
+
.then((authorized) => {
|
|
373
|
+
if (authorized) socket.join(`${ten}_${viewname}_${room_id}`);
|
|
374
|
+
});
|
|
375
|
+
} else socket.join(`${ten}_${viewname}_${room_id}`);
|
|
376
|
+
} catch (err) {
|
|
377
|
+
getState().log(1, `Socket join_room error: ${err.stack}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (ten && ten !== "public") db.runWithTenant(ten, f);
|
|
381
|
+
else f();
|
|
366
382
|
});
|
|
367
383
|
});
|
|
368
384
|
};
|
package/tests/api.test.js
CHANGED
|
@@ -84,6 +84,23 @@ describe("API read", () => {
|
|
|
84
84
|
)
|
|
85
85
|
);
|
|
86
86
|
});
|
|
87
|
+
it("should handle fkey args ", async () => {
|
|
88
|
+
const loginCookie = await getAdminLoginCookie();
|
|
89
|
+
const app = await getApp({ disableCsrf: true });
|
|
90
|
+
await request(app)
|
|
91
|
+
.get("/api/patients/?favbook=1")
|
|
92
|
+
.set("Cookie", loginCookie)
|
|
93
|
+
.expect(succeedJsonWith((rows) => rows.length == 1));
|
|
94
|
+
});
|
|
95
|
+
it("should handle fkey args with no value", async () => {
|
|
96
|
+
const loginCookie = await getAdminLoginCookie();
|
|
97
|
+
const app = await getApp({ disableCsrf: true });
|
|
98
|
+
await request(app)
|
|
99
|
+
.get("/api/patients/?favbook=")
|
|
100
|
+
.set("Cookie", loginCookie)
|
|
101
|
+
.expect(succeedJsonWith((rows) => rows.length == 0));
|
|
102
|
+
});
|
|
103
|
+
|
|
87
104
|
it("should get books for public with search and one field", async () => {
|
|
88
105
|
const app = await getApp({ disableCsrf: true });
|
|
89
106
|
await request(app)
|
package/tests/clientjs.test.js
CHANGED
|
@@ -34,8 +34,18 @@ test("updateQueryStringParameter", () => {
|
|
|
34
34
|
expect(removeQueryStringParameter("/foo?name=Bar&age=45", "age")).toBe(
|
|
35
35
|
"/foo?name=Bar"
|
|
36
36
|
);
|
|
37
|
+
expect(
|
|
38
|
+
updateQueryStringParameter("/foo", "publisher.publisher->name", "AK")
|
|
39
|
+
).toBe("/foo?publisher.publisher->name=AK");
|
|
40
|
+
expect(
|
|
41
|
+
updateQueryStringParameter(
|
|
42
|
+
"/foo?publisher.publisher->name=AB",
|
|
43
|
+
"publisher.publisher->name",
|
|
44
|
+
"AK"
|
|
45
|
+
)
|
|
46
|
+
).toBe("/foo?publisher.publisher->name=AK");
|
|
37
47
|
});
|
|
38
|
-
|
|
48
|
+
//publisher.publisher->name
|
|
39
49
|
test("updateQueryStringParameter hash", () => {
|
|
40
50
|
expect(updateQueryStringParameter("/foo#baz", "age", 43)).toBe(
|
|
41
51
|
"/foo?age=43#baz"
|