@saltcorn/server 0.8.6-beta.1 → 0.8.6-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/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.8.6-beta.1",
3
+ "version": "0.8.6-beta.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.6-beta.1",
10
- "@saltcorn/builder": "0.8.6-beta.1",
11
- "@saltcorn/data": "0.8.6-beta.1",
12
- "@saltcorn/admin-models": "0.8.6-beta.1",
13
- "@saltcorn/filemanager": "0.8.6-beta.1",
14
- "@saltcorn/markup": "0.8.6-beta.1",
15
- "@saltcorn/sbadmin2": "0.8.6-beta.1",
9
+ "@saltcorn/base-plugin": "0.8.6-beta.3",
10
+ "@saltcorn/builder": "0.8.6-beta.3",
11
+ "@saltcorn/data": "0.8.6-beta.3",
12
+ "@saltcorn/admin-models": "0.8.6-beta.3",
13
+ "@saltcorn/filemanager": "0.8.6-beta.3",
14
+ "@saltcorn/markup": "0.8.6-beta.3",
15
+ "@saltcorn/sbadmin2": "0.8.6-beta.3",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",f="month",h="quarter",c="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,f),s=n-i<0,u=e.clone().add(r+(s?-1:1),f);return+(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:f,y:c,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:h}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p=function(t){return t instanceof _},S=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},w=function(t,e){if(p(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},O=v;O.l=S,O.i=p,O.w=function(t,e){return w(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=S(t.locale,null,!0),this.parse(t)}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(O.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return O},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=w(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return w(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<w(t)},m.$g=function(t,e,n){return O.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!O.u(e)||e,h=O.p(t),l=function(t,e){var i=O.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return O.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(h){case c:return r?l(1,0):l(31,11);case f:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=O.p(t),h="set"+(this.$u?"UTC":""),l=(n={},n[a]=h+"Date",n[d]=h+"Date",n[f]=h+"Month",n[c]=h+"FullYear",n[u]=h+"Hours",n[s]=h+"Minutes",n[i]=h+"Seconds",n[r]=h+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===f||o===c){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[O.p(t)]()},m.add=function(r,h){var d,l=this;r=Number(r);var $=O.p(h),y=function(t){var e=w(l);return O.w(e.date(e.date()+Math.round(t*r)),l)};if($===f)return this.set(f,this.$M+r);if($===c)return this.set(c,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return O.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=O.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,f=n.months,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},c=function(t){return O.s(s%12||12,t,"0")},d=n.meridiem||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r},$={YY:String(this.$y).slice(-2),YYYY:this.$y,M:a+1,MM:O.s(a+1,2,"0"),MMM:h(n.monthsShort,a,f,3),MMMM:h(f,a),D:this.$D,DD:O.s(this.$D,2,"0"),d:String(this.$W),dd:h(n.weekdaysMin,this.$W,o,2),ddd:h(n.weekdaysShort,this.$W,o,3),dddd:o[this.$W],H:String(s),HH:O.s(s,2,"0"),h:c(1),hh:c(2),a:d(s,u,!0),A:d(s,u,!1),m:String(u),mm:O.s(u,2,"0"),s:String(this.$s),ss:O.s(this.$s,2,"0"),SSS:O.s(this.$ms,3,"0"),Z:i};return r.replace(y,(function(t,e){return e||$[t]||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=O.p(d),M=w(r),m=(M.utcOffset()-this.utcOffset())*e,v=this-M,g=O.m(this,M);return g=($={},$[c]=g/12,$[f]=g,$[h]=g/3,$[o]=(v-m)/6048e5,$[a]=(v-m)/864e5,$[u]=v/n,$[s]=v/e,$[i]=v/t,$)[y]||v,l?g:O.a(g)},m.daysInMonth=function(){return this.endOf(f).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=S(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return O.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),T=_.prototype;return w.prototype=T,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",f],["$y",c],["$D",d]].forEach((function(t){T[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),w.extend=function(t,e){return t.$i||(t(e,_,w),t.$i=!0),w},w.locale=S,w.isDayjs=p,w.unix=function(t){return w(1e3*t)},w.en=D[g],w.Ls=D,w.p={},w}));
@@ -257,6 +257,42 @@ function apply_showif() {
257
257
  },
258
258
  });
259
259
  });
260
+ const locale =
261
+ navigator.userLanguage ||
262
+ (navigator.languages &&
263
+ navigator.languages.length &&
264
+ navigator.languages[0]) ||
265
+ navigator.language ||
266
+ navigator.browserLanguage ||
267
+ navigator.systemLanguage ||
268
+ "en";
269
+ window.detected_locale = locale;
270
+ const parse = (s) => JSON.parse(decodeURIComponent(s));
271
+ $("time[locale-time-options]").each(function () {
272
+ var el = $(this);
273
+ var date = new Date(el.attr("datetime"));
274
+ const options = parse(el.attr("locale-time-options"));
275
+ el.text(date.toLocaleTimeString(locale, options));
276
+ });
277
+ $("time[locale-options]").each(function () {
278
+ var el = $(this);
279
+ var date = new Date(el.attr("datetime"));
280
+ const options = parse(el.attr("locale-options"));
281
+ el.text(date.toLocaleString(locale, options));
282
+ });
283
+ $("time[locale-date-options]").each(function () {
284
+ var el = $(this);
285
+ var date = new Date(el.attr("datetime"));
286
+ const options = parse(el.attr("locale-date-options"));
287
+ el.text(date.toLocaleDateString(locale, options));
288
+ });
289
+ $("time[locale-date-format]").each(function () {
290
+ var el = $(this);
291
+ var date = el.attr("datetime");
292
+ const format = parse(el.attr("locale-date-format"));
293
+ el.text(dayjs(date).format(format));
294
+ });
295
+
260
296
  _apply_showif_plugins.forEach((p) => p());
261
297
  }
262
298
 
@@ -568,35 +604,7 @@ function initialize_page() {
568
604
  });
569
605
  }, 100);
570
606
  });
