@saltcorn/server 0.8.5-beta.3 → 0.8.5-beta.5
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 +23 -2
- package/auth/testhelp.js +15 -0
- package/locales/da.json +3 -1
- package/locales/en.json +26 -1
- package/locales/pl.json +17 -1
- package/markup/admin.js +15 -6
- package/package.json +8 -8
- package/public/saltcorn-common.js +30 -1
- package/public/saltcorn.css +27 -0
- package/public/saltcorn.js +3 -0
- package/public/serviceworker.js +1 -0
- package/public/vis-network.min.js +49 -0
- package/routes/admin.js +110 -219
- package/routes/api.js +66 -10
- package/routes/common_lists.js +1 -0
- package/routes/fields.js +11 -1
- package/routes/index.js +2 -0
- package/routes/list.js +8 -5
- package/routes/notifications.js +136 -0
- package/routes/plugins.js +47 -16
- package/routes/tables.js +160 -23
- package/routes/tenant.js +4 -0
- package/routes/utils.js +64 -1
- package/tests/api.test.js +2 -2
- package/tests/crud.test.js +53 -0
- package/tests/plugins.test.js +1 -1
- package/tests/table.test.js +17 -0
- package/wrapper.js +40 -3
package/routes/admin.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
error_catcher,
|
|
11
11
|
getGitRevision,
|
|
12
12
|
setTenant,
|
|
13
|
+
admin_config_route,
|
|
13
14
|
get_sys_info,
|
|
14
15
|
} = require("./utils.js");
|
|
15
16
|
const Table = require("@saltcorn/data/models/table");
|
|
@@ -84,7 +85,6 @@ const {
|
|
|
84
85
|
//send_files_page,
|
|
85
86
|
config_fields_form,
|
|
86
87
|
save_config_from_form,
|
|
87
|
-
flash_restart_if_required,
|
|
88
88
|
} = require("../markup/admin.js");
|
|
89
89
|
const packagejson = require("../package.json");
|
|
90
90
|
const Form = require("@saltcorn/data/models/form");
|
|
@@ -107,52 +107,6 @@ const Crash = require("@saltcorn/data/models/crash");
|
|
|
107
107
|
const router = new Router();
|
|
108
108
|
module.exports = router;
|
|
109
109
|
|
|
110
|
-
/**
|
|
111
|
-
* Site identity form
|
|
112
|
-
* @param {object} req -http request
|
|
113
|
-
* @returns {Promise<Form>} form
|
|
114
|
-
*/
|
|
115
|
-
const site_id_form = (req) =>
|
|
116
|
-
config_fields_form({
|
|
117
|
-
req,
|
|
118
|
-
field_names: [
|
|
119
|
-
"site_name",
|
|
120
|
-
"timezone",
|
|
121
|
-
"base_url",
|
|
122
|
-
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
123
|
-
{ section_header: "Logo image" },
|
|
124
|
-
"site_logo_id",
|
|
125
|
-
"favicon_id",
|
|
126
|
-
{ section_header: "Custom code" },
|
|
127
|
-
"page_custom_css",
|
|
128
|
-
"page_custom_html",
|
|
129
|
-
{ section_header: "Extension store" },
|
|
130
|
-
"plugins_store_endpoint",
|
|
131
|
-
"packs_store_endpoint",
|
|
132
|
-
],
|
|
133
|
-
action: "/admin",
|
|
134
|
-
submitLabel: req.__("Save"),
|
|
135
|
-
});
|
|
136
|
-
/**
|
|
137
|
-
* Email settings form
|
|
138
|
-
* @param {object} req request
|
|
139
|
-
* @returns {Promise<Form>} form
|
|
140
|
-
*/
|
|
141
|
-
const email_form = async (req) => {
|
|
142
|
-
return await config_fields_form({
|
|
143
|
-
req,
|
|
144
|
-
field_names: [
|
|
145
|
-
"smtp_host",
|
|
146
|
-
"smtp_username",
|
|
147
|
-
"smtp_password",
|
|
148
|
-
"smtp_port",
|
|
149
|
-
"smtp_secure",
|
|
150
|
-
"email_from",
|
|
151
|
-
],
|
|
152
|
-
action: "/admin/email",
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
|
|
156
110
|
const app_files_table = (files, buildDirName, req) =>
|
|
157
111
|
mkTable(
|
|
158
112
|
[
|
|
@@ -182,17 +136,27 @@ const app_files_table = (files, buildDirName, req) =>
|
|
|
182
136
|
files
|
|
183
137
|
);
|
|
184
138
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
139
|
+
admin_config_route({
|
|
140
|
+
router,
|
|
141
|
+
path: "/",
|
|
142
|
+
super_path: "/admin",
|
|
143
|
+
flash: "Site identity settings updated",
|
|
144
|
+
field_names: [
|
|
145
|
+
"site_name",
|
|
146
|
+
"timezone",
|
|
147
|
+
"base_url",
|
|
148
|
+
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
149
|
+
{ section_header: "Logo image" },
|
|
150
|
+
"site_logo_id",
|
|
151
|
+
"favicon_id",
|
|
152
|
+
{ section_header: "Custom code" },
|
|
153
|
+
"page_custom_css",
|
|
154
|
+
"page_custom_html",
|
|
155
|
+
{ section_header: "Extension store" },
|
|
156
|
+
"plugins_store_endpoint",
|
|
157
|
+
"packs_store_endpoint",
|
|
158
|
+
],
|
|
159
|
+
response(form, req, res) {
|
|
196
160
|
send_admin_page({
|
|
197
161
|
res,
|
|
198
162
|
req,
|
|
@@ -204,53 +168,23 @@ router.get(
|
|
|
204
168
|
contents: [renderForm(form, req.csrfToken())],
|
|
205
169
|
},
|
|
206
170
|
});
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @name post
|
|
212
|
-
* @function
|
|
213
|
-
* @memberof module:routes/admin~routes/adminRouter
|
|
214
|
-
*/
|
|
215
|
-
router.post(
|
|
216
|
-
"/",
|
|
217
|
-
isAdmin,
|
|
218
|
-
error_catcher(async (req, res) => {
|
|
219
|
-
const form = await site_id_form(req);
|
|
220
|
-
form.validate(req.body);
|
|
221
|
-
if (form.hasErrors) {
|
|
222
|
-
send_admin_page({
|
|
223
|
-
res,
|
|
224
|
-
req,
|
|
225
|
-
active_sub: "Site identity",
|
|
226
|
-
contents: {
|
|
227
|
-
type: "card",
|
|
228
|
-
title: req.__("Site identity settings"),
|
|
229
|
-
contents: [renderForm(form, req.csrfToken())],
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
} else {
|
|
233
|
-
flash_restart_if_required(form, req);
|
|
234
|
-
await save_config_from_form(form);
|
|
235
|
-
|
|
236
|
-
if (!req.xhr) {
|
|
237
|
-
req.flash("success", req.__("Site identity settings updated"));
|
|
238
|
-
res.redirect("/admin");
|
|
239
|
-
} else res.json({ success: "ok" });
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
);
|
|
171
|
+
},
|
|
172
|
+
});
|
|
243
173
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
174
|
+
admin_config_route({
|
|
175
|
+
router,
|
|
176
|
+
path: "/email",
|
|
177
|
+
super_path: "/admin",
|
|
178
|
+
flash: "Email settings updated",
|
|
179
|
+
field_names: [
|
|
180
|
+
"smtp_host",
|
|
181
|
+
"smtp_username",
|
|
182
|
+
"smtp_password",
|
|
183
|
+
"smtp_port",
|
|
184
|
+
"smtp_secure",
|
|
185
|
+
"email_from",
|
|
186
|
+
],
|
|
187
|
+
response(form, req, res) {
|
|
254
188
|
send_admin_page({
|
|
255
189
|
res,
|
|
256
190
|
req,
|
|
@@ -272,8 +206,8 @@ router.get(
|
|
|
272
206
|
],
|
|
273
207
|
},
|
|
274
208
|
});
|
|
275
|
-
}
|
|
276
|
-
);
|
|
209
|
+
},
|
|
210
|
+
});
|
|
277
211
|
|
|
278
212
|
/**
|
|
279
213
|
* @name get/send-test-email
|
|
@@ -305,38 +239,6 @@ router.get(
|
|
|
305
239
|
})
|
|
306
240
|
);
|
|
307
241
|
|
|
308
|
-
/**
|
|
309
|
-
* @name post/email
|
|
310
|
-
* @function
|
|
311
|
-
* @memberof module:routes/admin~routes/adminRouter
|
|
312
|
-
*/
|
|
313
|
-
router.post(
|
|
314
|
-
"/email",
|
|
315
|
-
isAdmin,
|
|
316
|
-
error_catcher(async (req, res) => {
|
|
317
|
-
const form = await email_form(req);
|
|
318
|
-
form.validate(req.body);
|
|
319
|
-
if (form.hasErrors) {
|
|
320
|
-
send_admin_page({
|
|
321
|
-
res,
|
|
322
|
-
req,
|
|
323
|
-
active_sub: "Email",
|
|
324
|
-
contents: {
|
|
325
|
-
type: "card",
|
|
326
|
-
title: req.__("Email settings"),
|
|
327
|
-
contents: [renderForm(form, req.csrfToken())],
|
|
328
|
-
},
|
|
329
|
-
});
|
|
330
|
-
} else {
|
|
331
|
-
await save_config_from_form(form);
|
|
332
|
-
if (!req.xhr) {
|
|
333
|
-
req.flash("success", req.__("Email settings updated"));
|
|
334
|
-
res.redirect("/admin/email");
|
|
335
|
-
} else res.json({ success: "ok" });
|
|
336
|
-
}
|
|
337
|
-
})
|
|
338
|
-
);
|
|
339
|
-
|
|
340
242
|
/**
|
|
341
243
|
* @name get/backup
|
|
342
244
|
* @function
|
|
@@ -1321,12 +1223,16 @@ router.get(
|
|
|
1321
1223
|
"/configuration-check",
|
|
1322
1224
|
isAdmin,
|
|
1323
1225
|
error_catcher(async (req, res) => {
|
|
1324
|
-
const
|
|
1226
|
+
const start = new Date();
|
|
1227
|
+
const filename = `${moment(start).format("YYYYMMDDHHmm")}.html`;
|
|
1325
1228
|
await File.new_folder("configuration_checks");
|
|
1326
1229
|
const go = async () => {
|
|
1327
1230
|
const { passes, errors, pass, warnings } = await runConfigurationCheck(
|
|
1328
1231
|
req
|
|
1329
1232
|
);
|
|
1233
|
+
const end = new Date();
|
|
1234
|
+
const secs = Math.round((end.getTime() - start.getTime()) / 1000);
|
|
1235
|
+
|
|
1330
1236
|
const mkError = (err) =>
|
|
1331
1237
|
div(
|
|
1332
1238
|
{ class: "alert alert-danger", role: "alert" },
|
|
@@ -1355,6 +1261,11 @@ router.get(
|
|
|
1355
1261
|
h3("Passes"),
|
|
1356
1262
|
|
|
1357
1263
|
pre(code(passes.join("\n")))
|
|
1264
|
+
) +
|
|
1265
|
+
p(
|
|
1266
|
+
`Configuration check completed in ${
|
|
1267
|
+
secs > 60 ? `${Math.floor(secs / 60)}m ${secs % 60}s` : secs + "s"
|
|
1268
|
+
}`
|
|
1358
1269
|
);
|
|
1359
1270
|
await File.from_contents(
|
|
1360
1271
|
filename,
|
|
@@ -1958,42 +1869,31 @@ router.post(
|
|
|
1958
1869
|
})
|
|
1959
1870
|
);
|
|
1960
1871
|
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1872
|
+
admin_config_route({
|
|
1873
|
+
router,
|
|
1874
|
+
path: "/dev",
|
|
1875
|
+
super_path: "/admin",
|
|
1876
|
+
flash: "Development mode settings updated",
|
|
1877
|
+
async get_form(req) {
|
|
1878
|
+
const tenants_set_npm_modules = getRootState().getConfig(
|
|
1879
|
+
"tenants_set_npm_modules",
|
|
1880
|
+
false
|
|
1881
|
+
);
|
|
1882
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
1971
1883
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
};
|
|
1986
|
-
/**
|
|
1987
|
-
* Developer Mode page
|
|
1988
|
-
* @name get/dev
|
|
1989
|
-
* @function
|
|
1990
|
-
* @memberof module:routes/admin~routes/adminRouter
|
|
1991
|
-
*/
|
|
1992
|
-
router.get(
|
|
1993
|
-
"/dev",
|
|
1994
|
-
isAdmin,
|
|
1995
|
-
error_catcher(async (req, res) => {
|
|
1996
|
-
const form = await dev_form(req);
|
|
1884
|
+
return await config_fields_form({
|
|
1885
|
+
req,
|
|
1886
|
+
field_names: [
|
|
1887
|
+
"development_mode",
|
|
1888
|
+
"log_sql",
|
|
1889
|
+
"log_client_errors",
|
|
1890
|
+
"log_level",
|
|
1891
|
+
...(isRoot || tenants_set_npm_modules ? ["npm_available_js_code"] : []),
|
|
1892
|
+
],
|
|
1893
|
+
action: "/admin/dev",
|
|
1894
|
+
});
|
|
1895
|
+
},
|
|
1896
|
+
response(form, req, res) {
|
|
1997
1897
|
send_admin_page({
|
|
1998
1898
|
res,
|
|
1999
1899
|
req,
|
|
@@ -2002,51 +1902,42 @@ router.get(
|
|
|
2002
1902
|
type: "card",
|
|
2003
1903
|
title: req.__("Development settings"),
|
|
2004
1904
|
titleAjaxIndicator: true,
|
|
2005
|
-
contents: [
|
|
2006
|
-
renderForm(form, req.csrfToken()) /*,
|
|
2007
|
-
a(
|
|
2008
|
-
{
|
|
2009
|
-
id: "testemail",
|
|
2010
|
-
href: "/admin/send-test-email",
|
|
2011
|
-
class: "btn btn-primary",
|
|
2012
|
-
},
|
|
2013
|
-
req.__("Send test email")
|
|
2014
|
-
),*/,
|
|
2015
|
-
],
|
|
1905
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
2016
1906
|
},
|
|
2017
1907
|
});
|
|
2018
|
-
}
|
|
2019
|
-
);
|
|
1908
|
+
},
|
|
1909
|
+
});
|
|
2020
1910
|
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1911
|
+
admin_config_route({
|
|
1912
|
+
router,
|
|
1913
|
+
path: "/notifications",
|
|
1914
|
+
super_path: "/admin",
|
|
1915
|
+
field_names: [
|
|
1916
|
+
"notification_in_menu",
|
|
1917
|
+
{ section_header: "Progressive Web Application" },
|
|
1918
|
+
"pwa_enabled",
|
|
1919
|
+
{ name: "pwa_display", showIf: { pwa_enabled: true } },
|
|
1920
|
+
{ name: "pwa_set_colors", showIf: { pwa_enabled: true } },
|
|
1921
|
+
{
|
|
1922
|
+
name: "pwa_theme_color",
|
|
1923
|
+
showIf: { pwa_enabled: true, pwa_set_colors: true },
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
name: "pwa_background_color",
|
|
1927
|
+
showIf: { pwa_enabled: true, pwa_set_colors: true },
|
|
1928
|
+
},
|
|
1929
|
+
],
|
|
1930
|
+
response(form, req, res) {
|
|
1931
|
+
send_admin_page({
|
|
1932
|
+
res,
|
|
1933
|
+
req,
|
|
1934
|
+
active_sub: "Notifications",
|
|
1935
|
+
contents: {
|
|
1936
|
+
type: "card",
|
|
1937
|
+
title: req.__("Notification settings"),
|
|
1938
|
+
titleAjaxIndicator: true,
|
|
1939
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
1940
|
+
},
|
|
1941
|
+
});
|
|
1942
|
+
},
|
|
1943
|
+
});
|
package/routes/api.js
CHANGED
|
@@ -31,6 +31,7 @@ const {
|
|
|
31
31
|
readState,
|
|
32
32
|
strictParseInt,
|
|
33
33
|
} = require("@saltcorn/data/plugin-helper");
|
|
34
|
+
const Crash = require("@saltcorn/data/models/crash");
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* @type {object}
|
|
@@ -66,7 +67,7 @@ const limitFields = (fields) => (r) => {
|
|
|
66
67
|
* @param {Table} table
|
|
67
68
|
* @returns {boolean}
|
|
68
69
|
*/
|
|
69
|
-
function accessAllowedRead(req, user, table) {
|
|
70
|
+
function accessAllowedRead(req, user, table, allow_ownership) {
|
|
70
71
|
const role =
|
|
71
72
|
req.user && req.user.id
|
|
72
73
|
? req.user.role_id
|
|
@@ -74,7 +75,12 @@ function accessAllowedRead(req, user, table) {
|
|
|
74
75
|
? user.role_id
|
|
75
76
|
: 10;
|
|
76
77
|
|
|
77
|
-
return
|
|
78
|
+
return (
|
|
79
|
+
role <= table.min_role_read ||
|
|
80
|
+
((req.user?.id || user?.id) &&
|
|
81
|
+
allow_ownership &&
|
|
82
|
+
(table.ownership_field_id || table.ownership_formula))
|
|
83
|
+
);
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
/**
|
|
@@ -92,7 +98,11 @@ function accessAllowedWrite(req, user, table) {
|
|
|
92
98
|
? user.role_id
|
|
93
99
|
: 10;
|
|
94
100
|
|
|
95
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
role <= table.min_role_write ||
|
|
103
|
+
((req.user?.id || user?.id) &&
|
|
104
|
+
(table.ownership_field_id || table.ownership_formula))
|
|
105
|
+
);
|
|
96
106
|
}
|
|
97
107
|
/**
|
|
98
108
|
* Check that user has right to trigger call
|
|
@@ -126,6 +136,7 @@ router.post(
|
|
|
126
136
|
const view = await View.findOne({ name: viewName });
|
|
127
137
|
const db = require("@saltcorn/data/db");
|
|
128
138
|
if (!view) {
|
|
139
|
+
getState().log(3, `API viewQuery ${view.name} not found`);
|
|
129
140
|
res.status(404).json({
|
|
130
141
|
error: req.__("View %s not found", viewName),
|
|
131
142
|
view: viewName,
|
|
@@ -152,6 +163,10 @@ router.post(
|
|
|
152
163
|
const resp = await queries[queryName](...args, true);
|
|
153
164
|
res.json({ success: resp, alerts: getFlashes(req) });
|
|
154
165
|
} else {
|
|
166
|
+
getState().log(
|
|
167
|
+
3,
|
|
168
|
+
`API viewQuery ${view.name} ${queryName} not found`
|
|
169
|
+
);
|
|
155
170
|
res.status(404).json({
|
|
156
171
|
error: req.__("Query %s not found", queryName),
|
|
157
172
|
view: viewName,
|
|
@@ -163,6 +178,7 @@ router.post(
|
|
|
163
178
|
});
|
|
164
179
|
}
|
|
165
180
|
} else {
|
|
181
|
+
getState().log(3, `API viewQuery ${view.name} not authorized`);
|
|
166
182
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
167
183
|
}
|
|
168
184
|
}
|
|
@@ -208,6 +224,10 @@ router.get(
|
|
|
208
224
|
}
|
|
209
225
|
res.json({ success: dvs });
|
|
210
226
|
} else {
|
|
227
|
+
getState().log(
|
|
228
|
+
3,
|
|
229
|
+
`API distinct ${table.name}.${fieldName} not authorized`
|
|
230
|
+
);
|
|
211
231
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
212
232
|
}
|
|
213
233
|
}
|
|
@@ -234,6 +254,7 @@ router.get(
|
|
|
234
254
|
: { name: tableName }
|
|
235
255
|
);
|
|
236
256
|
if (!table) {
|
|
257
|
+
getState().log(3, `API get ${tableName} table not found`);
|
|
237
258
|
res.status(404).json({ error: req.__("Not found") });
|
|
238
259
|
return;
|
|
239
260
|
}
|
|
@@ -242,11 +263,13 @@ router.get(
|
|
|
242
263
|
["api-bearer", "jwt"],
|
|
243
264
|
{ session: false },
|
|
244
265
|
async function (err, user, info) {
|
|
245
|
-
if (accessAllowedRead(req, user, table)) {
|
|
266
|
+
if (accessAllowedRead(req, user, table, true)) {
|
|
246
267
|
let rows;
|
|
247
268
|
if (versioncount === "on") {
|
|
248
269
|
const joinOpts = {
|
|
249
270
|
orderBy: "id",
|
|
271
|
+
forUser: req.user || user || { role_id: 10 },
|
|
272
|
+
forPublic: !(req.user || user),
|
|
250
273
|
aggregations: {
|
|
251
274
|
_versions: {
|
|
252
275
|
table: table.name + "__history",
|
|
@@ -266,12 +289,22 @@ router.get(
|
|
|
266
289
|
state: req_query,
|
|
267
290
|
table,
|
|
268
291
|
});
|
|
269
|
-
rows = await table.getRows(qstate
|
|
292
|
+
rows = await table.getRows(qstate, {
|
|
293
|
+
forPublic: !(req.user || user),
|
|
294
|
+
forUser: req.user || user,
|
|
295
|
+
});
|
|
270
296
|
} else {
|
|
271
|
-
rows = await table.getRows(
|
|
297
|
+
rows = await table.getRows(
|
|
298
|
+
{},
|
|
299
|
+
{
|
|
300
|
+
forPublic: !(req.user || user),
|
|
301
|
+
forUser: req.user || user,
|
|
302
|
+
}
|
|
303
|
+
);
|
|
272
304
|
}
|
|
273
305
|
res.json({ success: rows.map(limitFields(fields)) });
|
|
274
306
|
} else {
|
|
307
|
+
getState().log(3, `API get ${table.name} not authorized`);
|
|
275
308
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
276
309
|
}
|
|
277
310
|
}
|
|
@@ -301,6 +334,7 @@ router.post(
|
|
|
301
334
|
});
|
|
302
335
|
|
|
303
336
|
if (!trigger) {
|
|
337
|
+
getState().log(3, `API action ${actionname} not found`);
|
|
304
338
|
res.status(400).json({ error: req.__("Not found") });
|
|
305
339
|
return;
|
|
306
340
|
}
|
|
@@ -314,13 +348,16 @@ router.post(
|
|
|
314
348
|
const resp = await action.run({
|
|
315
349
|
configuration: trigger.configuration,
|
|
316
350
|
body: req.body,
|
|
351
|
+
row: req.body,
|
|
317
352
|
req,
|
|
318
353
|
});
|
|
319
354
|
res.json({ success: true, data: resp });
|
|
320
355
|
} catch (e) {
|
|
356
|
+
Crash.create(e, req);
|
|
321
357
|
res.status(400).json({ success: false, error: e.message });
|
|
322
358
|
}
|
|
323
359
|
} else {
|
|
360
|
+
getState().log(3, `API action ${actionname} not authorized`);
|
|
324
361
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
325
362
|
}
|
|
326
363
|
}
|
|
@@ -340,6 +377,7 @@ router.post(
|
|
|
340
377
|
const { tableName } = req.params;
|
|
341
378
|
const table = await Table.findOne({ name: tableName });
|
|
342
379
|
if (!table) {
|
|
380
|
+
getState().log(3, `API POST ${tableName} not found`);
|
|
343
381
|
res.status(404).json({ error: req.__("Not found") });
|
|
344
382
|
return;
|
|
345
383
|
}
|
|
@@ -378,6 +416,10 @@ router.post(
|
|
|
378
416
|
}
|
|
379
417
|
});
|
|
380
418
|
if (hasErrors) {
|
|
419
|
+
getState().log(
|
|
420
|
+
2,
|
|
421
|
+
`API POST ${table.name} error: ${errors.join(", ")}`
|
|
422
|
+
);
|
|
381
423
|
res.status(400).json({ error: errors.join(", ") });
|
|
382
424
|
return;
|
|
383
425
|
}
|
|
@@ -385,9 +427,12 @@ router.post(
|
|
|
385
427
|
row,
|
|
386
428
|
req.user || user || { role_id: 10 }
|
|
387
429
|
);
|
|
388
|
-
if (ins_res.error)
|
|
389
|
-
|
|
430
|
+
if (ins_res.error) {
|
|
431
|
+
getState().log(2, `API POST ${table.name} error: ${ins_res.error}`);
|
|
432
|
+
res.status(400).json(ins_res);
|
|
433
|
+
} else res.json(ins_res);
|
|
390
434
|
} else {
|
|
435
|
+
getState().log(3, `API POST ${table.name} not authorized`);
|
|
391
436
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
392
437
|
}
|
|
393
438
|
}
|
|
@@ -408,6 +453,7 @@ router.post(
|
|
|
408
453
|
const { tableName, id } = req.params;
|
|
409
454
|
const table = await Table.findOne({ name: tableName });
|
|
410
455
|
if (!table) {
|
|
456
|
+
getState().log(3, `API POST ${tableName} not found`);
|
|
411
457
|
res.status(404).json({ error: req.__("Not found") });
|
|
412
458
|
return;
|
|
413
459
|
}
|
|
@@ -445,6 +491,10 @@ router.post(
|
|
|
445
491
|
}
|
|
446
492
|
}
|
|
447
493
|
if (hasErrors) {
|
|
494
|
+
getState().log(
|
|
495
|
+
2,
|
|
496
|
+
`API POST ${table.name} error: ${errors.join(", ")}`
|
|
497
|
+
);
|
|
448
498
|
res.status(400).json({ error: errors.join(", ") });
|
|
449
499
|
return;
|
|
450
500
|
}
|
|
@@ -454,9 +504,12 @@ router.post(
|
|
|
454
504
|
user || req.user || { role_id: 10 }
|
|
455
505
|
);
|
|
456
506
|
|
|
457
|
-
if (ins_res.error)
|
|
458
|
-
|
|
507
|
+
if (ins_res.error) {
|
|
508
|
+
getState().log(2, `API POST ${table.name} error: ${ins_res.error}`);
|
|
509
|
+
res.status(400).json(ins_res);
|
|
510
|
+
} else res.json(ins_res);
|
|
459
511
|
} else {
|
|
512
|
+
getState().log(3, `API POST ${table.name} not authorized`);
|
|
460
513
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
461
514
|
}
|
|
462
515
|
}
|
|
@@ -477,6 +530,7 @@ router.delete(
|
|
|
477
530
|
const { tableName, id } = req.params;
|
|
478
531
|
const table = await Table.findOne({ name: tableName });
|
|
479
532
|
if (!table) {
|
|
533
|
+
getState().log(3, `API DELETE ${tableName} not found`);
|
|
480
534
|
res.status(404).json({ error: req.__("Not found") });
|
|
481
535
|
return;
|
|
482
536
|
}
|
|
@@ -502,9 +556,11 @@ router.delete(
|
|
|
502
556
|
);
|
|
503
557
|
res.json({ success: true });
|
|
504
558
|
} catch (e) {
|
|
559
|
+
getState().log(2, `API DELETE ${table.name} error: ${e.message}`);
|
|
505
560
|
res.status(400).json({ error: e.message });
|
|
506
561
|
}
|
|
507
562
|
} else {
|
|
563
|
+
getState().log(3, `API DELETE ${table.name} not authorized`);
|
|
508
564
|
res.status(401).json({ error: req.__("Not authorized") });
|
|
509
565
|
}
|
|
510
566
|
}
|
package/routes/common_lists.js
CHANGED
|
@@ -33,6 +33,7 @@ const tableBadges = (t, req) => {
|
|
|
33
33
|
if (t.ownership_field_id) s += badge("primary", req.__("Owned"));
|
|
34
34
|
if (t.versioned) s += badge("success", req.__("History"));
|
|
35
35
|
if (t.external) s += badge("info", req.__("External"));
|
|
36
|
+
if (t.provider_name) s += badge("success", t.provider_name);
|
|
36
37
|
return s;
|
|
37
38
|
};
|
|
38
39
|
|
package/routes/fields.js
CHANGED
|
@@ -398,9 +398,16 @@ const fieldFlow = (req) =>
|
|
|
398
398
|
];
|
|
399
399
|
const keyfields = orderedFields
|
|
400
400
|
.filter((f) => !f.calculated || f.stored)
|
|
401
|
+
.sort((a, b) =>
|
|
402
|
+
a.type?.name === "String" && b.type?.name !== "String"
|
|
403
|
+
? -1
|
|
404
|
+
: a.type?.name !== "String" && b.type?.name === "String"
|
|
405
|
+
? 1
|
|
406
|
+
: 0
|
|
407
|
+
)
|
|
401
408
|
.map((f) => ({
|
|
402
409
|
value: f.name,
|
|
403
|
-
label: f.label
|
|
410
|
+
label: `${f.label} [${f.type?.name || f.type}]`,
|
|
404
411
|
}));
|
|
405
412
|
const textfields = orderedFields
|
|
406
413
|
.filter(
|
|
@@ -412,6 +419,9 @@ const fieldFlow = (req) =>
|
|
|
412
419
|
new Field({
|
|
413
420
|
name: "summary_field",
|
|
414
421
|
label: req.__("Summary field"),
|
|
422
|
+
sublabel: req.__(
|
|
423
|
+
"The field that will be shown to the user when choosing a value"
|
|
424
|
+
),
|
|
415
425
|
input_type: "select",
|
|
416
426
|
options: keyfields,
|
|
417
427
|
}),
|
package/routes/index.js
CHANGED
|
@@ -64,6 +64,7 @@ const edit = require("./edit");
|
|
|
64
64
|
const config = require("./config");
|
|
65
65
|
const viewedit = require("./viewedit");
|
|
66
66
|
const crashlog = require("./crashlog");
|
|
67
|
+
const notifications = require("./notifications");
|
|
67
68
|
const del = require("./delete");
|
|
68
69
|
const auth = require("../auth/routes");
|
|
69
70
|
const useradmin = require("../auth/admin");
|
|
@@ -96,6 +97,7 @@ module.exports =
|
|
|
96
97
|
app.use("/actions", actions);
|
|
97
98
|
app.use("/eventlog", eventlog);
|
|
98
99
|
app.use("/library", library);
|
|
100
|
+
app.use("/notifications", notifications);
|
|
99
101
|
app.use("/site-structure", infoarch);
|
|
100
102
|
app.use("/search", search);
|
|
101
103
|
app.use("/admin", admin);
|