@saltcorn/server 0.6.1-beta.2 → 0.6.2-beta.1
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/index.js +15 -2
- package/index.js +17 -1
- package/markup/index.js +14 -1
- package/package.json +14 -8
- package/public/saltcorn.js +12 -0
- package/restart_watcher.js +146 -0
- package/routes/fields.js +13 -13
- package/routes/homepage.js +129 -106
- package/routes/index.js +37 -0
- package/routes/page.js +6 -1
- package/routes/view.js +4 -4
- package/serve.js +10 -2
- package/wrapper.js +3 -0
package/auth/index.js
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @category server
|
|
5
|
-
* @module auth/
|
|
5
|
+
* @module auth/index
|
|
6
6
|
* @subcategory auth
|
|
7
|
-
*/
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* All files in the auth module.
|
|
11
|
+
* @namespace auth_overview
|
|
12
|
+
* @property {module:auth/admin} admin
|
|
13
|
+
* @property {module:auth/resetpw} resetpw
|
|
14
|
+
* @property {module:auth/roleadmin} roleadmin
|
|
15
|
+
* @property {module:auth/routes} routes
|
|
16
|
+
* @property {module:auth/testhelp} testhelp
|
|
17
|
+
*
|
|
18
|
+
* @category server
|
|
19
|
+
* @subcategory auth
|
|
20
|
+
*/
|
package/index.js
CHANGED
|
@@ -3,4 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @category server
|
|
5
5
|
* @module server/index
|
|
6
|
-
*/
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* All files and sub-modules in the saltcorn-markup package.
|
|
10
|
+
* @namespace server_overview
|
|
11
|
+
* @property {module:auth/index~auth_overview} auth
|
|
12
|
+
* @property {module:markup/index~markup_overview} markup
|
|
13
|
+
* @property {module:routes/index~routes_overview} routes
|
|
14
|
+
*
|
|
15
|
+
* @property {module:app} app
|
|
16
|
+
* @property {module:errors} errors
|
|
17
|
+
* @property {module:load_plugins} load_plugins
|
|
18
|
+
* @property {module:serve} serve
|
|
19
|
+
* @property {module:systemd} systemd
|
|
20
|
+
* @property {module:wrapper} wrapper
|
|
21
|
+
* @category server
|
|
22
|
+
*/
|
package/markup/index.js
CHANGED
|
@@ -4,4 +4,17 @@
|
|
|
4
4
|
* @category server
|
|
5
5
|
* @module markup/index
|
|
6
6
|
* @subcategory markup
|
|
7
|
-
*/
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* All files in the auth module.
|
|
11
|
+
* @namespace markup_overview
|
|
12
|
+
* @property {module:markup/admin} admin
|
|
13
|
+
* @property {module:markup/blockly} blockly
|
|
14
|
+
* @property {module:markup/expression_blurb} expression_blurb
|
|
15
|
+
* @property {module:markup/forms} forms
|
|
16
|
+
* @property {module:markup/plugin-store} plugin-store
|
|
17
|
+
*
|
|
18
|
+
* @category server
|
|
19
|
+
* @subcategory markup
|
|
20
|
+
*/
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2-beta.1",
|
|
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.6.
|
|
10
|
-
"@saltcorn/builder": "0.6.
|
|
11
|
-
"@saltcorn/data": "0.6.
|
|
9
|
+
"@saltcorn/base-plugin": "0.6.2-beta.1",
|
|
10
|
+
"@saltcorn/builder": "0.6.2-beta.1",
|
|
11
|
+
"@saltcorn/data": "0.6.2-beta.1",
|
|
12
12
|
"greenlock-express": "^4.0.3",
|
|
13
|
-
"@saltcorn/markup": "0.6.
|
|
14
|
-
"@saltcorn/sbadmin2": "0.6.
|
|
13
|
+
"@saltcorn/markup": "0.6.2-beta.1",
|
|
14
|
+
"@saltcorn/sbadmin2": "0.6.2-beta.1",
|
|
15
15
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
16
16
|
"@socket.io/sticky": "^1.0.1",
|
|
17
17
|
"connect-flash": "^0.1.1",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"live-plugin-manager": "^0.16.0",
|
|
34
34
|
"moment": "^2.27.0",
|
|
35
35
|
"node-fetch": "2.6.2",
|
|
36
|
+
"node-watch": "^0.7.2",
|
|
36
37
|
"passport": "^0.4.1",
|
|
37
38
|
"passport-custom": "^1.1.1",
|
|
38
39
|
"passport-http-bearer": "^1.0.1",
|
|
@@ -51,7 +52,9 @@
|
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
54
|
"dev": "nodemon index.js",
|
|
54
|
-
"test": "jest --runInBand"
|
|
55
|
+
"test": "jest --runInBand",
|
|
56
|
+
"tsc": "echo \"Error: no TypeScript support yet\"",
|
|
57
|
+
"clean": "echo \"Error: no TypeScript support yet\""
|
|
55
58
|
},
|
|
56
59
|
"jest": {
|
|
57
60
|
"testEnvironment": "node",
|
|
@@ -62,7 +65,10 @@
|
|
|
62
65
|
"coveragePathIgnorePatterns": [
|
|
63
66
|
"/node_modules/",
|
|
64
67
|
"/plugin_packages/"
|
|
65
|
-
]
|
|
68
|
+
],
|
|
69
|
+
"moduleNameMapper": {
|
|
70
|
+
"@saltcorn/sqlite/(.*)": "@saltcorn/sqlite/dist/$1"
|
|
71
|
+
}
|
|
66
72
|
},
|
|
67
73
|
"publishConfig": {
|
|
68
74
|
"access": "public"
|
package/public/saltcorn.js
CHANGED
|
@@ -306,6 +306,18 @@ function select_id(id) {
|
|
|
306
306
|
function set_state_field(key, value) {
|
|
307
307
|
pjax_to(updateQueryStringParameter(window.location.href, key, value));
|
|
308
308
|
}
|
|
309
|
+
|
|
310
|
+
function check_state_field(that) {
|
|
311
|
+
const checked = that.checked;
|
|
312
|
+
const name = that.name;
|
|
313
|
+
const value = that.value;
|
|
314
|
+
var separator = window.location.href.indexOf("?") !== -1 ? "&" : "?";
|
|
315
|
+
let dest;
|
|
316
|
+
if (checked) dest = window.location.href + `${separator}${name}=${value}`;
|
|
317
|
+
else dest = window.location.href.replace(`${name}=${value}`, "");
|
|
318
|
+
pjax_to(dest.replace("&&", "&").replace("?&", "?"));
|
|
319
|
+
}
|
|
320
|
+
|
|
309
321
|
function set_state_fields(kvs) {
|
|
310
322
|
var newhref = window.location.href;
|
|
311
323
|
Object.entries(kvs).forEach((kv) => {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category server
|
|
3
|
+
* @module restart_watcher
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const { spawnSync } = require("child_process");
|
|
8
|
+
const watch = require("node-watch");
|
|
9
|
+
const Plugin = require("@saltcorn/data/models/plugin");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* packages that should trigger a server re-start
|
|
13
|
+
*/
|
|
14
|
+
const relevantPackages = [
|
|
15
|
+
"db-common",
|
|
16
|
+
"postgres",
|
|
17
|
+
"saltcorn-data",
|
|
18
|
+
"saltcorn-markup",
|
|
19
|
+
"server",
|
|
20
|
+
"sqlite",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* excluded directories or file name patterns
|
|
25
|
+
*/
|
|
26
|
+
const excludePatterns = [
|
|
27
|
+
/\/node_modules/,
|
|
28
|
+
/\/public/,
|
|
29
|
+
/\.git/,
|
|
30
|
+
/\.docs/,
|
|
31
|
+
/\.docs/,
|
|
32
|
+
/\migrations/,
|
|
33
|
+
/.*test.js/,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* get the root directory of the saltcorn project
|
|
38
|
+
* @returns {string} project root path
|
|
39
|
+
*/
|
|
40
|
+
const getProjectRoot = () => {
|
|
41
|
+
return path.normalize(`${__dirname}/../../`);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* get the packages directory of the saltcorn project
|
|
46
|
+
* @returns {string} packages path
|
|
47
|
+
*/
|
|
48
|
+
const getPackagesDirectory = () => {
|
|
49
|
+
return `${getProjectRoot()}/packages`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* get all package directories that should trigger a server re-start
|
|
54
|
+
* @returns {string[]} list of paths to relevant directories
|
|
55
|
+
*/
|
|
56
|
+
const getRelevantPackages = () => {
|
|
57
|
+
const packagesDir = getPackagesDirectory();
|
|
58
|
+
return relevantPackages.map((packageName) => `${packagesDir}/${packageName}`);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* get all plugin directories that should trigger a server re-start
|
|
63
|
+
* @returns {string[]} list of paths to relevant directories
|
|
64
|
+
*/
|
|
65
|
+
const getPluginDirectories = async () => {
|
|
66
|
+
const local_plugins = await Plugin.find({ source: "local" });
|
|
67
|
+
return local_plugins.map((p) => p.location);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const projectRoot = getProjectRoot();
|
|
71
|
+
|
|
72
|
+
const watchCfg = {
|
|
73
|
+
recursive: true,
|
|
74
|
+
filter(file, skip) {
|
|
75
|
+
for (const excludePattern of excludePatterns) {
|
|
76
|
+
if (excludePattern.test(file)) return skip;
|
|
77
|
+
}
|
|
78
|
+
return /(\.js|\.ts)$/.test(file);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let activeWatchers = [];
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* close all open file watchers
|
|
86
|
+
*/
|
|
87
|
+
const closeWatchers = () => {
|
|
88
|
+
for (const activeWatcher of activeWatchers) {
|
|
89
|
+
if (!activeWatcher.isClosed()) {
|
|
90
|
+
activeWatcher.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* register many file change listener and do re-starts on changes
|
|
97
|
+
* The listener calls process.exit() and assumes
|
|
98
|
+
* that pm2 does the actual re-start.
|
|
99
|
+
* @param {string[]} projectDirs package paths that should trigger re-starts.
|
|
100
|
+
* @param {string[]} pluginDirs plugin paths that should trigger re-starts.
|
|
101
|
+
*/
|
|
102
|
+
const listenForChanges = (projectDirs, pluginDirs) => {
|
|
103
|
+
// watch project dirs
|
|
104
|
+
for (const projectDir of projectDirs) {
|
|
105
|
+
activeWatchers.push(
|
|
106
|
+
watch(
|
|
107
|
+
projectDir,
|
|
108
|
+
watchCfg,
|
|
109
|
+
// event is either 'update' or 'remove'
|
|
110
|
+
(event, file) => {
|
|
111
|
+
console.log("'%s' changed \n re-starting now", file);
|
|
112
|
+
closeWatchers();
|
|
113
|
+
spawnSync("npm", ["run", "tsc"], {
|
|
114
|
+
stdio: "inherit",
|
|
115
|
+
cwd: projectRoot,
|
|
116
|
+
});
|
|
117
|
+
process.exit();
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
// watch plugin dirs
|
|
123
|
+
for (const pluginDir of pluginDirs) {
|
|
124
|
+
activeWatchers.push(
|
|
125
|
+
watch(
|
|
126
|
+
pluginDir,
|
|
127
|
+
watchCfg,
|
|
128
|
+
// event is either 'update' or 'remove'
|
|
129
|
+
(event, file) => {
|
|
130
|
+
console.log("'%s' changed \n re-starting now", file);
|
|
131
|
+
closeWatchers();
|
|
132
|
+
process.exit();
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
module.exports = {
|
|
140
|
+
listenForChanges,
|
|
141
|
+
getProjectRoot,
|
|
142
|
+
getPackagesDirectory,
|
|
143
|
+
getRelevantPackages,
|
|
144
|
+
getPluginDirectories,
|
|
145
|
+
closeWatchers,
|
|
146
|
+
};
|
package/routes/fields.js
CHANGED
|
@@ -38,11 +38,11 @@ const router = new Router();
|
|
|
38
38
|
module.exports = router;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* @param {object} req
|
|
42
|
-
* @param {*} fkey_opts
|
|
43
|
-
* @param {*} existing_names
|
|
44
|
-
* @param {*} id
|
|
45
|
-
* @param {*} hasData
|
|
41
|
+
* @param {object} req
|
|
42
|
+
* @param {*} fkey_opts
|
|
43
|
+
* @param {*} existing_names
|
|
44
|
+
* @param {*} id
|
|
45
|
+
* @param {*} hasData
|
|
46
46
|
* @returns {Promise<Form>}
|
|
47
47
|
*/
|
|
48
48
|
const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
@@ -137,7 +137,7 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
|
-
* @param {string} ctxType
|
|
140
|
+
* @param {string} ctxType
|
|
141
141
|
* @returns {object}
|
|
142
142
|
*/
|
|
143
143
|
const calcFieldType = (ctxType) =>
|
|
@@ -146,8 +146,8 @@ const calcFieldType = (ctxType) =>
|
|
|
146
146
|
: { type: ctxType };
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
|
-
* @param {*} attrs
|
|
150
|
-
* @param {object} req
|
|
149
|
+
* @param {*} attrs
|
|
150
|
+
* @param {object} req
|
|
151
151
|
* @returns {*}
|
|
152
152
|
*/
|
|
153
153
|
const translateAttributes = (attrs, req) =>
|
|
@@ -156,8 +156,8 @@ const translateAttributes = (attrs, req) =>
|
|
|
156
156
|
: attrs;
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
|
-
* @param {*} attr
|
|
160
|
-
* @param {*} req
|
|
159
|
+
* @param {*} attr
|
|
160
|
+
* @param {*} req
|
|
161
161
|
* @returns {object}
|
|
162
162
|
*/
|
|
163
163
|
const translateAttribute = (attr, req) => {
|
|
@@ -167,7 +167,7 @@ const translateAttribute = (attr, req) => {
|
|
|
167
167
|
};
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
|
-
* @param {*} req
|
|
170
|
+
* @param {*} req
|
|
171
171
|
* @returns {Workflow}
|
|
172
172
|
*/
|
|
173
173
|
const fieldFlow = (req) =>
|
|
@@ -697,12 +697,12 @@ router.post(
|
|
|
697
697
|
if (!fv && field.type === "Key" && fieldview === "select")
|
|
698
698
|
res.send("<select disabled></select>");
|
|
699
699
|
else if (!fv) res.send("");
|
|
700
|
-
else if (fv.isEdit)
|
|
700
|
+
else if (fv.isEdit || fv.isFilter)
|
|
701
701
|
res.send(
|
|
702
702
|
fv.run(
|
|
703
703
|
field.name,
|
|
704
704
|
undefined,
|
|
705
|
-
{ disabled: true, ...configuration },
|
|
705
|
+
{ disabled: true, ...configuration, ...(field.attributes || {}) },
|
|
706
706
|
"",
|
|
707
707
|
false,
|
|
708
708
|
field
|
package/routes/homepage.js
CHANGED
|
@@ -21,8 +21,8 @@ const Trigger = require("@saltcorn/data/models/trigger");
|
|
|
21
21
|
const { fileUploadForm } = require("../markup/forms");
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* @param {*} tables
|
|
25
|
-
* @param {object} req
|
|
24
|
+
* @param {*} tables
|
|
25
|
+
* @param {object} req
|
|
26
26
|
* @returns {Table}
|
|
27
27
|
*/
|
|
28
28
|
const tableTable = (tables, req) =>
|
|
@@ -38,8 +38,8 @@ const tableTable = (tables, req) =>
|
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* @param {*} tables
|
|
42
|
-
* @param {object} req
|
|
41
|
+
* @param {*} tables
|
|
42
|
+
* @param {object} req
|
|
43
43
|
* @returns {object}
|
|
44
44
|
*/
|
|
45
45
|
const tableCard = (tables, req) => ({
|
|
@@ -67,8 +67,8 @@ const tableCard = (tables, req) => ({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
* @param {*} views
|
|
71
|
-
* @param {object} req
|
|
70
|
+
* @param {*} views
|
|
71
|
+
* @param {object} req
|
|
72
72
|
* @returns {Table}
|
|
73
73
|
*/
|
|
74
74
|
const viewTable = (views, req) =>
|
|
@@ -88,8 +88,8 @@ const viewTable = (views, req) =>
|
|
|
88
88
|
);
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
* @param {*} views
|
|
92
|
-
* @param {object} req
|
|
91
|
+
* @param {*} views
|
|
92
|
+
* @param {object} req
|
|
93
93
|
* @returns {object}
|
|
94
94
|
*/
|
|
95
95
|
const viewCard = (views, req) => ({
|
|
@@ -119,8 +119,8 @@ const viewCard = (views, req) => ({
|
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* @param {*} pages
|
|
123
|
-
* @param {object} req
|
|
122
|
+
* @param {*} pages
|
|
123
|
+
* @param {object} req
|
|
124
124
|
* @returns {Table}
|
|
125
125
|
*/
|
|
126
126
|
const pageTable = (pages, req) =>
|
|
@@ -140,8 +140,8 @@ const pageTable = (pages, req) =>
|
|
|
140
140
|
);
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
|
-
* @param {*} pages
|
|
144
|
-
* @param {object} req
|
|
143
|
+
* @param {*} pages
|
|
144
|
+
* @param {object} req
|
|
145
145
|
* @returns {object}
|
|
146
146
|
*/
|
|
147
147
|
const pageCard = (pages, req) => ({
|
|
@@ -172,11 +172,11 @@ const pageCard = (pages, req) => ({
|
|
|
172
172
|
});
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
|
-
* @param {object} req
|
|
175
|
+
* @param {object} req
|
|
176
176
|
* @returns {Promise<div>}
|
|
177
177
|
*/
|
|
178
178
|
const filesTab = async (req) => {
|
|
179
|
-
const files = await File.find({}, { orderBy: "filename" });
|
|
179
|
+
const files = await File.find({}, { orderBy: "filename", cached: true });
|
|
180
180
|
return div(
|
|
181
181
|
files.length == 0
|
|
182
182
|
? p(req.__("No files"))
|
|
@@ -196,16 +196,10 @@ const filesTab = async (req) => {
|
|
|
196
196
|
};
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* @param {object} req
|
|
199
|
+
* @param {object} req
|
|
200
200
|
* @returns {Promise<div>}
|
|
201
201
|
*/
|
|
202
|
-
const usersTab = async (req) => {
|
|
203
|
-
const users = await User.find({}, { orderBy: "id" });
|
|
204
|
-
const roles = await User.get_roles();
|
|
205
|
-
var roleMap = {};
|
|
206
|
-
roles.forEach((r) => {
|
|
207
|
-
roleMap[r.id] = r.role;
|
|
208
|
-
});
|
|
202
|
+
const usersTab = async (req, users, roleMap) => {
|
|
209
203
|
return div(
|
|
210
204
|
mkTable(
|
|
211
205
|
[
|
|
@@ -219,20 +213,19 @@ const usersTab = async (req) => {
|
|
|
219
213
|
users
|
|
220
214
|
),
|
|
221
215
|
a(
|
|
222
|
-
{ href: `/useradmin/new`, class: "btn btn-secondary" },
|
|
216
|
+
{ href: `/useradmin/new`, class: "btn btn-secondary my-3" },
|
|
223
217
|
req.__("Create user")
|
|
224
218
|
)
|
|
225
219
|
);
|
|
226
220
|
};
|
|
227
221
|
|
|
228
222
|
/**
|
|
229
|
-
* @param {object} req
|
|
223
|
+
* @param {object} req
|
|
230
224
|
* @returns {Promise<div>}
|
|
231
225
|
*/
|
|
232
|
-
const actionsTab = async (req) => {
|
|
233
|
-
const triggers = await Trigger.findAllWithTableName();
|
|
234
|
-
|
|
226
|
+
const actionsTab = async (req, triggers) => {
|
|
235
227
|
return div(
|
|
228
|
+
{ class: "pb-3" },
|
|
236
229
|
triggers.length <= 1 &&
|
|
237
230
|
p(
|
|
238
231
|
{ class: "mt-2 pr-2" },
|
|
@@ -259,14 +252,73 @@ const actionsTab = async (req) => {
|
|
|
259
252
|
triggers
|
|
260
253
|
),
|
|
261
254
|
a(
|
|
262
|
-
{ href: "/actions/new", class: "btn btn-secondary
|
|
255
|
+
{ href: "/actions/new", class: "btn btn-secondary my-3" },
|
|
263
256
|
req.__("Add trigger")
|
|
264
257
|
)
|
|
265
258
|
);
|
|
266
259
|
};
|
|
260
|
+
const packTab = (req, packlist) =>
|
|
261
|
+
div(
|
|
262
|
+
{ class: "pb-3 pt-2 pr-4" },
|
|
263
|
+
p(req.__("Instead of building, get up and running in no time with packs")),
|
|
264
|
+
p(
|
|
265
|
+
{ class: "font-italic" },
|
|
266
|
+
req.__(
|
|
267
|
+
"Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
|
|
268
|
+
)
|
|
269
|
+
),
|
|
270
|
+
mkTable(
|
|
271
|
+
[
|
|
272
|
+
{ label: req.__("Name"), key: "name" },
|
|
273
|
+
{
|
|
274
|
+
label: req.__("Description"),
|
|
275
|
+
key: "description",
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
packlist,
|
|
279
|
+
{ noHeader: true }
|
|
280
|
+
),
|
|
281
|
+
a(
|
|
282
|
+
{ href: `/plugins?set=packs`, class: "btn btn-primary" },
|
|
283
|
+
req.__("Go to pack store »")
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const helpCard = (req) =>
|
|
288
|
+
div(
|
|
289
|
+
{ class: "pb-3 pt-2 pr-4" },
|
|
290
|
+
p(req.__("Confused?")),
|
|
291
|
+
p(
|
|
292
|
+
req.__(
|
|
293
|
+
"The Wiki contains the documentation and tutorials on installing and using Saltcorn"
|
|
294
|
+
)
|
|
295
|
+
),
|
|
296
|
+
a(
|
|
297
|
+
{
|
|
298
|
+
href: `https://wiki.saltcorn.com/`,
|
|
299
|
+
class: "btn btn-primary",
|
|
300
|
+
},
|
|
301
|
+
req.__("Go to Wiki »")
|
|
302
|
+
),
|
|
303
|
+
p(req.__("The YouTube channel has some video tutorials")),
|
|
304
|
+
a(
|
|
305
|
+
{
|
|
306
|
+
href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
|
|
307
|
+
class: "btn btn-secondary",
|
|
308
|
+
},
|
|
309
|
+
req.__("Go to YouTube »")
|
|
310
|
+
),
|
|
311
|
+
div(
|
|
312
|
+
{ class: "mt-3" },
|
|
313
|
+
a(
|
|
314
|
+
{ href: `https://blog.saltcorn.com/` },
|
|
315
|
+
req.__("What's new? Read the blog »")
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
);
|
|
267
319
|
|
|
268
320
|
/**
|
|
269
|
-
* @param {object} req
|
|
321
|
+
* @param {object} req
|
|
270
322
|
* @returns {Promise<object>}
|
|
271
323
|
*/
|
|
272
324
|
const welcome_page = async (req) => {
|
|
@@ -275,10 +327,16 @@ const welcome_page = async (req) => {
|
|
|
275
327
|
...packs_available.slice(0, 5),
|
|
276
328
|
{ name: req.__("More..."), description: "" },
|
|
277
329
|
];
|
|
278
|
-
const tables = await Table.find({}, {
|
|
279
|
-
const views = await View.find({});
|
|
280
|
-
const pages = await Page.find({});
|
|
281
|
-
|
|
330
|
+
const tables = await Table.find({}, { cached: true });
|
|
331
|
+
const views = await View.find({}, { cached: true });
|
|
332
|
+
const pages = await Page.find({}, { cached: true });
|
|
333
|
+
const triggers = await Trigger.findAllWithTableName();
|
|
334
|
+
const users = await User.find({}, { orderBy: "id" });
|
|
335
|
+
const roles = await User.get_roles();
|
|
336
|
+
let roleMap = {};
|
|
337
|
+
roles.forEach((r) => {
|
|
338
|
+
roleMap[r.id] = r.role;
|
|
339
|
+
});
|
|
282
340
|
return {
|
|
283
341
|
above: [
|
|
284
342
|
{
|
|
@@ -293,75 +351,37 @@ const welcome_page = async (req) => {
|
|
|
293
351
|
{
|
|
294
352
|
type: "card",
|
|
295
353
|
//title: req.__("Install pack"),
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{ label: req.__("Name"), key: "name" },
|
|
312
|
-
{
|
|
313
|
-
label: req.__("Description"),
|
|
314
|
-
key: "description",
|
|
315
|
-
},
|
|
316
|
-
],
|
|
317
|
-
packlist,
|
|
318
|
-
{ noHeader: true }
|
|
319
|
-
),
|
|
320
|
-
a(
|
|
321
|
-
{ href: `/plugins?set=packs`, class: "btn btn-primary" },
|
|
322
|
-
req.__("Go to pack store »")
|
|
323
|
-
)
|
|
324
|
-
),
|
|
325
|
-
Triggers: await actionsTab(req),
|
|
326
|
-
Files: await filesTab(req),
|
|
327
|
-
},
|
|
354
|
+
bodyClass: "py-0 pr-0",
|
|
355
|
+
class: "welcome-page-entity-list",
|
|
356
|
+
|
|
357
|
+
tabContents:
|
|
358
|
+
triggers.length > 0
|
|
359
|
+
? {
|
|
360
|
+
Triggers: await actionsTab(req, triggers),
|
|
361
|
+
Files: await filesTab(req),
|
|
362
|
+
Packs: packTab(req, packlist),
|
|
363
|
+
}
|
|
364
|
+
: {
|
|
365
|
+
Packs: packTab(req, packlist),
|
|
366
|
+
Triggers: await actionsTab(req, triggers),
|
|
367
|
+
Files: await filesTab(req),
|
|
368
|
+
},
|
|
328
369
|
},
|
|
329
370
|
{
|
|
330
371
|
type: "card",
|
|
331
372
|
//title: req.__("Learn"),
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
class: "btn btn-primary",
|
|
344
|
-
},
|
|
345
|
-
req.__("Go to Wiki »")
|
|
346
|
-
),
|
|
347
|
-
p(req.__("The YouTube channel has some video tutorials")),
|
|
348
|
-
a(
|
|
349
|
-
{
|
|
350
|
-
href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
|
|
351
|
-
class: "btn btn-secondary",
|
|
373
|
+
bodyClass: "py-0 pr-0",
|
|
374
|
+
class: "welcome-page-entity-list",
|
|
375
|
+
tabContents:
|
|
376
|
+
users.length > 4
|
|
377
|
+
? {
|
|
378
|
+
Users: await usersTab(req, users, roleMap),
|
|
379
|
+
Help: helpCard(req),
|
|
380
|
+
}
|
|
381
|
+
: {
|
|
382
|
+
Help: helpCard(req),
|
|
383
|
+
Users: await usersTab(req, users, roleMap),
|
|
352
384
|
},
|
|
353
|
-
req.__("Go to YouTube »")
|
|
354
|
-
),
|
|
355
|
-
div(
|
|
356
|
-
{ class: "mt-3" },
|
|
357
|
-
a(
|
|
358
|
-
{ href: `https://blog.saltcorn.com/` },
|
|
359
|
-
req.__("What's new? Read the blog »")
|
|
360
|
-
)
|
|
361
|
-
)
|
|
362
|
-
),
|
|
363
|
-
Users: await usersTab(req),
|
|
364
|
-
},
|
|
365
385
|
},
|
|
366
386
|
],
|
|
367
387
|
},
|
|
@@ -370,8 +390,8 @@ const welcome_page = async (req) => {
|
|
|
370
390
|
};
|
|
371
391
|
|
|
372
392
|
/**
|
|
373
|
-
* @param {object} req
|
|
374
|
-
* @param {object} res
|
|
393
|
+
* @param {object} req
|
|
394
|
+
* @param {object} res
|
|
375
395
|
* @returns {Promise<void>}
|
|
376
396
|
*/
|
|
377
397
|
const no_views_logged_in = async (req, res) => {
|
|
@@ -400,9 +420,9 @@ const no_views_logged_in = async (req, res) => {
|
|
|
400
420
|
};
|
|
401
421
|
|
|
402
422
|
/**
|
|
403
|
-
* @param {number} role_id
|
|
404
|
-
* @param {object} res
|
|
405
|
-
* @param {object} req
|
|
423
|
+
* @param {number} role_id
|
|
424
|
+
* @param {object} res
|
|
425
|
+
* @param {object} req
|
|
406
426
|
* @returns {Promise<boolean>}
|
|
407
427
|
*/
|
|
408
428
|
const get_config_response = async (role_id, res, req) => {
|
|
@@ -420,8 +440,11 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
420
440
|
const contents = await db_page.run(req.query, { res, req });
|
|
421
441
|
|
|
422
442
|
res.sendWrap(
|
|
423
|
-
{
|
|
424
|
-
|
|
443
|
+
{
|
|
444
|
+
title: db_page.title,
|
|
445
|
+
description: db_page.description,
|
|
446
|
+
bodyClass: "page_" + db.sqlsanitize(homeCfg),
|
|
447
|
+
} || `${pagename} page`,
|
|
425
448
|
contents
|
|
426
449
|
);
|
|
427
450
|
} else res.redirect(homeCfg);
|
|
@@ -431,8 +454,8 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
431
454
|
|
|
432
455
|
/**
|
|
433
456
|
* Function assigned to 'module.exports'.
|
|
434
|
-
* @param {object} req
|
|
435
|
-
* @param {object} res
|
|
457
|
+
* @param {object} req
|
|
458
|
+
* @param {object} res
|
|
436
459
|
* @returns {Promise<void>}
|
|
437
460
|
*/
|
|
438
461
|
module.exports = async (req, res) => {
|
package/routes/index.js
CHANGED
|
@@ -4,6 +4,43 @@
|
|
|
4
4
|
* @module routes/index
|
|
5
5
|
* @subcategory routes
|
|
6
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
|
+
*/
|
|
43
|
+
|
|
7
44
|
const table = require("./tables");
|
|
8
45
|
const field = require("./fields");
|
|
9
46
|
const list = require("./list");
|
package/routes/page.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
const { add_edit_bar } = require("../markup/admin.js");
|
|
19
19
|
const { traverseSync } = require("@saltcorn/data/models/layout");
|
|
20
20
|
const { run_action_column } = require("@saltcorn/data/plugin-helper");
|
|
21
|
+
const db = require("@saltcorn/data/db");
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @type {object}
|
|
@@ -47,7 +48,11 @@ router.get(
|
|
|
47
48
|
const contents = await db_page.run(req.query, { res, req });
|
|
48
49
|
const title = scan_for_page_title(contents, db_page.title);
|
|
49
50
|
res.sendWrap(
|
|
50
|
-
{
|
|
51
|
+
{
|
|
52
|
+
title,
|
|
53
|
+
description: db_page.description,
|
|
54
|
+
bodyClass: "page_" + db.sqlsanitize(pagename),
|
|
55
|
+
} || `${pagename} page`,
|
|
51
56
|
add_edit_bar({
|
|
52
57
|
role,
|
|
53
58
|
title: db_page.name,
|
package/routes/view.js
CHANGED
|
@@ -68,7 +68,7 @@ router.get(
|
|
|
68
68
|
what: req.__("View"),
|
|
69
69
|
url: `/viewedit/edit/${encodeURIComponent(view.name)}`,
|
|
70
70
|
contents,
|
|
71
|
-
req
|
|
71
|
+
req,
|
|
72
72
|
})
|
|
73
73
|
);
|
|
74
74
|
})
|
|
@@ -92,18 +92,18 @@ router.post(
|
|
|
92
92
|
res.send("");
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
|
-
let query = {};
|
|
95
|
+
let query = req.body || {};
|
|
96
96
|
let row;
|
|
97
97
|
let table;
|
|
98
98
|
const sfs = await view.get_state_fields();
|
|
99
99
|
for (const sf of sfs) {
|
|
100
|
-
if (sf.required) {
|
|
100
|
+
if (sf.required && !query[sf.name]) {
|
|
101
101
|
if (!row) {
|
|
102
102
|
if (!table)
|
|
103
103
|
table = await Table.findOne(view.table_id || view.exttable_name);
|
|
104
104
|
row = await table.getRow({});
|
|
105
105
|
}
|
|
106
|
-
if(row) query[sf.name] = row[sf.name];
|
|
106
|
+
if (row) query[sf.name] = row[sf.name];
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
const contents = await view.run(query, { req, res });
|
package/serve.js
CHANGED
|
@@ -33,11 +33,14 @@ const { setTenant, getSessionStore } = require("./routes/utils");
|
|
|
33
33
|
const passport = require("passport");
|
|
34
34
|
const { authenticate } = require("passport");
|
|
35
35
|
const View = require("@saltcorn/data/models/view");
|
|
36
|
+
const {
|
|
37
|
+
listenForChanges,
|
|
38
|
+
getRelevantPackages,
|
|
39
|
+
getPluginDirectories,
|
|
40
|
+
} = require("./restart_watcher");
|
|
36
41
|
|
|
37
42
|
// helpful https://gist.github.com/jpoehls/2232358
|
|
38
|
-
|
|
39
43
|
/**
|
|
40
|
-
* @param {object} opts
|
|
41
44
|
* @param {object} opts
|
|
42
45
|
* @param {boolean} opts.disableMigrate
|
|
43
46
|
* @param {boolean} [useClusterAdaptor = true]
|
|
@@ -146,6 +149,7 @@ module.exports =
|
|
|
146
149
|
* @param {boolean} opts.watchReaper
|
|
147
150
|
* @param {boolean} opts.disableScheduler
|
|
148
151
|
* @param {number} opts.defaultNCPUs
|
|
152
|
+
* @param {boolean} opts.dev
|
|
149
153
|
* @param {...*} opts.appargs
|
|
150
154
|
* @returns {Promise<void>}
|
|
151
155
|
*/
|
|
@@ -154,8 +158,12 @@ module.exports =
|
|
|
154
158
|
watchReaper,
|
|
155
159
|
disableScheduler,
|
|
156
160
|
defaultNCPUs,
|
|
161
|
+
dev,
|
|
157
162
|
...appargs
|
|
158
163
|
} = {}) => {
|
|
164
|
+
if (dev && cluster.isMaster) {
|
|
165
|
+
listenForChanges(getRelevantPackages(), await getPluginDirectories());
|
|
166
|
+
}
|
|
159
167
|
const useNCpus = process.env.SALTCORN_NWORKERS
|
|
160
168
|
? +process.env.SALTCORN_NWORKERS
|
|
161
169
|
: defaultNCPUs;
|
package/wrapper.js
CHANGED
|
@@ -240,12 +240,14 @@ module.exports = (version_tag) =>
|
|
|
240
240
|
headers: get_headers(req, version_tag),
|
|
241
241
|
role,
|
|
242
242
|
req,
|
|
243
|
+
bodyClass: "auth",
|
|
243
244
|
})
|
|
244
245
|
);
|
|
245
246
|
}
|
|
246
247
|
};
|
|
247
248
|
res.sendWrap = function (opts, ...html) {
|
|
248
249
|
const title = typeof opts === "string" ? opts : opts.title;
|
|
250
|
+
const bodyClass = opts.bodyClass || "";
|
|
249
251
|
const alerts = getFlashes(req);
|
|
250
252
|
const state = getState();
|
|
251
253
|
const layout = state.getLayout(req.user);
|
|
@@ -282,6 +284,7 @@ module.exports = (version_tag) =>
|
|
|
282
284
|
headers: get_headers(req, version_tag, opts.description, pageHeaders),
|
|
283
285
|
role,
|
|
284
286
|
req,
|
|
287
|
+
bodyClass,
|
|
285
288
|
})
|
|
286
289
|
);
|
|
287
290
|
};
|