@saltcorn/server 0.6.1-beta.3 → 0.6.2-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/app.js +9 -13
- package/auth/admin.js +5 -22
- package/auth/index.js +15 -2
- package/auth/roleadmin.js +6 -16
- package/auth/routes.js +23 -52
- package/index.js +17 -1
- package/locales/en.json +20 -1
- package/markup/admin.js +17 -0
- package/markup/index.js +14 -1
- package/package.json +22 -9
- package/public/saltcorn.css +2 -2
- package/public/saltcorn.js +12 -0
- package/restart_watcher.js +155 -0
- package/routes/actions.js +1 -15
- package/routes/admin.js +6 -23
- package/routes/api.js +51 -60
- package/routes/config.js +0 -1
- package/routes/crashlog.js +1 -4
- package/routes/delete.js +1 -2
- package/routes/edit.js +1 -2
- package/routes/eventlog.js +2 -15
- package/routes/events.js +0 -1
- package/routes/fields.js +14 -22
- package/routes/files.js +144 -64
- package/routes/homepage.js +131 -105
- package/routes/index.js +37 -0
- package/routes/infoarch.js +12 -19
- package/routes/library.js +1 -4
- package/routes/list.js +2 -5
- package/routes/menu.js +6 -8
- package/routes/packs.js +2 -8
- package/routes/page.js +7 -8
- package/routes/pageedit.js +18 -30
- package/routes/plugins.js +21 -34
- package/routes/scapi.js +155 -184
- package/routes/search.js +6 -10
- package/routes/settings.js +1 -2
- package/routes/tables.js +1 -22
- package/routes/tenant.js +1 -9
- package/routes/utils.js +19 -19
- package/routes/view.js +11 -9
- package/routes/viewedit.js +18 -29
- package/s3storage.js +167 -0
- package/serve.js +10 -2
- package/wrapper.js +3 -0
package/s3storage.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
var aws = require("aws-sdk");
|
|
2
|
+
var express = require("express");
|
|
3
|
+
var multer = require("multer");
|
|
4
|
+
var multerS3 = require("multer-s3");
|
|
5
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
6
|
+
const fileUpload = require("express-fileupload");
|
|
7
|
+
const { v4: uuidv4 } = require("uuid");
|
|
8
|
+
const { create } = require("@saltcorn/data/models/file");
|
|
9
|
+
var contentDisposition = require("content-disposition");
|
|
10
|
+
|
|
11
|
+
function createS3Client() {
|
|
12
|
+
return new aws.S3({
|
|
13
|
+
secretAccessKey: getState().getConfig("storage_s3_access_secret"),
|
|
14
|
+
accessKeyId: getState().getConfig("storage_s3_access_key"),
|
|
15
|
+
region: getState().getConfig("storage_s3_region"),
|
|
16
|
+
endpoint: getState().getConfig("storage_s3_endpoint"),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
/**
|
|
22
|
+
* Selector for file upload handler middleware. It will dispatch the
|
|
23
|
+
* file upload handler to the engine specified in the configuration.
|
|
24
|
+
*
|
|
25
|
+
* @param {*} req
|
|
26
|
+
* @param {*} res
|
|
27
|
+
* @param {*} next
|
|
28
|
+
*/
|
|
29
|
+
middlewareSelect: async function (req, res, next) {
|
|
30
|
+
const useS3 = getState().getConfig("storage_s3_enabled");
|
|
31
|
+
if (useS3 === true) {
|
|
32
|
+
// Create S3 object
|
|
33
|
+
|
|
34
|
+
// Create multer function
|
|
35
|
+
const s3upload = multer({
|
|
36
|
+
storage: multerS3({
|
|
37
|
+
s3: createS3Client(),
|
|
38
|
+
bucket: getState().getConfig("storage_s3_bucket"),
|
|
39
|
+
metadata: function (req, file, cb) {
|
|
40
|
+
cb(null, { fieldName: file.fieldname });
|
|
41
|
+
},
|
|
42
|
+
key: function (req, file, cb) {
|
|
43
|
+
cb(null, "tmp/" + Date.now().toString() + uuidv4());
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
}).any();
|
|
47
|
+
|
|
48
|
+
s3upload(req, res, next);
|
|
49
|
+
} else {
|
|
50
|
+
// Use regular file upload
|
|
51
|
+
fileUpload({
|
|
52
|
+
useTempFiles: true,
|
|
53
|
+
createParentPath: true,
|
|
54
|
+
tempFileDir: "/tmp/",
|
|
55
|
+
})(req, res, next);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Transform the processed req.files into that is suitable with
|
|
61
|
+
* Saltcorn File interface. It must be run after the middlewareSelect
|
|
62
|
+
*
|
|
63
|
+
* @param {*} req
|
|
64
|
+
* @param {*} res
|
|
65
|
+
* @param {*} next
|
|
66
|
+
*/
|
|
67
|
+
middlewareTransform: async function (req, res, next) {
|
|
68
|
+
// If nothing to process or S3 is not enabled
|
|
69
|
+
const useS3 = getState().getConfig("storage_s3_enabled");
|
|
70
|
+
if (!req.files || !useS3) {
|
|
71
|
+
next();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create S3 object
|
|
76
|
+
var s3 = createS3Client();
|
|
77
|
+
const bucket = getState().getConfig("storage_s3_bucket");
|
|
78
|
+
|
|
79
|
+
let newFileObject = {};
|
|
80
|
+
for (const file of req.files) {
|
|
81
|
+
file.mv = (newpath) => {
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
s3.copyObject(
|
|
84
|
+
{
|
|
85
|
+
Bucket: bucket,
|
|
86
|
+
CopySource: bucket + "/" + file.key,
|
|
87
|
+
Key: newpath,
|
|
88
|
+
},
|
|
89
|
+
function (err, data) {
|
|
90
|
+
if (err) reject(err);
|
|
91
|
+
else {
|
|
92
|
+
// Then delete
|
|
93
|
+
s3.deleteObject(
|
|
94
|
+
{
|
|
95
|
+
Bucket: bucket,
|
|
96
|
+
Key: file.key,
|
|
97
|
+
},
|
|
98
|
+
(err, data) => {
|
|
99
|
+
if (err) reject(err);
|
|
100
|
+
else resolve(data);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
file.s3object = true;
|
|
109
|
+
file.name = file.originalname;
|
|
110
|
+
newFileObject[file.fieldname] = file;
|
|
111
|
+
}
|
|
112
|
+
req.files = newFileObject;
|
|
113
|
+
next();
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Selector to serve object based on S3 state
|
|
118
|
+
*
|
|
119
|
+
* @param {*} file
|
|
120
|
+
* @param {*} res
|
|
121
|
+
* @param {*} download
|
|
122
|
+
*/
|
|
123
|
+
serveObject: function (file, res, download) {
|
|
124
|
+
if (file.s3_store) {
|
|
125
|
+
var s3 = createS3Client();
|
|
126
|
+
const bucket = getState().getConfig("storage_s3_bucket");
|
|
127
|
+
|
|
128
|
+
var params = {
|
|
129
|
+
Bucket: bucket,
|
|
130
|
+
Key: file.location,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Forward the object
|
|
134
|
+
s3.getObject(params)
|
|
135
|
+
.on("httpHeaders", function (statusCode, headers) {
|
|
136
|
+
if (!!download)
|
|
137
|
+
res.set("Content-Disposition", contentDisposition(file.filename));
|
|
138
|
+
res.set("Content-Length", headers["content-length"]);
|
|
139
|
+
this.response.httpResponse.createUnbufferedStream().pipe(res);
|
|
140
|
+
})
|
|
141
|
+
.send();
|
|
142
|
+
} else {
|
|
143
|
+
// Use legacy file download
|
|
144
|
+
res.download(file.location, file.filename);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
unlinkObject: function (file) {
|
|
149
|
+
if (file.s3_store) {
|
|
150
|
+
var s3 = createS3Client();
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
s3.deleteObject(
|
|
153
|
+
{
|
|
154
|
+
Bucket: getState().getConfig("storage_s3_bucket"),
|
|
155
|
+
Key: file.location,
|
|
156
|
+
},
|
|
157
|
+
(err, data) => {
|
|
158
|
+
if (err) reject(err);
|
|
159
|
+
else resolve(data);
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
return fs.unlink(file.location);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
};
|
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
|
};
|