571
- const locale =
572
- navigator.userLanguage ||
573
- (navigator.languages &&
574
- navigator.languages.length &&
575
- navigator.languages[0]) ||
576
- navigator.language ||
577
- navigator.browserLanguage ||
578
- navigator.systemLanguage ||
579
- "en";
580
- window.detected_locale = locale;
581
- const parse = (s) => JSON.parse(decodeURIComponent(s));
582
- $("time[locale-time-options]").each(function () {
583
- var el = $(this);
584
- var date = new Date(el.attr("datetime"));
585
- const options = parse(el.attr("locale-time-options"));
586
- el.text(date.toLocaleTimeString(locale, options));
587
- });
588
- $("time[locale-options]").each(function () {
589
- var el = $(this);
590
- var date = new Date(el.attr("datetime"));
591
- const options = parse(el.attr("locale-options"));
592
- el.text(date.toLocaleString(locale, options));
593
- });
594
- $("time[locale-date-options]").each(function () {
595
- var el = $(this);
596
- var date = new Date(el.attr("datetime"));
597
- const options = parse(el.attr("locale-date-options"));
598
- el.text(date.toLocaleDateString(locale, options));
599
- });
607
+
600
608
  if ($.fn.historyTabs && $.fn.tab)
601
609
  $('a[data-bs-toggle="tab"].deeplink').historyTabs();
602
610
  init_bs5_dropdowns();
@@ -1006,12 +1014,15 @@ function room_older(viewname, room_id, btn) {
1006
1014
  }
1007
1015
 
