@balena/pinejs 15.0.1 → 15.1.0-build-web-resource-4-528904929ba5aa3ec2cf3e80bd7800775c7b60a3-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/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +24 -1
- package/CHANGELOG.md +7 -1
- package/README.md +10 -0
- package/VERSION +1 -1
- package/docker-compose.npm-test.yml +22 -1
- package/out/config-loader/config-loader.d.ts +2 -0
- package/out/config-loader/config-loader.js +13 -2
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.d.ts +6 -2
- package/out/sbvr-api/sbvr-utils.js +11 -2
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/server-glue/server.js +0 -2
- package/out/server-glue/server.js.map +1 -1
- package/out/server-glue/webresource-handler.d.ts +21 -0
- package/out/server-glue/webresource-handler.js +217 -0
- package/out/server-glue/webresource-handler.js.map +1 -0
- package/out/server-glue/webresource-handlers/NoopHandler.d.ts +5 -0
- package/out/server-glue/webresource-handlers/NoopHandler.js +17 -0
- package/out/server-glue/webresource-handlers/NoopHandler.js.map +1 -0
- package/out/server-glue/webresource-handlers/S3Handler.d.ts +11 -0
- package/out/server-glue/webresource-handlers/S3Handler.js +58 -0
- package/out/server-glue/webresource-handlers/S3Handler.js.map +1 -0
- package/package.json +17 -12
- package/src/config-loader/config-loader.ts +27 -2
- package/src/sbvr-api/sbvr-utils.ts +10 -1
- package/src/server-glue/server.ts +0 -3
- package/src/server-glue/webresource-handler.ts +317 -0
- package/src/server-glue/webresource-handlers/NoopHandler.ts +20 -0
- package/src/server-glue/webresource-handlers/S3Handler.ts +82 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.setupUploadHooks = exports.getDefaultHandler = exports.getUploaderMiddlware = void 0;
|
4
|
+
const busboy = require("busboy");
|
5
|
+
const is = require("type-is");
|
6
|
+
const uriParser = require("../sbvr-api/uri-parser");
|
7
|
+
const sbvr_utils_1 = require("../sbvr-api/sbvr-utils");
|
8
|
+
const permissions_1 = require("../sbvr-api/permissions");
|
9
|
+
const sbvrUtils = require("../sbvr-api/sbvr-utils");
|
10
|
+
const S3Handler_1 = require("./webresource-handlers/S3Handler");
|
11
|
+
const NoopHandler_1 = require("./webresource-handlers/NoopHandler");
|
12
|
+
const odata_to_abstract_sql_1 = require("@balena/odata-to-abstract-sql");
|
13
|
+
const module_1 = require("./module");
|
14
|
+
const ifFileInValidPath = async (fieldname, req) => {
|
15
|
+
if (req.method !== 'POST' && req.method !== 'PATCH') {
|
16
|
+
return false;
|
17
|
+
}
|
18
|
+
const apiRoot = (0, sbvr_utils_1.getApiRoot)(req);
|
19
|
+
if (apiRoot == null) {
|
20
|
+
return false;
|
21
|
+
}
|
22
|
+
const model = (0, sbvr_utils_1.getModel)(apiRoot);
|
23
|
+
const { resourceName } = await uriParser.parseOData({
|
24
|
+
url: req.url,
|
25
|
+
method: req.method,
|
26
|
+
});
|
27
|
+
const permission = req.method === 'POST' ? 'create' : 'update';
|
28
|
+
const vocab = model.versions[model.versions.length - 1];
|
29
|
+
const hasPermissions = await (0, permissions_1.checkPermissions)(req, permission, resourceName, vocab);
|
30
|
+
if (!hasPermissions) {
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
const fields = model.abstractSql.tables[resourceName].fields;
|
34
|
+
const dbFieldName = (0, odata_to_abstract_sql_1.odataNameToSqlName)(fieldname);
|
35
|
+
for (const field of fields) {
|
36
|
+
if (field.fieldName === dbFieldName && field.dataType === 'WebResource') {
|
37
|
+
return true;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
return false;
|
41
|
+
};
|
42
|
+
const getUploaderMiddlware = (handler) => {
|
43
|
+
const completeUploads = [];
|
44
|
+
const filesUploaded = [];
|
45
|
+
return async (req, _res, next) => {
|
46
|
+
if (!is(req, ['multipart'])) {
|
47
|
+
return next();
|
48
|
+
}
|
49
|
+
const bb = busboy({ headers: req.headers });
|
50
|
+
let isAborting = false;
|
51
|
+
const done = () => {
|
52
|
+
req.unpipe(bb);
|
53
|
+
req.on('readable', req.read.bind(req));
|
54
|
+
bb.removeAllListeners();
|
55
|
+
};
|
56
|
+
const clearFiles = () => {
|
57
|
+
isAborting = true;
|
58
|
+
const deletions = filesUploaded.map((file) => handler.removeFile(file));
|
59
|
+
return Promise.all(deletions).catch((err) => console.error('Error deleting file', err));
|
60
|
+
};
|
61
|
+
bb.on('file', async (fieldname, filestream, info) => {
|
62
|
+
if (!isAborting && (await ifFileInValidPath(fieldname, req))) {
|
63
|
+
const file = {
|
64
|
+
originalname: info.filename,
|
65
|
+
encoding: info.encoding,
|
66
|
+
mimetype: info.mimeType,
|
67
|
+
stream: filestream,
|
68
|
+
fieldname,
|
69
|
+
};
|
70
|
+
const promise = handler.handleFile(file).then((result) => {
|
71
|
+
req.body[fieldname] = {
|
72
|
+
filename: info.filename,
|
73
|
+
contentType: info.mimeType,
|
74
|
+
contentDisposition: undefined,
|
75
|
+
size: result.size,
|
76
|
+
href: result.filename,
|
77
|
+
};
|
78
|
+
filesUploaded.push(result.filename);
|
79
|
+
});
|
80
|
+
completeUploads.push(promise);
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
filestream.resume();
|
84
|
+
}
|
85
|
+
});
|
86
|
+
bb.on('field', (name, val, _info) => {
|
87
|
+
req.body[name] = val;
|
88
|
+
});
|
89
|
+
bb.on('finish', async () => {
|
90
|
+
try {
|
91
|
+
await Promise.all(completeUploads);
|
92
|
+
done();
|
93
|
+
next();
|
94
|
+
}
|
95
|
+
catch (err) {
|
96
|
+
console.error('Error uploading file', err);
|
97
|
+
await clearFiles();
|
98
|
+
next(err);
|
99
|
+
}
|
100
|
+
});
|
101
|
+
bb.on('error', async (err) => {
|
102
|
+
await clearFiles();
|
103
|
+
done();
|
104
|
+
next(err);
|
105
|
+
});
|
106
|
+
req.pipe(bb);
|
107
|
+
};
|
108
|
+
};
|
109
|
+
exports.getUploaderMiddlware = getUploaderMiddlware;
|
110
|
+
const getWebResourceFields = (request) => {
|
111
|
+
const fields = request.abstractSqlModel?.tables[request.resourceName]?.modifyFields ??
|
112
|
+
request.abstractSqlModel?.tables[request.resourceName]?.fields;
|
113
|
+
if (fields == null) {
|
114
|
+
return [];
|
115
|
+
}
|
116
|
+
return fields
|
117
|
+
.filter((f) => f.dataType === 'WebResource')
|
118
|
+
.map((f) => (0, odata_to_abstract_sql_1.sqlNameToODataName)(f.fieldName));
|
119
|
+
};
|
120
|
+
const getModifiedFields = (request) => {
|
121
|
+
return Object.entries(request.values)
|
122
|
+
.filter(([_key, value]) => value !== undefined)
|
123
|
+
.map(([key, _value]) => key);
|
124
|
+
};
|
125
|
+
const deleteFiles = async (keysToDelete, webResourceHandler) => {
|
126
|
+
const promises = keysToDelete.map((r) => webResourceHandler.removeFile(r));
|
127
|
+
await Promise.all(promises);
|
128
|
+
};
|
129
|
+
const getCreateWebResourceHook = (webResourceHandler) => {
|
130
|
+
return {
|
131
|
+
'POSTRUN-ERROR': async ({ tx, request }) => {
|
132
|
+
tx?.on('rollback', async () => {
|
133
|
+
const fields = getWebResourceFields(request);
|
134
|
+
if (fields.length === 0) {
|
135
|
+
return;
|
136
|
+
}
|
137
|
+
const keysToDelete = fields
|
138
|
+
.filter((f) => isDefined(request.values[f]))
|
139
|
+
.map((f) => request.values[f].href);
|
140
|
+
await deleteFiles(keysToDelete, webResourceHandler);
|
141
|
+
});
|
142
|
+
},
|
143
|
+
};
|
144
|
+
};
|
145
|
+
const isDefined = (x) => x != null;
|
146
|
+
const getWebResourcesHrefs = (webResources) => {
|
147
|
+
const hrefs = Object.values(webResources ?? {})
|
148
|
+
.filter(isDefined)
|
149
|
+
.map((resourceKey) => resourceKey.href);
|
150
|
+
return hrefs;
|
151
|
+
};
|
152
|
+
const getRemoveWebResourceHook = (webResourceHandler) => {
|
153
|
+
return {
|
154
|
+
PRERUN: async (args) => {
|
155
|
+
const { api, request } = args;
|
156
|
+
let fields = getWebResourceFields(request);
|
157
|
+
if (fields.length === 0) {
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
if (request.method === 'PATCH') {
|
161
|
+
if (request.odataQuery?.key == null) {
|
162
|
+
throw new module_1.errors.BadRequestError('WebResources can only be updated when providing a resource key.');
|
163
|
+
}
|
164
|
+
const allFields = getModifiedFields(request);
|
165
|
+
fields = fields.filter((f) => allFields.includes(f));
|
166
|
+
}
|
167
|
+
if (fields.length === 0) {
|
168
|
+
return;
|
169
|
+
}
|
170
|
+
const ids = await sbvrUtils.getAffectedIds(args);
|
171
|
+
if (ids.length === 0) {
|
172
|
+
return;
|
173
|
+
}
|
174
|
+
if (ids.length !== 1) {
|
175
|
+
throw new module_1.errors.BadRequestError('Resources containing webresources can only be updated/deleted one at a time.');
|
176
|
+
}
|
177
|
+
const webResources = (await api.get({
|
178
|
+
resource: request.resourceName,
|
179
|
+
passthrough: {
|
180
|
+
tx: args.tx,
|
181
|
+
req: module_1.permissions.root,
|
182
|
+
},
|
183
|
+
id: ids[0],
|
184
|
+
options: {
|
185
|
+
$select: fields,
|
186
|
+
},
|
187
|
+
}));
|
188
|
+
request.custom.$pineWebResourcesToDelete =
|
189
|
+
getWebResourcesHrefs(webResources);
|
190
|
+
},
|
191
|
+
POSTRUN: ({ tx, request }) => {
|
192
|
+
tx.on('end', () => {
|
193
|
+
const keysToDelete = request.custom.$pineWebResourcesToDelete || [];
|
194
|
+
deleteFiles(keysToDelete, webResourceHandler);
|
195
|
+
});
|
196
|
+
},
|
197
|
+
};
|
198
|
+
};
|
199
|
+
const getDefaultHandler = () => {
|
200
|
+
let handler;
|
201
|
+
try {
|
202
|
+
handler = new S3Handler_1.S3Handler();
|
203
|
+
}
|
204
|
+
catch (e) {
|
205
|
+
console.warn(`Failed to initialize S3 handler, using noop ${e}`);
|
206
|
+
handler = new NoopHandler_1.NoopHandler();
|
207
|
+
}
|
208
|
+
return handler;
|
209
|
+
};
|
210
|
+
exports.getDefaultHandler = getDefaultHandler;
|
211
|
+
const setupUploadHooks = (handler, apiRoot, resourceName) => {
|
212
|
+
sbvrUtils.addPureHook('PATCH', apiRoot, resourceName, getRemoveWebResourceHook(handler));
|
213
|
+
sbvrUtils.addPureHook('DELETE', apiRoot, resourceName, getRemoveWebResourceHook(handler));
|
214
|
+
sbvrUtils.addPureHook('POST', apiRoot, resourceName, getCreateWebResourceHook(handler));
|
215
|
+
};
|
216
|
+
exports.setupUploadHooks = setupUploadHooks;
|
217
|
+
//# sourceMappingURL=webresource-handler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"webresource-handler.js","sourceRoot":"","sources":["../../src/server-glue/webresource-handler.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { IncomingFile, UploadResponse, WebResourceHandler } from '../webresource-handler';
|
2
|
+
export declare class NoopHandler implements WebResourceHandler {
|
3
|
+
handleFile(resource: IncomingFile): Promise<UploadResponse>;
|
4
|
+
removeFile(_fileReference: string): Promise<void>;
|
5
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.NoopHandler = void 0;
|
4
|
+
class NoopHandler {
|
5
|
+
async handleFile(resource) {
|
6
|
+
resource.stream.resume();
|
7
|
+
return {
|
8
|
+
filename: 'noop',
|
9
|
+
size: 0,
|
10
|
+
};
|
11
|
+
}
|
12
|
+
async removeFile(_fileReference) {
|
13
|
+
return;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
exports.NoopHandler = NoopHandler;
|
17
|
+
//# sourceMappingURL=NoopHandler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"NoopHandler.js","sourceRoot":"","sources":["../../../src/server-glue/webresource-handlers/NoopHandler.ts"],"names":[],"mappings":";;;AAMA,MAAa,WAAW;IAChB,KAAK,CAAC,UAAU,CAAC,QAAsB;QAE7C,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO;YACN,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,CAAC;SACP,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,cAAsB;QAC7C,OAAO;IACR,CAAC;CACD;AAbD,kCAaC"}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { IncomingFile, UploadResponse, WebResourceHandler } from '../webresource-handler';
|
2
|
+
export declare class S3Handler implements WebResourceHandler {
|
3
|
+
private readonly config;
|
4
|
+
private readonly bucket;
|
5
|
+
private readonly endpoint;
|
6
|
+
private client;
|
7
|
+
constructor();
|
8
|
+
handleFile(resource: IncomingFile): Promise<UploadResponse>;
|
9
|
+
removeFile(fileReference: string): Promise<void>;
|
10
|
+
private getS3URL;
|
11
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.S3Handler = void 0;
|
4
|
+
const env_parsing_1 = require("@balena/env-parsing");
|
5
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
6
|
+
const lib_storage_1 = require("@aws-sdk/lib-storage");
|
7
|
+
const crypto_1 = require("crypto");
|
8
|
+
class S3Handler {
|
9
|
+
constructor() {
|
10
|
+
this.endpoint = (0, env_parsing_1.requiredVar)('S3_ENDPOINT');
|
11
|
+
this.config = {
|
12
|
+
region: (0, env_parsing_1.optionalVar)('S3_REGION', 'us-east-1'),
|
13
|
+
credentials: {
|
14
|
+
accessKeyId: (0, env_parsing_1.requiredVar)('S3_ACCESS_KEY'),
|
15
|
+
secretAccessKey: (0, env_parsing_1.requiredVar)('S3_SECRET_KEY'),
|
16
|
+
},
|
17
|
+
endpoint: this.endpoint,
|
18
|
+
forcePathStyle: true,
|
19
|
+
};
|
20
|
+
this.bucket = (0, env_parsing_1.optionalVar)('S3_STORAGE_ADAPTER_BUCKET', 'balena-pine-web-resources');
|
21
|
+
this.client = new client_s3_1.S3Client(this.config);
|
22
|
+
}
|
23
|
+
async handleFile(resource) {
|
24
|
+
let size = 0;
|
25
|
+
const key = `${resource.fieldname}_${(0, crypto_1.randomUUID)()}_${resource.originalname}`;
|
26
|
+
const params = {
|
27
|
+
Bucket: this.bucket,
|
28
|
+
ACL: 'public-read',
|
29
|
+
StorageClass: 'STANDARD',
|
30
|
+
Key: key,
|
31
|
+
Body: resource.stream,
|
32
|
+
ContentType: resource.mimetype,
|
33
|
+
};
|
34
|
+
const upload = new lib_storage_1.Upload({ client: this.client, params });
|
35
|
+
upload.on('httpUploadProgress', (ev) => {
|
36
|
+
size = ev.total ? ev.total : ev.loaded;
|
37
|
+
});
|
38
|
+
await upload.done();
|
39
|
+
const filename = this.getS3URL(key);
|
40
|
+
return { size, filename };
|
41
|
+
}
|
42
|
+
async removeFile(fileReference) {
|
43
|
+
const fileReferences = fileReference.split('/');
|
44
|
+
const fileKey = fileReferences[fileReferences.length - 1];
|
45
|
+
const command = new client_s3_1.DeleteObjectCommand({
|
46
|
+
Bucket: this.bucket,
|
47
|
+
Key: fileKey,
|
48
|
+
});
|
49
|
+
await this.client.send(command);
|
50
|
+
}
|
51
|
+
getS3URL(key) {
|
52
|
+
return this.endpoint.includes(this.bucket)
|
53
|
+
? `${this.endpoint}/${key}`
|
54
|
+
: `${this.endpoint}/${this.bucket}/${key}`;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
exports.S3Handler = S3Handler;
|
58
|
+
//# sourceMappingURL=S3Handler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"S3Handler.js","sourceRoot":"","sources":["../../../src/server-glue/webresource-handlers/S3Handler.ts"],"names":[],"mappings":";;;AAAA,qDAA+D;AAM/D,kDAI4B;AAC5B,sDAA8C;AAE9C,mCAAoC;AAEpC,MAAa,SAAS;IAMrB;QACC,IAAI,CAAC,QAAQ,GAAG,IAAA,yBAAW,EAAC,aAAa,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG;YACb,MAAM,EAAE,IAAA,yBAAW,EAAC,WAAW,EAAE,WAAW,CAAC;YAC7C,WAAW,EAAE;gBACZ,WAAW,EAAE,IAAA,yBAAW,EAAC,eAAe,CAAC;gBACzC,eAAe,EAAE,IAAA,yBAAW,EAAC,eAAe,CAAC;aAC7C;YACD,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,cAAc,EAAE,IAAI;SACpB,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,IAAA,yBAAW,EACxB,2BAA2B,EAC3B,2BAA2B,CAC3B,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,IAAI,oBAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,QAAsB;QAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,SAAS,IAAI,IAAA,mBAAU,GAAE,IAChD,QAAQ,CAAC,YACV,EAAE,CAAC;QACH,MAAM,MAAM,GAAG;YACd,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,aAAa;YAClB,YAAY,EAAE,UAAU;YACxB,GAAG,EAAE,GAAG;YACR,IAAI,EAAE,QAAQ,CAAC,MAAM;YACrB,WAAW,EAAE,QAAQ,CAAC,QAAQ;SAC9B,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,oBAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAE3D,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,EAAE,EAAE,EAAE;YACtC,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAO,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,aAAqB;QAC5C,MAAM,cAAc,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,cAAc,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE1D,MAAM,OAAO,GAAG,IAAI,+BAAmB,CAAC;YACvC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,OAAO;SACZ,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAEO,QAAQ,CAAC,GAAW;QAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YACzC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,GAAG,EAAE;YAC3B,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;IAC7C,CAAC;CACD;AAlED,8BAkEC"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@balena/pinejs",
|
3
|
-
"version": "15.0
|
3
|
+
"version": "15.1.0-build-web-resource-4-528904929ba5aa3ec2cf3e80bd7800775c7b60a3-2",
|
4
4
|
"main": "out/server-glue/module",
|
5
5
|
"repository": "git@github.com:balena-io/pinejs.git",
|
6
6
|
"license": "Apache-2.0",
|
@@ -18,20 +18,20 @@
|
|
18
18
|
"webpack-server": "grunt server",
|
19
19
|
"webpack-build": "npm run webpack-browser && npm run webpack-module && npm run webpack-server",
|
20
20
|
"lint": "balena-lint -e js -e ts src build typings Gruntfile.ts && npx tsc --project tsconfig.dev.json --noEmit",
|
21
|
-
"test": "npm run lint
|
22
|
-
"test:compose": "trap 'docker-compose -f docker-compose.npm-test.yml down ; echo Stopped ; exit 0' SIGINT; docker-compose -f docker-compose.npm-test.yml up -d && sleep 2 && DATABASE_URL=postgres://docker:docker@localhost:5431/postgres npm run mocha",
|
21
|
+
"test": "npm run lint & npm run build && npm run webpack-build && npm run test:compose",
|
22
|
+
"test:compose": "trap 'docker-compose -f docker-compose.npm-test.yml down ; echo Stopped ; exit 0' SIGINT; docker-compose -f docker-compose.npm-test.yml up -d && sleep 2 && DATABASE_URL=postgres://docker:docker@localhost:5431/postgres S3_ENDPOINT=http://localhost:43680 S3_ACCESS_KEY=USERNAME S3_SECRET_KEY=PASSWORD npm run mocha",
|
23
23
|
"mocha": "TS_NODE_FILES=true mocha",
|
24
24
|
"prettify": "balena-lint -e js -e ts --fix src build typings Gruntfile.ts"
|
25
25
|
},
|
26
26
|
"dependencies": {
|
27
|
-
"@balena/abstract-sql-compiler": "
|
28
|
-
"@balena/abstract-sql-to-typescript": "
|
27
|
+
"@balena/abstract-sql-compiler": "9.0.3-build-bumps-sbvr-types-with-web-resource-aa97f14f560002592f9d270a6746423f38eaf5da-1",
|
28
|
+
"@balena/abstract-sql-to-typescript": "2.1.0-build-add-web-resource-f0327d04d7bccca132d040a45f91eb23c30d8f3d-1",
|
29
29
|
"@balena/env-parsing": "^1.1.5",
|
30
30
|
"@balena/lf-to-abstract-sql": "^5.0.0",
|
31
|
-
"@balena/odata-parser": "^
|
32
|
-
"@balena/odata-to-abstract-sql": "
|
31
|
+
"@balena/odata-parser": "^2.4.6",
|
32
|
+
"@balena/odata-to-abstract-sql": "6.0.2-build-bumps-for-web-resource-fc6584414cedc9d72ae810bc2804a00d218b48a7-1",
|
33
33
|
"@balena/sbvr-parser": "^1.4.3",
|
34
|
-
"@balena/sbvr-types": "
|
34
|
+
"@balena/sbvr-types": "5.1.0-build-web-resource-2-18a09938422881c4507118f81f2d5395e132e741-1",
|
35
35
|
"@types/body-parser": "^1.19.2",
|
36
36
|
"@types/compression": "^1.7.2",
|
37
37
|
"@types/cookie-parser": "^1.4.3",
|
@@ -51,6 +51,7 @@
|
|
51
51
|
"@types/randomstring": "^1.1.8",
|
52
52
|
"@types/websql": "^0.0.27",
|
53
53
|
"commander": "^10.0.1",
|
54
|
+
"busboy": "^1.6.0",
|
54
55
|
"deep-freeze": "^0.0.1",
|
55
56
|
"eventemitter3": "^5.0.0",
|
56
57
|
"express-session": "^1.17.3",
|
@@ -58,18 +59,21 @@
|
|
58
59
|
"memoizee": "^0.4.15",
|
59
60
|
"pinejs-client-core": "^6.12.3",
|
60
61
|
"randomstring": "^1.2.3",
|
61
|
-
"
|
62
|
+
"type-is": "^1.6.18",
|
63
|
+
"typed-error": "^3.2.1"
|
62
64
|
},
|
63
65
|
"devDependencies": {
|
64
66
|
"@balena/lint": "^6.2.2",
|
65
67
|
"@faker-js/faker": "^7.6.0",
|
68
|
+
"@types/busboy": "^1.5.0",
|
66
69
|
"@types/chai": "^4.3.4",
|
67
70
|
"@types/chai-as-promised": "^7.1.5",
|
68
71
|
"@types/grunt": "^0.4.27",
|
69
72
|
"@types/mocha": "^10.0.1",
|
70
73
|
"@types/supertest": "^2.0.12",
|
71
74
|
"@types/terser-webpack-plugin": "^5.2.0",
|
72
|
-
"@types/
|
75
|
+
"@types/type-is": "^1.6.3",
|
76
|
+
"@types/webpack": "^5.28.0",
|
73
77
|
"chai": "^4.3.7",
|
74
78
|
"grunt": "^1.6.1",
|
75
79
|
"grunt-check-dependencies": "^1.0.0",
|
@@ -98,13 +102,14 @@
|
|
98
102
|
"webpack-dev-server": "^4.13.3"
|
99
103
|
},
|
100
104
|
"optionalDependencies": {
|
105
|
+
"@aws-sdk/client-s3": "^3.200.0",
|
106
|
+
"@aws-sdk/lib-storage": "^3.200.0",
|
101
107
|
"bcrypt": "^5.1.0",
|
102
108
|
"body-parser": "^1.20.2",
|
103
109
|
"compression": "^1.7.4",
|
104
110
|
"cookie-parser": "^1.4.6",
|
105
111
|
"express": "^4.18.2",
|
106
112
|
"method-override": "^3.0.0",
|
107
|
-
"multer": "1.4.5-lts.1",
|
108
113
|
"mysql": "^2.18.1",
|
109
114
|
"passport": "^0.6.0",
|
110
115
|
"passport-local": "^1.0.0",
|
@@ -134,6 +139,6 @@
|
|
134
139
|
"recursive": true
|
135
140
|
},
|
136
141
|
"versionist": {
|
137
|
-
"publishedAt": "2023-05-
|
142
|
+
"publishedAt": "2023-05-24T14:54:34.406Z"
|
138
143
|
}
|
139
144
|
}
|
@@ -27,6 +27,12 @@ import * as path from 'path';
|
|
27
27
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
28
28
|
|
29
29
|
import * as permissions from '../sbvr-api/permissions';
|
30
|
+
import {
|
31
|
+
getDefaultHandler,
|
32
|
+
getUploaderMiddlware,
|
33
|
+
WebResourceHandler,
|
34
|
+
setupUploadHooks,
|
35
|
+
} from '../server-glue/webresource-handler';
|
30
36
|
import { AliasValidNodeType } from '../sbvr-api/translations';
|
31
37
|
|
32
38
|
export type SetupFunction = (
|
@@ -55,6 +61,7 @@ export interface Model {
|
|
55
61
|
translations?: Dictionary<
|
56
62
|
Definition | Dictionary<string | AliasValidNodeType>
|
57
63
|
>;
|
64
|
+
webResourceHandler?: WebResourceHandler;
|
58
65
|
}
|
59
66
|
export interface User {
|
60
67
|
username: string;
|
@@ -195,15 +202,33 @@ export const setup = (app: Express.Application) => {
|
|
195
202
|
);
|
196
203
|
|
197
204
|
const apiRoute = `/${model.apiRoot}/*`;
|
205
|
+
const webResourceHandler =
|
206
|
+
model.webResourceHandler ?? getDefaultHandler();
|
207
|
+
|
208
|
+
const fileUploadMiddleware =
|
209
|
+
getUploaderMiddlware(webResourceHandler);
|
198
210
|
app
|
199
211
|
.route(apiRoute)
|
200
212
|
.options((_req, res) => res.status(200).end())
|
201
213
|
.get(sbvrUtils.handleODataRequest)
|
202
214
|
.put(sbvrUtils.handleODataRequest)
|
203
|
-
.post(sbvrUtils.handleODataRequest)
|
204
|
-
.patch(sbvrUtils.handleODataRequest)
|
215
|
+
.post(fileUploadMiddleware, sbvrUtils.handleODataRequest)
|
216
|
+
.patch(fileUploadMiddleware, sbvrUtils.handleODataRequest)
|
205
217
|
.merge(sbvrUtils.handleODataRequest)
|
206
218
|
.delete(sbvrUtils.handleODataRequest);
|
219
|
+
app.options(apiRoute, (_req, res) => res.status(200).end());
|
220
|
+
|
221
|
+
const loadedModel = sbvrUtils.getModel(model.apiRoot!);
|
222
|
+
if (translateTo == null) {
|
223
|
+
const resources = Object.entries(
|
224
|
+
loadedModel.abstractSql.tables,
|
225
|
+
).filter(([_resourceName, table]) =>
|
226
|
+
table.fields.some((f) => f.dataType === 'WebResource'),
|
227
|
+
);
|
228
|
+
for (const [resource, _table] of resources) {
|
229
|
+
setupUploadHooks(webResourceHandler, model.apiRoot!, resource);
|
230
|
+
}
|
231
|
+
}
|
207
232
|
|
208
233
|
console.info(
|
209
234
|
'Successfully executed ' + model.modelName + ' model.',
|
@@ -1153,6 +1153,10 @@ const $getAffectedIds = async ({
|
|
1153
1153
|
return result.rows.map((row) => row[idField]);
|
1154
1154
|
};
|
1155
1155
|
|
1156
|
+
export const getModel = (vocabulary: string) => {
|
1157
|
+
return models[vocabulary];
|
1158
|
+
};
|
1159
|
+
|
1156
1160
|
const runODataRequest = (req: Express.Request, vocabulary: string) => {
|
1157
1161
|
if (env.DEBUG) {
|
1158
1162
|
api[vocabulary].logger.log('Parsing', req.method, req.url);
|
@@ -1328,8 +1332,13 @@ const runODataRequest = (req: Express.Request, vocabulary: string) => {
|
|
1328
1332
|
};
|
1329
1333
|
};
|
1330
1334
|
|
1331
|
-
export const
|
1335
|
+
export const getApiRoot = (req: Express.Request): string | undefined => {
|
1332
1336
|
const [, apiRoot] = req.url.split('/', 2);
|
1337
|
+
return apiRoot;
|
1338
|
+
};
|
1339
|
+
|
1340
|
+
export const handleODataRequest: Express.Handler = async (req, res, next) => {
|
1341
|
+
const apiRoot = getApiRoot(req);
|
1333
1342
|
if (apiRoot == null || models[apiRoot] == null) {
|
1334
1343
|
return next('route');
|
1335
1344
|
}
|
@@ -3,7 +3,6 @@ import type * as Compression from 'compression';
|
|
3
3
|
import type * as CookieParser from 'cookie-parser';
|
4
4
|
import type * as ExpressSession from 'express-session';
|
5
5
|
import type * as MethodOverride from 'method-override';
|
6
|
-
import type * as Multer from 'multer';
|
7
6
|
import type * as Passport from 'passport';
|
8
7
|
import type * as Path from 'path';
|
9
8
|
import type * as ServeStatic from 'serve-static';
|
@@ -35,7 +34,6 @@ if (!process.browser) {
|
|
35
34
|
const serveStatic: typeof ServeStatic = require('serve-static');
|
36
35
|
const cookieParser: typeof CookieParser = require('cookie-parser');
|
37
36
|
const bodyParser: typeof BodyParser = require('body-parser');
|
38
|
-
const multer: typeof Multer = require('multer');
|
39
37
|
const methodOverride: typeof MethodOverride = require('method-override');
|
40
38
|
const expressSession: typeof ExpressSession = require('express-session');
|
41
39
|
// tslint:enable:no-var-requires
|
@@ -47,7 +45,6 @@ if (!process.browser) {
|
|
47
45
|
|
48
46
|
app.use(cookieParser());
|
49
47
|
app.use(bodyParser());
|
50
|
-
app.use(multer().any());
|
51
48
|
app.use(methodOverride());
|
52
49
|
app.use(
|
53
50
|
expressSession({
|