@saltcorn/server 0.7.3 → 0.7.4-beta.2

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/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
- (msg) => {
136
- //console.log("worker msg", typeof msg, msg);
137
- if (msg === "Start" && !masterState.started) {
138
- masterState.started = true;
139
- runScheduler({
140
- port,
141
- watchReaper,
142
- disableScheduler,
143
- eachTenant,
144
- auto_backup_now,
145
- });
146
- require("./systemd")({ port });
147
- return true;
148
- } else if (msg === "RestartServer") {
149
- process.exit(0);
150
- return true;
151
- } else if (msg.tenant || msg.createTenant) {
152
- ///ie from saltcorn
153
- //broadcast
154
- Object.entries(cluster.workers).forEach(([wpid, w]) => {
155
- if (wpid !== pid) w.send(msg);
156
- });
157
- workerDispatchMsg(msg); //also master
158
- return true;
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 view = View.findOne({ name: viewname });
359
- if (view.viewtemplateObj.authorize_join) {
360
- view.viewtemplateObj
361
- .authorize_join(view.configuration, room_id, socket.request.user)
362
- .then((authorized) => {
363
- if (authorized) socket.join(`${viewname}_${room_id}`);
364
- });
365
- } else socket.join(`${viewname}_${room_id}`);
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
  };
@@ -57,7 +57,7 @@ describe("admin page", () => {
57
57
  await request(app)
58
58
  .get("/settings")
59
59
  .set("Cookie", loginCookie)
60
- .expect(toInclude("Plugin and pack installation and control"));
60
+ .expect(toInclude("Module installation and control"));
61
61
  });
62
62
  it("show admin page", async () => {
63
63
  const app = await getApp({ disableCsrf: true });
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)
@@ -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"
@@ -30,7 +30,7 @@ describe("Plugin Endpoints", () => {
30
30
  await request(app)
31
31
  .get("/plugins")
32
32
  .set("Cookie", loginCookie)
33
- .expect(toInclude("Plugin and pack store"));
33
+ .expect(toInclude("Module store"));
34
34
  });
35
35
 
36
36
  it("should show new", async () => {
@@ -54,7 +54,7 @@ describe("viewedit new List", () => {
54
54
  await request(app)
55
55
  .get("/viewedit/new")
56
56
  .set("Cookie", loginCookie)
57
- .expect(toInclude("Template"));
57
+ .expect(toInclude("View pattern"));
58
58
  });
59
59
  it("submit new view", async () => {
60
60
  const loginCookie = await getAdminLoginCookie();
package/wrapper.js CHANGED
@@ -13,7 +13,7 @@ const renderLayout = require("@saltcorn/markup/layout");
13
13
  * @returns {T[]}
14
14
  */
15
15
  const getFlashes = (req) =>
16
- ["error", "success", "danger", "warning","information"]
16
+ ["error", "success", "danger", "warning", "information"]
17
17
  .map((type) => {
18
18
  return { type, msg: req.flash(type) };
19
19
  })
@@ -44,11 +44,13 @@ const get_extra_menu = (role, state, req) => {
44
44
  link:
45
45
  item.type === "Link"
46
46
  ? item.url
47
- : item.type === "View"
48
- ? `/view/${encodeURIComponent(item.viewname)}`
49
- : item.type === "Page"
50
- ? `/page/${encodeURIComponent(item.pagename)}`
51
- : undefined,
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,
52
54
  ...(item.subitems ? { subitems: transform(item.subitems) } : {}),
53
55
  }));
54
56
  return transform(cfg);
@@ -68,41 +70,41 @@ const get_menu = (req) => {
68
70
  const extra_menu = get_extra_menu(role, state, req);
69
71
  const authItems = isAuth
70
72
  ? [
71
- {
72
- label: req.__("User"),
73
- icon: "far fa-user",
74
- isUser: true,
75
- subitems: [
76
- { label: small((req.user.email || "").split("@")[0]) },
77
- {
78
- label: req.__("User Settings"),
79
- icon: "fas fa-user-cog",
73
+ {
74
+ label: req.__("User"),
75
+ icon: "far fa-user",
76
+ isUser: true,
77
+ subitems: [
78
+ { label: small((req.user.email || "").split("@")[0]) },
79
+ {
80
+ label: req.__("User Settings"),
81
+ icon: "fas fa-user-cog",
80
82
 
81
- link: "/auth/settings",
82
- },
83
- {
84
- link: "/auth/logout",
85
- icon: "fas fa-sign-out-alt",
86
- label: req.__("Logout"),
87
- },
88
- ],
89
- },
90
- ]
83
+ link: "/auth/settings",
84
+ },
85
+ {
86
+ link: "/auth/logout",
87
+ icon: "fas fa-sign-out-alt",
88
+ label: req.__("Logout"),
89
+ },
90
+ ],
91
+ },
92
+ ]
91
93
  : [
92
- ...(allow_signup
93
- ? [{ link: "/auth/signup", label: req.__("Sign up") }]
94
- : []),
95
- ...(login_menu
96
- ? [{ link: "/auth/login", label: req.__("Login") }]
97
- : []),
98
- ];
94
+ ...(allow_signup
95
+ ? [{ link: "/auth/signup", label: req.__("Sign up") }]
96
+ : []),
97
+ ...(login_menu
98
+ ? [{ link: "/auth/login", label: req.__("Login") }]
99
+ : []),
100
+ ];
99
101
  // const schema = db.getTenantSchema();
100
102
  // Admin role id (todo move to common constants)
101
103
  const isAdmin = role === 1;
102
- /*
103
- * Admin Menu items
104
- *
105
- */
104
+ /*
105
+ * Admin Menu items
106
+ *
107
+ */
106
108
  const adminItems = [
107
109
  { link: "/table", icon: "fas fa-table", label: req.__("Tables") },
108
110
  { link: "/viewedit", icon: "far fa-eye", label: req.__("Views") },
@@ -116,7 +118,7 @@ const get_menu = (req) => {
116
118
  icon: "fas fa-tools",
117
119
  label: req.__("About application"),
118
120
  },
119
- { link: "/plugins", icon: "fas fa-plug", label: req.__("Plugins") },
121
+ { link: "/plugins", icon: "fas fa-cubes", label: req.__("Modules") },
120
122
  {
121
123
  link: "/useradmin",
122
124
  icon: "fas fa-users-cog",
@@ -177,17 +179,17 @@ const get_headers = (req, version_tag, description, extras = []) => {
177
179
 
178
180
  const iconHeader = favicon
179
181
  ? [
180
- {
181
- headerTag: `<link rel="icon" type="image/png" href="/files/serve/${favicon}">`,
182
- },
183
- ]
182
+ {
183
+ headerTag: `<link rel="icon" type="image/png" href="/files/serve/${favicon}">`,
184
+ },
185
+ ]
184
186
  : [];
185
187
  const meta_description = description
186
188
  ? [
187
- {
188
- headerTag: `<meta name="description" content="${description}">`,
189
- },
190
- ]
189
+ {
190
+ headerTag: `<meta name="description" content="${description}">`,
191
+ },
192
+ ]
191
193
  : [];
192
194
  const stdHeaders = [
193
195
  {
@@ -228,12 +230,12 @@ const get_brand = (state) => {
228
230
  };
229
231
  };
230
232
  module.exports = (version_tag) =>
231
- /**
232
- *
233
- * @param req
234
- * @param res
235
- * @param next
236
- */
233
+ /**
234
+ *
235
+ * @param req
236
+ * @param res
237
+ * @param next
238
+ */
237
239
  function (req, res, next) {
238
240
  const role = (req.user || {}).role_id || 10;
239
241
 
@@ -349,7 +351,7 @@ const defaultRenderToHtml = (s, role) =>
349
351
  typeof s === "string"
350
352
  ? s
351
353
  : renderLayout({
352
- blockDispatch: {},
353
- role,
354
- layout: s,
355
- });
354
+ blockDispatch: {},
355
+ role,
356
+ layout: s,
357
+ });