1008
1016
  function init_room(viewname, room_id) {
1009
- const socket = parent?.config?.server_path
1010
- ? io(parent.config.server_path, {
1011
- query: `jwt=${localStorage.getItem("auth_jwt")}`,
1012
- transports: ["websocket"],
1013
- })
1014
- : io({ transports: ["websocket"] });
1017
+ let socket = null;
1018
+ if (parent?.saltcorn?.data?.state) {
1019
+ const { server_path, jwt } =
1020
+ parent.saltcorn.data.state.getState().mobileConfig;
1021
+ socket = io(server_path, {
1022
+ query: `jwt=${jwt}`,
1023
+ transports: ["websocket"],
1024
+ });
1025
+ } else socket = io({ transports: ["websocket"] });
1015
1026
 
1016
1027
  socket.emit("join_room", [viewname, room_id]);
1017
1028
  socket.on("message", (msg) => {
@@ -599,11 +599,11 @@ function poll_mobile_build_finished(outDirName, pollCount, orginalBtnHtml) {
599
599
  data: { build_dir: outDirName },
600
600
  success: function (res) {
601
601
  if (!res.finished) {
602
- if (pollCount >= 50) {
602
+ if (pollCount >= 100) {
603
603
  removeSpinner("buildMobileAppBtnId", orginalBtnHtml);
604
604
  notifyAlert({
605
605
  type: "danger",
606
- text: "unable to get the build results",
606
+ text: "Unable to get the build results",
607
607
  });
608
608
  } else {
609
609
  setTimeout(() => {
package/routes/admin.js CHANGED
@@ -1564,6 +1564,27 @@ router.get(
1564
1564
  placeholder: getState().getConfig("base_url") || "",
1565
1565
  })
1566
1566
  )
1567
+ ),
1568
+ div(
1569
+ // TODO only for some tables?
1570
+ { class: "row pb-2" },
1571
+ div(
1572
+ { class: "col-sm-4" },
1573
+ input({
1574
+ type: "checkbox",
1575
+ id: "offlineModeBoxId",
1576
+ class: "form-check-input me-2",
1577
+ name: "allowOfflineMode",
1578
+ checked: true,
1579
+ }),
1580
+ label(
1581
+ {
1582
+ for: "offlineModeBoxId",
1583
+ class: "form-label",
1584
+ },
1585
+ req.__("Allow offline mode")
1586
+ )
1587
+ )
1567
1588
  )
1568
1589
  ),
1569
1590
  button(
@@ -1664,6 +1685,7 @@ router.post(
1664
1685
  useDocker,
1665
1686
  appFile,
1666
1687
  serverURL,
1688
+ allowOfflineMode,
1667
1689
  } = req.body;
1668
1690
  if (!androidPlatform && !iOSPlatform) {
1669
1691
  return res.json({
@@ -1711,6 +1733,7 @@ router.post(
1711
1733
  }
1712
1734
  if (appFile) spawnParams.push("-a", appFile);
1713
1735
  if (serverURL) spawnParams.push("-s", serverURL);
1736
+ if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
1714
1737
  if (
1715
1738
  db.is_it_multi_tenant() &&
1716
1739
  db.getTenantSchema() !== db.connectObj.default_schema
package/routes/api.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Table Data API handler
3
3
  * Allows to manipulate with saltcorn tables data.
4
4
  *
5
- * Attention! Currently you cannot insert / update users table via this api
5
+ * Attention! Currently, you cannot insert / update users table via this api
6
6
  * because users table has specific meaning in SC and
7
7
  * not all required (mandatory) fields of user available via this api.
8
8
  * For now this is platform limitation.
@@ -73,7 +73,7 @@ function accessAllowedRead(req, user, table, allow_ownership) {
73
73
  ? req.user.role_id
74
74
  : user && user.role_id
75
75
  ? user.role_id
76
- : 10;
76
+ : 100;
77
77
 
78
78
  return (
79
79
  role <= table.min_role_read ||
@@ -96,7 +96,7 @@ function accessAllowedWrite(req, user, table) {
96
96
  ? req.user.role_id
97
97
  : user && user.role_id
98
98
  ? user.role_id
99
- : 10;
99
+ : 100;
100
100
 
101
101
  return (
102
102
  role <= table.min_role_write ||
@@ -117,7 +117,7 @@ function accessAllowed(req, user, trigger) {
117
117
  ? req.user.role_id
118
118
  : user && user.role_id
119
119
  ? user.role_id
120
- : 10;
120
+ : 100;
121
121
 
122
122
  return role <= trigger.min_role;
123
123
  }
@@ -152,7 +152,7 @@ router.post(
152
152
  "jwt",
153
153
  { session: false },
154
154
  async function (err, user, info) {
155
- const role = user && user.id ? user.role_id : 10;
155
+ const role = user && user.id ? user.role_id : 100;
156
156
  if (
157
157
  role <= view.min_role ||
158
158
  (await view.authorise_get({ req, ...view })) // TODO set query to state
@@ -185,7 +185,9 @@ router.post(
185
185
  )(req, res, next);
186
186
  })
187
187
  );
188
-
188
+ /**
189
+ *
190
+ */
189
191
  router.get(
190
192
  "/:tableName/distinct/:fieldName",
191
193
  //passport.authenticate("api-bearer", { session: false }),
@@ -206,9 +208,7 @@ router.get(
206
208
  { session: false },
207
209
  async function (err, user, info) {
208
210
  if (accessAllowedRead(req, user, table)) {
209
- const field = (await table.getFields()).find(
210
- (f) => f.name === fieldName
211
- );
211
+ const field = table.getFields().find((f) => f.name === fieldName);
212
212
  if (!field) {
213
213
  res.status(404).json({ error: req.__("Not found") });
214
214
  return;
@@ -268,7 +268,7 @@ router.get(
268
268
  if (versioncount === "on") {
269
269
  const joinOpts = {
270
270
  orderBy: "id",
271
- forUser: req.user || user || { role_id: 10 },
271
+ forUser: req.user || user || { role_id: 100 },
272
272
  forPublic: !(req.user || user),
273
273
  aggregations: {
274
274
  _versions: {
@@ -281,7 +281,7 @@ router.get(
281
281
  };
282
282
  rows = await table.getJoinedRows(joinOpts);
283
283
  } else if (req_query && req_query !== {}) {
284
- const tbl_fields = await table.getFields();
284
+ const tbl_fields = table.getFields();
285
285
  readState(req_query, tbl_fields, req);
286
286
  const qstate = await stateFieldsToWhere({
287
287
  fields: tbl_fields,
@@ -335,7 +335,7 @@ router.post(
335
335
 
336
336
  if (!trigger) {
337
337
  getState().log(3, `API action ${actionname} not found`);
338
- res.status(400).json({ error: req.__("Not found") });
338
+ res.status(404).json({ error: req.__("Not found") });
339
339
  return;
340
340
  }
341
341
  await passport.authenticate(
@@ -350,6 +350,7 @@ router.post(
350
350
  body: req.body,
351
351
  row: req.body,
352
352
  req,
353
+ user: user || req.user,
353
354
  });
354
355
  res.json({ success: true, data: resp });
355
356
  } catch (e) {
@@ -387,8 +388,8 @@ router.post(
387
388
  async function (err, user, info) {
388
389
  if (accessAllowedWrite(req, user, table)) {
389
390
  const { _versions, ...row } = req.body;
390
- const fields = await table.getFields();
391
- readState(row, fields);
391
+ const fields = table.getFields();
392
+ readState(row, fields, req);
392
393
  let errors = [];
393
394
  let hasErrors = false;
394
395
  Object.keys(row).forEach((k) => {
@@ -425,7 +426,7 @@ router.post(
425
426
  }
426
427
  const ins_res = await table.tryInsertRow(
427
428
  row,
428
- req.user || user || { role_id: 10 }
429
+ req.user || user || { role_id: 100 }
429
430
  );
430
431
  if (ins_res.error) {
431
432
  getState().log(2, `API POST ${table.name} error: ${ins_res.error}`);
@@ -463,8 +464,8 @@ router.post(
463
464
  async function (err, user, info) {
464
465
  if (accessAllowedWrite(req, user, table)) {
465
466
  const { _versions, ...row } = req.body;
466
- const fields = await table.getFields();
467
- readState(row, fields);
467
+ const fields = table.getFields();
468
+ readState(row, fields, req);
468
469
  let errors = [];
469
470
  let hasErrors = false;
470
471
  for (const k of Object.keys(row)) {
@@ -501,7 +502,7 @@ router.post(
501
502
  const ins_res = await table.tryUpdateRow(
502
503
  row,
503
504
  id,
504
- user || req.user || { role_id: 10 }
505
+ user || req.user || { role_id: 100 }
505
506
  );
506
507
 
507
508
  if (ins_res.error) {
@@ -547,12 +548,12 @@ router.delete(
547
548
  //readState(row, fields);
548
549
  await table.deleteRows(
549
550
  { [pk_name]: row[pk_name] },
550
- user || req.user || { role_id: 10 }
551
+ user || req.user || { role_id: 100 }
551
552
  );
552
553
  } else
553
554
  await table.deleteRows(
554
555
  { id },
555
- user || req.user || { role_id: 10 }
556
+ user || req.user || { role_id: 100 }
556
557
  );
557
558
  res.json({ success: true });
558
559
  } catch (e) {
@@ -363,7 +363,10 @@ const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
363
363
  { label: req.__("Action"), key: "action" },
364
364
  {
365
365
  label: req.__("Table or Channel"),
366
- key: (r) => r.table_name || r.channel,
366
+ key: (r) =>
367
+ r.table_name
368
+ ? a({ href: `/table/${r.table_name}` }, r.table_name)
369
+ : r.channel,
367
370
  },
368
371
  {
369
372
  label: req.__("When"),
package/routes/delete.js CHANGED
@@ -34,14 +34,14 @@ router.post(
34
34
  const { redirect } = req.query;
35
35
  // todo check that works after where change
36
36
  const table = await Table.findOne({ name: tableName });
37
- const role = req.user && req.user.id ? req.user.role_id : 10;
37
+ const role = req.user && req.user.id ? req.user.role_id : 100;
38
38
  try {
39
39
  if (role <= table.min_role_write)
40
- await table.deleteRows({ id }, req.user || { role_id: 10 });
40
+ await table.deleteRows({ id }, req.user || { role_id: 100 });
41
41
  else if (table.ownership_field_id && req.user) {
42
42
  const row = await table.getRow({ id });
43
43
  if (row && table.is_owner(req.user, row))
44
- await table.deleteRows({ id }, req.user || { role_id: 10 });
44
+ await table.deleteRows({ id }, req.user || { role_id: 100 });
45
45
  else req.flash("error", req.__("Not authorized"));
46
46
  } else
47
47
  req.flash(
package/routes/edit.js CHANGED
@@ -41,7 +41,7 @@ router.post(
41
41
  await table.updateRow(
42
42
  { [field_name]: !row[field_name] },
43
43
  id,
44
- req.user || { role_id: 10 }
44
+ req.user || { role_id: 100 }
45
45
  );
46
46
 
47
47
  if (req.xhr) res.send("OK");
package/routes/fields.js CHANGED
@@ -83,6 +83,13 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
83
83
  if (!s || s === "") return req.__("Missing label");
84
84
  if (!id && existing_names.includes(Field.labelToName(s)))
85
85
  return req.__("Column %s already exists", s);
86
+ if (Field.labelToName(s) === "row")
87
+ return req.__("Not a valid field name");
88
+ try {
89
+ new Function(s, "return;");
90
+ } catch {
91
+ return req.__("Not a valid field name");
92
+ }
86
93
  },
87
94
  }),
88
95
  // description
@@ -717,7 +724,7 @@ router.post(
717
724
  error_catcher(async (req, res) => {
718
725
  const { tableName, fieldName, fieldview } = req.params;
719
726
  const table = await Table.findOne({ name: tableName });
720
- const role = req.user && req.user.id ? req.user.role_id : 10;
727
+ const role = req.user && req.user.id ? req.user.role_id : 100;
721
728
 
722
729
  const fields = await table.getFields();
723
730
  let row = { ...req.body };
package/routes/files.js CHANGED
@@ -130,7 +130,7 @@ router.get(
130
130
  router.get(
131
131
  "/download/*",
132
132
  error_catcher(async (req, res) => {
133
- const role = req.user && req.user.id ? req.user.role_id : 10;
133
+ const role = req.user && req.user.id ? req.user.role_id : 100;
134
134
  const user_id = req.user && req.user.id;
135
135
  const serve_path = req.params[0];
136
136
  const file = await File.findOne(serve_path);
@@ -155,7 +155,7 @@ router.post(
155
155
  isAdmin,
156
156
 
157
157
  error_catcher(async (req, res) => {
158
- const role = req.user && req.user.id ? req.user.role_id : 10;
158
+ const role = req.user && req.user.id ? req.user.role_id : 100;
159
159
  const user_id = req.user && req.user.id;
160
160
  const files = req.body.files;
161
161
  const location = req.body.location;
@@ -189,7 +189,7 @@ router.post(
189
189
  router.get(
190
190
  "/serve/*",
191
191
  error_catcher(async (req, res) => {
192
- const role = req.user && req.user.id ? req.user.role_id : 10;
192
+ const role = req.user && req.user.id ? req.user.role_id : 100;
193
193
  const user_id = req.user && req.user.id;
194
194
  const serve_path = req.params[0];
195
195
  //let file;
@@ -201,7 +201,7 @@ router.get(
201
201
  (role <= file.min_role_read || (user_id && user_id === file.user_id))
202
202
  ) {
203
203
  res.type(file.mimetype);
204
- const cacheability = file.min_role_read === 10 ? "public" : "private";
204
+ const cacheability = file.min_role_read === 100 ? "public" : "private";
205
205
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
206
206
  if (file.s3_store) s3storage.serveObject(file, res, false);
207
207
  else res.sendFile(file.location);
@@ -228,7 +228,7 @@ router.get(
228
228
  router.get(
229
229
  "/resize/:width_str/:height_str/*",
230
230
  error_catcher(async (req, res) => {
231
- const role = req.user && req.user.id ? req.user.role_id : 10;
231
+ const role = req.user && req.user.id ? req.user.role_id : 100;
232
232
  const user_id = req.user && req.user.id;
233
233
  const { width_str, height_str } = req.params;
234
234
  const serve_path = req.params[0];
@@ -240,7 +240,7 @@ router.get(
240
240
  (role <= file.min_role_read || (user_id && user_id === file.user_id))
241
241
  ) {
242
242
  res.type(file.mimetype);
243
- const cacheability = file.min_role_read === 10 ? "public" : "private";
243
+ const cacheability = file.min_role_read === 100 ? "public" : "private";
244
244
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
245
245
  //TODO s3
246
246
  if (file.s3_store) s3storage.serveObject(file, res, false);
@@ -386,7 +386,7 @@ router.post(
386
386
  let { folder } = req.body;
387
387
  let jsonResp = {};
388
388
  const min_role_upload = getState().getConfig("min_role_upload", 1);
389
- const role = req.user && req.user.id ? req.user.role_id : 10;
389
+ const role = req.user && req.user.id ? req.user.role_id : 100;
390
390
  let file_for_redirect;
391
391
  if (role > +min_role_upload) {
392
392
  if (!req.xhr) req.flash("warning", req.__("Not authorized"));
@@ -426,7 +426,7 @@ const welcome_page = async (req) => {
426
426
  * @returns {Promise<void>}
427
427
  */
428
428
  const no_views_logged_in = async (req, res) => {
429
- const role = req.user && req.user.id ? req.user.role_id : 10;
429
+ const role = req.user && req.user.id ? req.user.role_id : 100;
430
430
  if (role > 1 || req.user.tenant !== db.getTenantSchema())
431
431
  res.sendWrap(req.__("Hello"), req.__("Welcome to Saltcorn!"));
432
432
  else {
@@ -463,7 +463,7 @@ const no_views_logged_in = async (req, res) => {
463
463
  const get_config_response = async (role_id, res, req) => {
464
464
  const modernCfg = getState().getConfig("home_page_by_role", false);
465
465
  // predefined roles
466
- const legacy_role = { 10: "public", 8: "user", 4: "staff", 1: "admin" }[
466
+ const legacy_role = { 100: "public", 80: "user", 40: "staff", 1: "admin" }[
467
467
  role_id
468
468
  ];
469
469
  let homeCfg = modernCfg && modernCfg[role_id];
@@ -497,7 +497,7 @@ module.exports =
497
497
  */
498
498
  async (req, res) => {
499
499
  const isAuth = req.user && req.user.id;
500
- const role_id = req.user ? req.user.role_id : 10;
500
+ const role_id = req.user ? req.user.role_id : 100;
501
501
  const cfgResp = await get_config_response(role_id, res, req);
502
502
  if (cfgResp) return;
503
503
 
package/routes/index.js CHANGED
@@ -1,44 +1,5 @@
1
1
  /**
2
2
  * Index is Main Router of App
3
- * @category server
4
- * @module routes/index
5
- * @subcategory routes
6
- */
7
-
8
- /**
9
- * All files in the routes module.
10
- * @namespace routes_overview
11
- * @property {module:routes/actions} actions
12
- * @property {module:routes/admin} admin
13
- * @property {module:routes/api} api
14
- * @property {module:routes/config} config
15
- * @property {module:routes/crashlog} crashlog
16
- * @property {module:routes/delete} delete
17
- * @property {module:routes/edit} edit
18
- * @property {module:routes/eventlog} eventlog
19
- * @property {module:routes/events} events
20
- * @property {module:routes/fields} fields
21
- * @property {module:routes/files} files
22
- * @property {module:routes/homepage} homepage
23
- * @property {module:routes/infoarch} infoarch
24
- * @property {module:routes/library} library
25
- * @property {module:routes/list} list
26
- * @property {module:routes/menu} menu
27
- * @property {module:routes/packs} packs
28
- * @property {module:routes/page} page
29
- * @property {module:routes/pageedit} pageedit
30
- * @property {module:routes/plugins} plugins
31
- * @property {module:routes/scapi} scapi
32
- * @property {module:routes/search} search
33
- * @property {module:routes/settings} settings
34
- * @property {module:routes/tables} tables
35
- * @property {module:routes/tenant} tenant
36
- * @property {module:routes/utils} utils
37
- * @property {module:routes/view} view
38
- * @property {module:routes/viewedit} viewedit
39
- *
40
- * @category server
41
- * @subcategory routes
42
3
  */
43
4
 
44
5
  const table = require("./tables");
@@ -71,7 +32,8 @@ const useradmin = require("../auth/admin");
71
32
  const roleadmin = require("../auth/roleadmin");
72
33
  const tags = require("./tags");
73
34
  const tagentries = require("./tag_entries");
74
- const dataDiagram = require("./diagram");
35
+ const diagram = require("./diagram");
36
+ const sync = require("./sync");
75
37
 
76
38
  module.exports =
77
39
  /**
@@ -109,5 +71,6 @@ module.exports =
109
71
  app.use("/roleadmin", roleadmin);
110
72
  app.use("/tag", tags);
111
73
  app.use("/tag-entries", tagentries);
112
- app.use("/diagram", dataDiagram);
74
+ app.use("/diagram", diagram);
75
+ app.use("/sync", sync);
113
76
  };
package/routes/menu.js CHANGED
@@ -454,7 +454,7 @@ router.post(
454
454
  "/runaction/:name",
455
455
  error_catcher(async (req, res) => {
456
456
  const { name } = req.params;
457
- const role = (req.user || {}).role_id || 10;
457
+ const role = (req.user || {}).role_id || 100;
458
458
  const state = getState();
459
459
  const menu_items = state.getConfig("menu_items");
460
460
  let menu_item;
package/routes/page.js CHANGED
@@ -42,7 +42,7 @@ router.get(
42
42
  state.log(3, `Route /page/${pagename} user=${req.user?.id}`);
43
43
  const tic = state.logLevel >= 5 ? new Date() : null;
44
44
 
45
- const role = req.user && req.user.id ? req.user.role_id : 10;
45
+ const role = req.user && req.user.id ? req.user.role_id : 100;
46
46
  const db_page = await Page.findOne({ name: pagename });
47
47
  if (db_page && role <= db_page.min_role) {
48
48
  const contents = await db_page.run(req.query, { res, req });
@@ -104,7 +104,7 @@ router.post(
104
104
  "/:pagename/action/:rndid",
105
105
  error_catcher(async (req, res) => {
106
106
  const { pagename, rndid } = req.params;
107
- const role = req.user && req.user.id ? req.user.role_id : 10;
107
+ const role = req.user && req.user.id ? req.user.role_id : 100;
108
108
  const db_page = await Page.findOne({ name: pagename });
109
109
  if (db_page && role <= db_page.min_role) {
110
110
  let col;
@@ -387,7 +387,7 @@ router.get(
387
387
  isAdmin,
388
388
  error_catcher(async (req, res) => {
389
389
  const { pagename } = req.params;
390
- const page = await Page.findOne({ name: pagename });
390
+ const [page] = await Page.find({ name: pagename });
391
391
  if (!page) {
392
392
  req.flash("error", req.__(`Page %s not found`, pagename));
393
393
  res.redirect(`/pageedit`);
@@ -506,7 +506,7 @@ router.post(
506
506
  const valres = form.validate(req.body);
507
507
  if (valres.success) {
508
508
  const home_page_by_role =
509
- getState().getConfigCopy("home_page_by_role", []) || [];
509
+ getState().getConfigCopy("home_page_by_role", {}) || {};
510
510
  for (const role of roles) {
511
511
  home_page_by_role[role.id] = valres.success[role.role];
512
512
  }
package/routes/scapi.js CHANGED
@@ -49,7 +49,7 @@ function accessAllowedRead(req, user) {
49
49
  ? req.user.role_id
50
50
  : user && user.role_id
51
51
  ? user.role_id
52
- : 10;
52
+ : 100;
53
53
 
54
54
  if (role === 1) return true;
55
55
  return false;
package/routes/search.js CHANGED
@@ -165,7 +165,7 @@ const searchForm = () =>
165
165
  * @returns {Promise<void>}
166
166
  */
167
167
  const runSearch = async ({ q, _page, table }, req, res) => {
168
- const role = (req.user || {}).role_id || 10;
168
+ const role = (req.user || {}).role_id || 100;
169
169
  // globalSearch contains list of pairs: table, view
170
170
  const cfg = getState().getConfig("globalSearch");
171
171
  const page_size = getState().getConfig("search_page_size");
@@ -269,10 +269,9 @@ const runSearch = async ({ q, _page, table }, req, res) => {
269
269
  router.get(
270
270
  "/",
271
271
  error_catcher(async (req, res) => {
272
-
273
272
  const min_role = getState().getConfig("min_role_search");
274
- const role = (req.user || {}).role_id || 10;
275
- if(role>min_role){
273
+ const role = (req.user || {}).role_id || 100;
274
+ if (role > min_role) {
276
275
  res.redirect("/"); // silent redirect to home page
277
276
  return;
278
277
  }
@@ -283,7 +282,7 @@ router.get(
283
282
  const cfg = getState().getConfig("globalSearch");
284
283
 
285
284
  if (!cfg) {
286
- const role = (req.user || {}).role_id || 10;
285
+ const role = (req.user || {}).role_id || 100;
287
286
 
288
287
  req.flash("warning", req.__("Search not configured"));
289
288
  res.redirect(role === 1 ? "/search/config" : "/");