@saltcorn/server 0.9.4-beta.9 → 0.9.4
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/app.js +16 -1
- package/auth/admin.js +19 -3
- package/auth/routes.js +16 -4
- package/auth/testhelp.js +17 -1
- package/load_plugins.js +8 -2
- package/locales/en.json +24 -2
- package/markup/admin.js +22 -18
- package/package.json +10 -9
- package/public/dayjslocales/af.js +1 -0
- package/public/dayjslocales/am.js +1 -0
- package/public/dayjslocales/ar-dz.js +1 -0
- package/public/dayjslocales/ar-iq.js +1 -0
- package/public/dayjslocales/ar-kw.js +1 -0
- package/public/dayjslocales/ar-ly.js +1 -0
- package/public/dayjslocales/ar-ma.js +1 -0
- package/public/dayjslocales/ar-sa.js +1 -0
- package/public/dayjslocales/ar-tn.js +1 -0
- package/public/dayjslocales/ar.js +1 -0
- package/public/dayjslocales/az.js +1 -0
- package/public/dayjslocales/be.js +1 -0
- package/public/dayjslocales/bg.js +1 -0
- package/public/dayjslocales/bi.js +1 -0
- package/public/dayjslocales/bm.js +1 -0
- package/public/dayjslocales/bn-bd.js +1 -0
- package/public/dayjslocales/bn.js +1 -0
- package/public/dayjslocales/bo.js +1 -0
- package/public/dayjslocales/br.js +1 -0
- package/public/dayjslocales/bs.js +1 -0
- package/public/dayjslocales/ca.js +1 -0
- package/public/dayjslocales/cs.js +1 -0
- package/public/dayjslocales/cv.js +1 -0
- package/public/dayjslocales/cy.js +1 -0
- package/public/dayjslocales/da.js +1 -0
- package/public/dayjslocales/de-at.js +1 -0
- package/public/dayjslocales/de-ch.js +1 -0
- package/public/dayjslocales/de.js +1 -0
- package/public/dayjslocales/dv.js +1 -0
- package/public/dayjslocales/el.js +1 -0
- package/public/dayjslocales/en-au.js +1 -0
- package/public/dayjslocales/en-ca.js +1 -0
- package/public/dayjslocales/en-gb.js +1 -0
- package/public/dayjslocales/en-ie.js +1 -0
- package/public/dayjslocales/en-il.js +1 -0
- package/public/dayjslocales/en-in.js +1 -0
- package/public/dayjslocales/en-nz.js +1 -0
- package/public/dayjslocales/en-sg.js +1 -0
- package/public/dayjslocales/en-tt.js +1 -0
- package/public/dayjslocales/en.js +1 -0
- package/public/dayjslocales/eo.js +1 -0
- package/public/dayjslocales/es-do.js +1 -0
- package/public/dayjslocales/es-mx.js +1 -0
- package/public/dayjslocales/es-pr.js +1 -0
- package/public/dayjslocales/es-us.js +1 -0
- package/public/dayjslocales/es.js +1 -0
- package/public/dayjslocales/et.js +1 -0
- package/public/dayjslocales/eu.js +1 -0
- package/public/dayjslocales/fa.js +1 -0
- package/public/dayjslocales/fi.js +1 -0
- package/public/dayjslocales/fo.js +1 -0
- package/public/dayjslocales/fr-ca.js +1 -0
- package/public/dayjslocales/fr-ch.js +1 -0
- package/public/dayjslocales/fr.js +1 -0
- package/public/dayjslocales/fy.js +1 -0
- package/public/dayjslocales/ga.js +1 -0
- package/public/dayjslocales/gd.js +1 -0
- package/public/dayjslocales/gl.js +1 -0
- package/public/dayjslocales/gom-latn.js +1 -0
- package/public/dayjslocales/gu.js +1 -0
- package/public/dayjslocales/he.js +1 -0
- package/public/dayjslocales/hi.js +1 -0
- package/public/dayjslocales/hr.js +1 -0
- package/public/dayjslocales/ht.js +1 -0
- package/public/dayjslocales/hu.js +1 -0
- package/public/dayjslocales/hy-am.js +1 -0
- package/public/dayjslocales/id.js +1 -0
- package/public/dayjslocales/is.js +1 -0
- package/public/dayjslocales/it-ch.js +1 -0
- package/public/dayjslocales/it.js +1 -0
- package/public/dayjslocales/ja.js +1 -0
- package/public/dayjslocales/jv.js +1 -0
- package/public/dayjslocales/ka.js +1 -0
- package/public/dayjslocales/kk.js +1 -0
- package/public/dayjslocales/km.js +1 -0
- package/public/dayjslocales/kn.js +1 -0
- package/public/dayjslocales/ko.js +1 -0
- package/public/dayjslocales/ku.js +1 -0
- package/public/dayjslocales/ky.js +1 -0
- package/public/dayjslocales/lb.js +1 -0
- package/public/dayjslocales/lo.js +1 -0
- package/public/dayjslocales/lt.js +1 -0
- package/public/dayjslocales/lv.js +1 -0
- package/public/dayjslocales/me.js +1 -0
- package/public/dayjslocales/mi.js +1 -0
- package/public/dayjslocales/mk.js +1 -0
- package/public/dayjslocales/ml.js +1 -0
- package/public/dayjslocales/mn.js +1 -0
- package/public/dayjslocales/mr.js +1 -0
- package/public/dayjslocales/ms-my.js +1 -0
- package/public/dayjslocales/ms.js +1 -0
- package/public/dayjslocales/mt.js +1 -0
- package/public/dayjslocales/my.js +1 -0
- package/public/dayjslocales/nb.js +1 -0
- package/public/dayjslocales/ne.js +1 -0
- package/public/dayjslocales/nl-be.js +1 -0
- package/public/dayjslocales/nl.js +1 -0
- package/public/dayjslocales/nn.js +1 -0
- package/public/dayjslocales/oc-lnc.js +1 -0
- package/public/dayjslocales/pa-in.js +1 -0
- package/public/dayjslocales/pl.js +1 -0
- package/public/dayjslocales/pt-br.js +1 -0
- package/public/dayjslocales/pt.js +1 -0
- package/public/dayjslocales/rn.js +1 -0
- package/public/dayjslocales/ro.js +1 -0
- package/public/dayjslocales/ru.js +1 -0
- package/public/dayjslocales/rw.js +1 -0
- package/public/dayjslocales/sd.js +1 -0
- package/public/dayjslocales/se.js +1 -0
- package/public/dayjslocales/si.js +1 -0
- package/public/dayjslocales/sk.js +1 -0
- package/public/dayjslocales/sl.js +1 -0
- package/public/dayjslocales/sq.js +1 -0
- package/public/dayjslocales/sr-cyrl.js +1 -0
- package/public/dayjslocales/sr.js +1 -0
- package/public/dayjslocales/ss.js +1 -0
- package/public/dayjslocales/sv-fi.js +1 -0
- package/public/dayjslocales/sv.js +1 -0
- package/public/dayjslocales/sw.js +1 -0
- package/public/dayjslocales/ta.js +1 -0
- package/public/dayjslocales/te.js +1 -0
- package/public/dayjslocales/tet.js +1 -0
- package/public/dayjslocales/tg.js +1 -0
- package/public/dayjslocales/th.js +1 -0
- package/public/dayjslocales/tk.js +1 -0
- package/public/dayjslocales/tl-ph.js +1 -0
- package/public/dayjslocales/tlh.js +1 -0
- package/public/dayjslocales/tr.js +1 -0
- package/public/dayjslocales/tzl.js +1 -0
- package/public/dayjslocales/tzm-latn.js +1 -0
- package/public/dayjslocales/tzm.js +1 -0
- package/public/dayjslocales/ug-cn.js +1 -0
- package/public/dayjslocales/uk.js +1 -0
- package/public/dayjslocales/ur.js +1 -0
- package/public/dayjslocales/uz-latn.js +1 -0
- package/public/dayjslocales/uz.js +1 -0
- package/public/dayjslocales/vi.js +1 -0
- package/public/dayjslocales/x-pseudo.js +1 -0
- package/public/dayjslocales/yo.js +1 -0
- package/public/dayjslocales/zh-cn.js +1 -0
- package/public/dayjslocales/zh-hk.js +1 -0
- package/public/dayjslocales/zh-tw.js +1 -0
- package/public/dayjslocales/zh.js +1 -0
- package/public/gridedit.js +2 -2
- package/public/log_viewer_utils.js +156 -0
- package/public/saltcorn-builder.css +43 -2
- package/public/saltcorn-common.js +39 -29
- package/public/saltcorn.js +26 -8
- package/public/tabulator_bootstrap5.min.css +1 -0
- package/restart_watcher.js +1 -0
- package/routes/actions.js +158 -16
- package/routes/admin.js +83 -9
- package/routes/common_lists.js +40 -17
- package/routes/fields.js +18 -3
- package/routes/homepage.js +2 -1
- package/routes/page.js +30 -13
- package/routes/page_groupedit.js +104 -83
- package/routes/pageedit.js +4 -3
- package/routes/tables.js +34 -0
- package/routes/tag_entries.js +6 -1
- package/routes/tags.js +4 -0
- package/routes/utils.js +23 -2
- package/routes/view.js +5 -1
- package/routes/viewedit.js +9 -0
- package/serve.js +177 -10
- package/tests/admin.test.js +17 -11
- package/tests/api.test.js +27 -0
- package/tests/fields.test.js +132 -5
- package/tests/help.test.js +37 -0
- package/tests/page_group.test.js +1 -0
- package/tests/plugins.test.js +0 -12
- package/tests/table.test.js +1 -5
- package/tests/view.test.js +12 -0
- package/tests/viewedit.test.js +52 -8
- package/wrapper.js +9 -1
package/serve.js
CHANGED
|
@@ -112,6 +112,10 @@ const initMaster = async ({ disableMigrate }, useClusterAdaptor = true) => {
|
|
|
112
112
|
const tenants = await getAllTenants();
|
|
113
113
|
await init_multi_tenant(loadAllPlugins, disableMigrate, tenants);
|
|
114
114
|
}
|
|
115
|
+
eachTenant(async () => {
|
|
116
|
+
const state = getState();
|
|
117
|
+
if (state) await state.setConfig("joined_log_socket_ids", []);
|
|
118
|
+
});
|
|
115
119
|
if (useClusterAdaptor) setupPrimary();
|
|
116
120
|
};
|
|
117
121
|
|
|
@@ -283,7 +287,7 @@ module.exports =
|
|
|
283
287
|
})
|
|
284
288
|
.ready((glx) => {
|
|
285
289
|
const httpsServer = glx.httpsServer();
|
|
286
|
-
setupSocket(httpsServer);
|
|
290
|
+
setupSocket(appargs?.subdomainOffset, httpsServer);
|
|
287
291
|
httpsServer.setTimeout(timeout * 1000);
|
|
288
292
|
process.on("message", workerDispatchMsg);
|
|
289
293
|
glx.serveApp(app);
|
|
@@ -350,7 +354,7 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
350
354
|
// todo timeout to config
|
|
351
355
|
httpServer.setTimeout(timeout * 1000);
|
|
352
356
|
httpsServer.setTimeout(timeout * 1000);
|
|
353
|
-
setupSocket(httpServer, httpsServer);
|
|
357
|
+
setupSocket(appargs?.subdomainOffset, httpServer, httpsServer);
|
|
354
358
|
httpServer.listen(port, () => {
|
|
355
359
|
console.log("HTTP Server running on port 80");
|
|
356
360
|
});
|
|
@@ -363,7 +367,7 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
363
367
|
// server with http only
|
|
364
368
|
const http = require("http");
|
|
365
369
|
const httpServer = http.createServer(app);
|
|
366
|
-
setupSocket(httpServer);
|
|
370
|
+
setupSocket(appargs?.subdomainOffset, httpServer);
|
|
367
371
|
|
|
368
372
|
// todo timeout to config
|
|
369
373
|
// todo refer in doc to httpserver doc
|
|
@@ -380,7 +384,7 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
380
384
|
*
|
|
381
385
|
* @param {...*} servers
|
|
382
386
|
*/
|
|
383
|
-
const setupSocket = (...servers) => {
|
|
387
|
+
const setupSocket = (subdomainOffset, ...servers) => {
|
|
384
388
|
// https://socket.io/docs/v4/middlewares/
|
|
385
389
|
const wrap = (middleware) => (socket, next) =>
|
|
386
390
|
middleware(socket.request, {}, next);
|
|
@@ -390,15 +394,29 @@ const setupSocket = (...servers) => {
|
|
|
390
394
|
io.attach(server);
|
|
391
395
|
}
|
|
392
396
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
+
const passportInit = passport.initialize();
|
|
398
|
+
const sessionStore = getSessionStore();
|
|
399
|
+
const setupNamespace = (namespace) => {
|
|
400
|
+
//io.of(namespace).use(wrap(setTenant));
|
|
401
|
+
io.of(namespace).use(wrap(sessionStore));
|
|
402
|
+
io.of(namespace).use(wrap(passportInit));
|
|
403
|
+
io.of(namespace).use(wrap(passport.authenticate(["jwt", "session"])));
|
|
404
|
+
};
|
|
405
|
+
setupNamespace("/");
|
|
406
|
+
setupNamespace("/datastream");
|
|
397
407
|
if (process.send && !cluster.isMaster) io.adapter(createAdapter());
|
|
398
408
|
getState().setRoomEmitter((tenant, viewname, room_id, msg) => {
|
|
399
|
-
io.to(`${tenant}_${viewname}_${room_id}`).emit("message", msg);
|
|
409
|
+
io.of("/").to(`${tenant}_${viewname}_${room_id}`).emit("message", msg);
|
|
400
410
|
});
|
|
401
|
-
|
|
411
|
+
|
|
412
|
+
getState().setLogEmitter((tenant, level, msg) => {
|
|
413
|
+
const time = new Date().valueOf();
|
|
414
|
+
io.of("/")
|
|
415
|
+
.to(`_logs_${tenant}_`)
|
|
416
|
+
.emit("log_msg", { text: msg, time, level });
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
io.of("/").on("connection", (socket) => {
|
|
402
420
|
socket.on("join_room", ([viewname, room_id]) => {
|
|
403
421
|
const ten = get_tenant_from_req(socket.request) || "public";
|
|
404
422
|
const f = () => {
|
|
@@ -418,5 +436,154 @@ const setupSocket = (...servers) => {
|
|
|
418
436
|
if (ten && ten !== "public") db.runWithTenant(ten, f);
|
|
419
437
|
else f();
|
|
420
438
|
});
|
|
439
|
+
|
|
440
|
+
socket.on("join_log_room", async (callback) => {
|
|
441
|
+
const tenant =
|
|
442
|
+
get_tenant_from_req(socket.request, subdomainOffset) || "public";
|
|
443
|
+
const f = async () => {
|
|
444
|
+
try {
|
|
445
|
+
const user = socket.request.user;
|
|
446
|
+
if (!user || user.role_id !== 1) throw new Error("Not authorized");
|
|
447
|
+
else {
|
|
448
|
+
socket.join(`_logs_${tenant}_`);
|
|
449
|
+
const socketIds = await getState().getConfig(
|
|
450
|
+
"joined_log_socket_ids"
|
|
451
|
+
);
|
|
452
|
+
socketIds.push(socket.id);
|
|
453
|
+
await getState().setConfig("joined_log_socket_ids", [...socketIds]);
|
|
454
|
+
callback({ status: "ok" });
|
|
455
|
+
}
|
|
456
|
+
} catch (err) {
|
|
457
|
+
getState().log(1, `Socket join_logs stream: ${err.stack}`);
|
|
458
|
+
callback({ status: "error", msg: err.message || "unknown error" });
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
if (tenant && tenant !== "public") db.runWithTenant(tenant, f);
|
|
462
|
+
else await f();
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
socket.on("disconnect", async () => {
|
|
466
|
+
const tenant =
|
|
467
|
+
get_tenant_from_req(socket.request, subdomainOffset) || "public";
|
|
468
|
+
const f = async () => {
|
|
469
|
+
const socketIds = await getState().getConfig("joined_log_socket_ids");
|
|
470
|
+
const newSocketIds = socketIds.filter((id) => id !== socket.id);
|
|
471
|
+
await getState().setConfig("joined_log_socket_ids", newSocketIds);
|
|
472
|
+
};
|
|
473
|
+
if (tenant && tenant !== "public") db.runWithTenant(tenant, f);
|
|
474
|
+
else f();
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
io.of("/datastream").on("connection", (socket) => {
|
|
479
|
+
let dataStream = null;
|
|
480
|
+
let dataTarget = null;
|
|
481
|
+
socket.on(
|
|
482
|
+
"open_data_stream",
|
|
483
|
+
async ([viewName, id, fieldName, fieldView, targetOpts], callback) => {
|
|
484
|
+
const tenant =
|
|
485
|
+
get_tenant_from_req(socket.request, subdomainOffset) || "public";
|
|
486
|
+
const f = async () => {
|
|
487
|
+
try {
|
|
488
|
+
const user = socket.request.user;
|
|
489
|
+
const view = View.findOne({ name: viewName });
|
|
490
|
+
if (view.viewtemplateObj.authorizeDataStream) {
|
|
491
|
+
const authorized = await view.viewtemplateObj.authorizeDataStream(
|
|
492
|
+
view,
|
|
493
|
+
id,
|
|
494
|
+
fieldName,
|
|
495
|
+
user,
|
|
496
|
+
targetOpts
|
|
497
|
+
);
|
|
498
|
+
if (!authorized) throw new Error("Not authorized");
|
|
499
|
+
}
|
|
500
|
+
const { stream, target } = await view.openDataStream(
|
|
501
|
+
id,
|
|
502
|
+
fieldName,
|
|
503
|
+
fieldView,
|
|
504
|
+
user,
|
|
505
|
+
targetOpts
|
|
506
|
+
);
|
|
507
|
+
dataStream = stream;
|
|
508
|
+
dataTarget = target;
|
|
509
|
+
getState().log(
|
|
510
|
+
5,
|
|
511
|
+
`opened data stram to: ${JSON.stringify(dataTarget)}`
|
|
512
|
+
);
|
|
513
|
+
callback({ status: "ok", target });
|
|
514
|
+
} catch (err) {
|
|
515
|
+
getState().log(
|
|
516
|
+
1,
|
|
517
|
+
`Socket open_data_stream: ${err.message || "unknown error"}`
|
|
518
|
+
);
|
|
519
|
+
callback({ status: "error", msg: err.message || "unknown error" });
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
if (tenant && tenant !== "public") db.runWithTenant(tenant, f);
|
|
523
|
+
else f();
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
socket.on("write_to_stream", async (data, callback) => {
|
|
527
|
+
if (!dataStream) {
|
|
528
|
+
getState().log(1, "Socket write_to_stream: No stream available");
|
|
529
|
+
callback({ status: "error", msg: "No stream available" });
|
|
530
|
+
} else
|
|
531
|
+
dataStream.write(data, (err) => {
|
|
532
|
+
if (err) {
|
|
533
|
+
getState().log(1, "Socket write_to_stream: No stream available");
|
|
534
|
+
callback({ status: "error", msg: err.message || "unknown error" });
|
|
535
|
+
} else callback({ status: "ok" });
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
socket.on("close_data_stream", async (callback) => {
|
|
540
|
+
if (!dataStream) {
|
|
541
|
+
getState().log(1, "Socket close_data_stream: No stream available");
|
|
542
|
+
callback({ status: "error", msg: "No stream available" });
|
|
543
|
+
} else {
|
|
544
|
+
dataStream.close((err) => {
|
|
545
|
+
if (err) {
|
|
546
|
+
getState().log(
|
|
547
|
+
1,
|
|
548
|
+
`Socket close_data_stream: ${err.message || "unknown error"}`
|
|
549
|
+
);
|
|
550
|
+
callback({ status: "error", msg: err.message || "unknown error" });
|
|
551
|
+
} else {
|
|
552
|
+
getState().log(
|
|
553
|
+
5,
|
|
554
|
+
`closed data stram of: ${JSON.stringify(dataTarget)}`
|
|
555
|
+
);
|
|
556
|
+
callback({ status: "ok" });
|
|
557
|
+
dataStream = null;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
socket.on("disconnect", async () => {
|
|
564
|
+
const tenant =
|
|
565
|
+
get_tenant_from_req(socket.request, subdomainOffset) || "public";
|
|
566
|
+
const f = async () => {
|
|
567
|
+
if (dataStream)
|
|
568
|
+
dataStream.close((err) => {
|
|
569
|
+
if (err) {
|
|
570
|
+
getState().log(
|
|
571
|
+
1,
|
|
572
|
+
`Socket disconnect close_data_stream: ${
|
|
573
|
+
err.message || "unknown error"
|
|
574
|
+
}`
|
|
575
|
+
);
|
|
576
|
+
} else {
|
|
577
|
+
getState().log(
|
|
578
|
+
5,
|
|
579
|
+
`closed data stram of: ${JSON.stringify(dataTarget)}`
|
|
580
|
+
);
|
|
581
|
+
dataStream = null;
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
};
|
|
585
|
+
if (tenant && tenant !== "public") db.runWithTenant(tenant, f);
|
|
586
|
+
else f();
|
|
587
|
+
});
|
|
421
588
|
});
|
|
422
589
|
};
|
package/tests/admin.test.js
CHANGED
|
@@ -382,13 +382,13 @@ describe("actions", () => {
|
|
|
382
382
|
.send("table_id=2")
|
|
383
383
|
.send("name=mynewaction")
|
|
384
384
|
.send("when_trigger=Insert")
|
|
385
|
-
.expect(toRedirect("/actions/configure/
|
|
385
|
+
.expect(toRedirect("/actions/configure/3"));
|
|
386
386
|
});
|
|
387
387
|
it("show edit", async () => {
|
|
388
388
|
const app = await getApp({ disableCsrf: true });
|
|
389
389
|
const loginCookie = await getAdminLoginCookie();
|
|
390
390
|
await request(app)
|
|
391
|
-
.get("/actions/edit/
|
|
391
|
+
.get("/actions/edit/3")
|
|
392
392
|
.set("Cookie", loginCookie)
|
|
393
393
|
.expect(toInclude("Edit trigger"))
|
|
394
394
|
.expect(toInclude("run_js_code"));
|
|
@@ -397,7 +397,7 @@ describe("actions", () => {
|
|
|
397
397
|
const app = await getApp({ disableCsrf: true });
|
|
398
398
|
const loginCookie = await getAdminLoginCookie();
|
|
399
399
|
await request(app)
|
|
400
|
-
.get("/actions/configure/
|
|
400
|
+
.get("/actions/configure/3")
|
|
401
401
|
.set("Cookie", loginCookie)
|
|
402
402
|
.expect(toInclude("Configure trigger"))
|
|
403
403
|
.expect(toInclude("Code"));
|
|
@@ -406,7 +406,7 @@ describe("actions", () => {
|
|
|
406
406
|
const app = await getApp({ disableCsrf: true });
|
|
407
407
|
const loginCookie = await getAdminLoginCookie();
|
|
408
408
|
await request(app)
|
|
409
|
-
.post("/actions/configure/
|
|
409
|
+
.post("/actions/configure/3")
|
|
410
410
|
.set("Cookie", loginCookie)
|
|
411
411
|
.send("code=console.log(12345678)")
|
|
412
412
|
.expect(toRedirect("/actions/"));
|
|
@@ -415,7 +415,7 @@ describe("actions", () => {
|
|
|
415
415
|
const app = await getApp({ disableCsrf: true });
|
|
416
416
|
const loginCookie = await getAdminLoginCookie();
|
|
417
417
|
await request(app)
|
|
418
|
-
.get("/actions/testrun/
|
|
418
|
+
.get("/actions/testrun/3")
|
|
419
419
|
.set("Cookie", loginCookie)
|
|
420
420
|
.expect(toInclude("12345678"));
|
|
421
421
|
});
|
|
@@ -423,7 +423,7 @@ describe("actions", () => {
|
|
|
423
423
|
const app = await getApp({ disableCsrf: true });
|
|
424
424
|
const loginCookie = await getAdminLoginCookie();
|
|
425
425
|
await request(app)
|
|
426
|
-
.post("/actions/configure/
|
|
426
|
+
.post("/actions/configure/3")
|
|
427
427
|
.set("Cookie", loginCookie)
|
|
428
428
|
.send("code=1")
|
|
429
429
|
.expect(toRedirect("/actions/"));
|
|
@@ -432,7 +432,7 @@ describe("actions", () => {
|
|
|
432
432
|
const app = await getApp({ disableCsrf: true });
|
|
433
433
|
const loginCookie = await getAdminLoginCookie();
|
|
434
434
|
await request(app)
|
|
435
|
-
.get("/actions/testrun/
|
|
435
|
+
.get("/actions/testrun/2")
|
|
436
436
|
.set("Cookie", loginCookie)
|
|
437
437
|
.expect(toRedirect("/actions/"));
|
|
438
438
|
});
|
|
@@ -446,9 +446,9 @@ describe("actions", () => {
|
|
|
446
446
|
.send("action=run_js_code")
|
|
447
447
|
.send("when_trigger=API+call")
|
|
448
448
|
.send("min_role=1")
|
|
449
|
-
.expect(toRedirect("/actions/configure/
|
|
449
|
+
.expect(toRedirect("/actions/configure/4"));
|
|
450
450
|
await request(app)
|
|
451
|
-
.post("/actions/configure/
|
|
451
|
+
.post("/actions/configure/4")
|
|
452
452
|
.set("Cookie", loginCookie)
|
|
453
453
|
.send("code=return 27")
|
|
454
454
|
.expect(toRedirect("/actions/"));
|
|
@@ -463,7 +463,7 @@ describe("actions", () => {
|
|
|
463
463
|
const app = await getApp({ disableCsrf: true });
|
|
464
464
|
const loginCookie = await getAdminLoginCookie();
|
|
465
465
|
await request(app)
|
|
466
|
-
.post("/actions/delete/
|
|
466
|
+
.post("/actions/delete/3")
|
|
467
467
|
.set("Cookie", loginCookie)
|
|
468
468
|
.expect(toRedirect("/actions/"));
|
|
469
469
|
});
|
|
@@ -555,7 +555,7 @@ describe("diagram", () => {
|
|
|
555
555
|
});
|
|
556
556
|
|
|
557
557
|
/**
|
|
558
|
-
*
|
|
558
|
+
* Tags tests
|
|
559
559
|
*/
|
|
560
560
|
describe("tags", () => {
|
|
561
561
|
itShouldRedirectUnauthToLogin("/tag");
|
|
@@ -604,6 +604,12 @@ describe("tags", () => {
|
|
|
604
604
|
.expect(toRedirect("/tag"));
|
|
605
605
|
});
|
|
606
606
|
});
|
|
607
|
+
|
|
608
|
+
describe("server logs viewer", () => {
|
|
609
|
+
itShouldRedirectUnauthToLogin("/admin/dev/logs_viewer");
|
|
610
|
+
itShouldIncludeTextForAdmin("/admin/dev/logs_viewer", "Server logs");
|
|
611
|
+
});
|
|
612
|
+
|
|
607
613
|
/**
|
|
608
614
|
* Clear all tests
|
|
609
615
|
*/
|
package/tests/api.test.js
CHANGED
|
@@ -161,6 +161,33 @@ describe("API read", () => {
|
|
|
161
161
|
succeedJsonWith((rows) => rows.length == 2 && +rows[0]._versions === 0)
|
|
162
162
|
);
|
|
163
163
|
});
|
|
164
|
+
it("should get distinct authors for public", async () => {
|
|
165
|
+
const app = await getApp({ disableCsrf: true });
|
|
166
|
+
await request(app)
|
|
167
|
+
.get("/api/books/distinct/author")
|
|
168
|
+
.expect(
|
|
169
|
+
succeedJsonWith((vals) => {
|
|
170
|
+
return vals.length == 2 && vals.includes("Herman Melville");
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
it("should not allow public access to distinct patients", async () => {
|
|
175
|
+
const app = await getApp({ disableCsrf: true });
|
|
176
|
+
await request(app).get("/api/patients/distinct/name").expect(notAuthorized);
|
|
177
|
+
});
|
|
178
|
+
it("should allow staff access to distinct patients", async () => {
|
|
179
|
+
const loginCookie = await getStaffLoginCookie();
|
|
180
|
+
|
|
181
|
+
const app = await getApp({ disableCsrf: true });
|
|
182
|
+
await request(app)
|
|
183
|
+
.get("/api/patients/distinct/name")
|
|
184
|
+
.set("Cookie", loginCookie)
|
|
185
|
+
.expect(
|
|
186
|
+
succeedJsonWith(
|
|
187
|
+
(rows) => rows.length == 2 && rows.includes("Kirk Douglas")
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
});
|
|
164
191
|
});
|
|
165
192
|
describe("API post", () => {
|
|
166
193
|
it("should post books", async () => {
|
package/tests/fields.test.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
getAdminLoginCookie,
|
|
9
9
|
itShouldRedirectUnauthToLogin,
|
|
10
10
|
toInclude,
|
|
11
|
+
toBeTrue,
|
|
11
12
|
toNotInclude,
|
|
12
13
|
toRedirect,
|
|
13
14
|
resetToFixtures,
|
|
@@ -364,9 +365,12 @@ describe("Field Endpoints", () => {
|
|
|
364
365
|
await request(app)
|
|
365
366
|
.post("/field/show-calculated/books/pagesp1/show")
|
|
366
367
|
.set("Cookie", loginCookie)
|
|
367
|
-
.
|
|
368
|
+
.send({
|
|
369
|
+
id: 1,
|
|
370
|
+
})
|
|
371
|
+
.expect(toBeTrue((r) => +r.text > 500 && +r.text < 1500));
|
|
368
372
|
});
|
|
369
|
-
it("should show calculated with single joinfield", async () => {
|
|
373
|
+
it("should show calculated field with single joinfield", async () => {
|
|
370
374
|
const loginCookie = await getAdminLoginCookie();
|
|
371
375
|
const table = Table.findOne({ name: "patients" });
|
|
372
376
|
await Field.create({
|
|
@@ -382,9 +386,12 @@ describe("Field Endpoints", () => {
|
|
|
382
386
|
await request(app)
|
|
383
387
|
.post("/field/show-calculated/patients/pagesp1/show")
|
|
384
388
|
.set("Cookie", loginCookie)
|
|
385
|
-
.
|
|
389
|
+
.send({
|
|
390
|
+
id: 1,
|
|
391
|
+
})
|
|
392
|
+
.expect(toBeTrue((r) => +r.text > 2));
|
|
386
393
|
});
|
|
387
|
-
it("should show calculated with double joinfield", async () => {
|
|
394
|
+
it("should show calculated field with double joinfield", async () => {
|
|
388
395
|
const loginCookie = await getAdminLoginCookie();
|
|
389
396
|
const table = Table.findOne({ name: "readings" });
|
|
390
397
|
await Field.create({
|
|
@@ -400,7 +407,127 @@ describe("Field Endpoints", () => {
|
|
|
400
407
|
await request(app)
|
|
401
408
|
.post("/field/show-calculated/readings/pagesp1/show")
|
|
402
409
|
.set("Cookie", loginCookie)
|
|
403
|
-
.
|
|
410
|
+
.send({
|
|
411
|
+
id: 1,
|
|
412
|
+
})
|
|
413
|
+
.expect(toBeTrue((r) => +r.text > 2));
|
|
414
|
+
});
|
|
415
|
+
it("should show-calculated on join field value", async () => {
|
|
416
|
+
const loginCookie = await getAdminLoginCookie();
|
|
417
|
+
|
|
418
|
+
const app = await getApp({ disableCsrf: true });
|
|
419
|
+
|
|
420
|
+
await request(app)
|
|
421
|
+
.post("/field/show-calculated/books/publisher.name/as_text")
|
|
422
|
+
.set("Cookie", loginCookie)
|
|
423
|
+
.send({
|
|
424
|
+
publisher: 1,
|
|
425
|
+
})
|
|
426
|
+
.expect(toBeTrue((r) => r.text === "AK Press"));
|
|
427
|
+
});
|
|
428
|
+
it("should show-calculated on join field value", async () => {
|
|
429
|
+
const loginCookie = await getAdminLoginCookie();
|
|
430
|
+
|
|
431
|
+
const app = await getApp({ disableCsrf: true });
|
|
432
|
+
|
|
433
|
+
await request(app)
|
|
434
|
+
.post("/field/show-calculated/books/publisher.name/code")
|
|
435
|
+
.set("Cookie", loginCookie)
|
|
436
|
+
.send({
|
|
437
|
+
publisher: 1,
|
|
438
|
+
})
|
|
439
|
+
.expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
|
|
440
|
+
});
|
|
441
|
+
it("should show-calculated on join field value", async () => {
|
|
442
|
+
const loginCookie = await getAdminLoginCookie();
|
|
443
|
+
|
|
444
|
+
const app = await getApp({ disableCsrf: true });
|
|
445
|
+
|
|
446
|
+
await request(app)
|
|
447
|
+
.post("/field/show-calculated/books/publisher.name/code")
|
|
448
|
+
.set("Cookie", loginCookie)
|
|
449
|
+
.send({
|
|
450
|
+
publisher: 1,
|
|
451
|
+
})
|
|
452
|
+
.expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
|
|
453
|
+
});
|
|
454
|
+
it("should show-calculated on join field value", async () => {
|
|
455
|
+
const loginCookie = await getAdminLoginCookie();
|
|
456
|
+
|
|
457
|
+
const app = await getApp({ disableCsrf: true });
|
|
458
|
+
|
|
459
|
+
await request(app)
|
|
460
|
+
.post(
|
|
461
|
+
"/field/show-calculated/books/publisher.name/show_with_html?code=pub%3A%7B%7Bit.toLowerCase()%7D%7D"
|
|
462
|
+
)
|
|
463
|
+
.set("Cookie", loginCookie)
|
|
464
|
+
.send({
|
|
465
|
+
publisher: 1,
|
|
466
|
+
})
|
|
467
|
+
.expect(toBeTrue((r) => r.text === "pub:ak press"));
|
|
468
|
+
});
|
|
469
|
+
it("should show-calculated on double-join field value", async () => {
|
|
470
|
+
const loginCookie = await getAdminLoginCookie();
|
|
471
|
+
|
|
472
|
+
const app = await getApp({ disableCsrf: true });
|
|
473
|
+
|
|
474
|
+
await request(app)
|
|
475
|
+
.post("/field/show-calculated/patients/favbook.publisher.name/as_text")
|
|
476
|
+
.set("Cookie", loginCookie)
|
|
477
|
+
.send({
|
|
478
|
+
favbook: 2,
|
|
479
|
+
})
|
|
480
|
+
.expect(toBeTrue((r) => r.text === "AK Press"));
|
|
481
|
+
});
|
|
482
|
+
it("should not show unauth show-calculated on double-join field value", async () => {
|
|
483
|
+
const app = await getApp({ disableCsrf: true });
|
|
484
|
+
|
|
485
|
+
await request(app)
|
|
486
|
+
.post("/field/show-calculated/patients/favbook.publisher.name/as_text")
|
|
487
|
+
|
|
488
|
+
.send({
|
|
489
|
+
favbook: 2,
|
|
490
|
+
})
|
|
491
|
+
.expect(401);
|
|
492
|
+
});
|
|
493
|
+
it("should preview field", async () => {
|
|
494
|
+
const loginCookie = await getAdminLoginCookie();
|
|
495
|
+
|
|
496
|
+
const app = await getApp({ disableCsrf: true });
|
|
497
|
+
|
|
498
|
+
await request(app)
|
|
499
|
+
.post("/field/preview/books/author/code")
|
|
500
|
+
.set("Cookie", loginCookie)
|
|
501
|
+
.send({})
|
|
502
|
+
.expect(
|
|
503
|
+
toBeTrue((r) => r.text === "<pre><code>Herman Melville</code></pre>")
|
|
504
|
+
);
|
|
505
|
+
});
|
|
506
|
+
it("should preview joinfield", async () => {
|
|
507
|
+
const loginCookie = await getAdminLoginCookie();
|
|
508
|
+
|
|
509
|
+
const app = await getApp({ disableCsrf: true });
|
|
510
|
+
|
|
511
|
+
await request(app)
|
|
512
|
+
.post("/field/preview/books/publisher.name/code")
|
|
513
|
+
.set("Cookie", loginCookie)
|
|
514
|
+
.send({})
|
|
515
|
+
.expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
|
|
516
|
+
});
|
|
517
|
+
it("should preview joinfield with cfg", async () => {
|
|
518
|
+
const loginCookie = await getAdminLoginCookie();
|
|
519
|
+
|
|
520
|
+
const app = await getApp({ disableCsrf: true });
|
|
521
|
+
|
|
522
|
+
await request(app)
|
|
523
|
+
.post("/field/preview/books/publisher.name/show_with_html")
|
|
524
|
+
.set("Cookie", loginCookie)
|
|
525
|
+
.send({
|
|
526
|
+
configuration: {
|
|
527
|
+
code: "pub:{{it.toLowerCase()}}",
|
|
528
|
+
},
|
|
529
|
+
})
|
|
530
|
+
.expect(toBeTrue((r) => r.text === "pub:ak press"));
|
|
404
531
|
});
|
|
405
532
|
});
|
|
406
533
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const request = require("supertest");
|
|
2
|
+
const getApp = require("../app");
|
|
3
|
+
const {
|
|
4
|
+
toRedirect,
|
|
5
|
+
getAdminLoginCookie,
|
|
6
|
+
getStaffLoginCookie,
|
|
7
|
+
itShouldRedirectUnauthToLogin,
|
|
8
|
+
toInclude,
|
|
9
|
+
toNotInclude,
|
|
10
|
+
resetToFixtures,
|
|
11
|
+
succeedJsonWith,
|
|
12
|
+
} = require("../auth/testhelp");
|
|
13
|
+
const db = require("@saltcorn/data/db");
|
|
14
|
+
const View = require("@saltcorn/data/models/view");
|
|
15
|
+
const Table = require("@saltcorn/data/models/table");
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
await resetToFixtures();
|
|
19
|
+
});
|
|
20
|
+
afterAll(db.close);
|
|
21
|
+
|
|
22
|
+
jest.setTimeout(30000);
|
|
23
|
+
|
|
24
|
+
describe("Help topics", () => {
|
|
25
|
+
itShouldRedirectUnauthToLogin("/admin/help/View%20patterns");
|
|
26
|
+
|
|
27
|
+
it("should show view patterns help", async () => {
|
|
28
|
+
const loginCookie = await getAdminLoginCookie();
|
|
29
|
+
|
|
30
|
+
const app = await getApp({ disableCsrf: true });
|
|
31
|
+
await request(app)
|
|
32
|
+
.get("/admin/help/View patterns")
|
|
33
|
+
.set("Cookie", loginCookie)
|
|
34
|
+
.expect(toInclude("<title>Help: View patterns</title>"))
|
|
35
|
+
.expect(toInclude("The view pattern is a fundamental concept"));
|
|
36
|
+
});
|
|
37
|
+
});
|
package/tests/page_group.test.js
CHANGED
package/tests/plugins.test.js
CHANGED
|
@@ -280,19 +280,7 @@ describe("Pack clash detection", () => {
|
|
|
280
280
|
.set("Cookie", loginCookie)
|
|
281
281
|
.expect(toRedirect("/"));
|
|
282
282
|
});
|
|
283
|
-
it("should install issues", async () => {
|
|
284
|
-
const loginCookie = await getAdminLoginCookie();
|
|
285
283
|
|
|
286
|
-
const app = await getApp({ disableCsrf: true });
|
|
287
|
-
await request(app)
|
|
288
|
-
.post("/packs/install-named/Blog")
|
|
289
|
-
.set("Cookie", loginCookie)
|
|
290
|
-
.expect(toRedirect("/plugins"));
|
|
291
|
-
await request(app)
|
|
292
|
-
.get("/plugins")
|
|
293
|
-
.set("Cookie", loginCookie)
|
|
294
|
-
.expect(toInclude("Tables already exist: comments"));
|
|
295
|
-
});
|
|
296
284
|
it("should reset again", async () => {
|
|
297
285
|
await resetToFixtures();
|
|
298
286
|
});
|
package/tests/table.test.js
CHANGED
|
@@ -254,11 +254,7 @@ Gordon Kane, 218`;
|
|
|
254
254
|
await request(app)
|
|
255
255
|
.get("/table/")
|
|
256
256
|
.set("Cookie", loginCookie)
|
|
257
|
-
.expect(
|
|
258
|
-
toInclude(
|
|
259
|
-
"cannot drop table books because other objects depend on it"
|
|
260
|
-
)
|
|
261
|
-
);
|
|
257
|
+
.expect(toInclude("has views. Delete these first"));
|
|
262
258
|
});
|
|
263
259
|
});
|
|
264
260
|
describe("deletion to table with row ownership", () => {
|
package/tests/view.test.js
CHANGED
|
@@ -37,6 +37,18 @@ describe("view list endpoint", () => {
|
|
|
37
37
|
describe("nonexisting view", () => {
|
|
38
38
|
itShouldRedirectUnauthToLogin("/view/patlist", "/");
|
|
39
39
|
});
|
|
40
|
+
describe("preview view", () => {
|
|
41
|
+
it("should show previewview to auth", async () => {
|
|
42
|
+
const loginCookie = await getAdminLoginCookie();
|
|
43
|
+
|
|
44
|
+
const app = await getApp({ disableCsrf: true });
|
|
45
|
+
await request(app)
|
|
46
|
+
.post("/view/authorlist/preview")
|
|
47
|
+
.set("Cookie", loginCookie)
|
|
48
|
+
.expect(toInclude("Tolstoy"))
|
|
49
|
+
.expect(toNotInclude(">728<"));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
40
52
|
describe("view patients list endpoint", () => {
|
|
41
53
|
itShouldRedirectUnauthToLogin("/view/patientlist");
|
|
42
54
|
|