@webiny/api-file-manager-s3 0.0.0-mt-3 → 0.0.0-unstable.085ff6572f
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/index.d.ts +1 -1
- package/index.js +1 -6
- package/index.js.map +1 -0
- package/multiPartUpload/CompleteMultiPartUploadUseCase.d.ts +13 -0
- package/multiPartUpload/CompleteMultiPartUploadUseCase.js +65 -0
- package/multiPartUpload/CompleteMultiPartUploadUseCase.js.map +1 -0
- package/multiPartUpload/CreateMultiPartUploadUseCase.d.ts +23 -0
- package/multiPartUpload/CreateMultiPartUploadUseCase.js +47 -0
- package/multiPartUpload/CreateMultiPartUploadUseCase.js.map +1 -0
- package/package.json +19 -15
- package/plugins/checkPermissions.d.ts +4 -0
- package/plugins/checkPermissions.js +42 -0
- package/plugins/checkPermissions.js.map +1 -0
- package/plugins/fileStorageS3.d.ts +1 -1
- package/plugins/fileStorageS3.js +18 -22
- package/plugins/fileStorageS3.js.map +1 -0
- package/plugins/graphqlFileStorageS3.js +127 -57
- package/plugins/graphqlFileStorageS3.js.map +1 -0
- package/types.d.ts +20 -0
- package/types.js +5 -1
- package/types.js.map +1 -0
- package/utils/getPresignedPostPayload.d.ts +3 -11
- package/utils/getPresignedPostPayload.js +12 -52
- package/utils/getPresignedPostPayload.js.map +1 -0
- package/utils/mimeTypes.d.ts +5 -0
- package/utils/mimeTypes.js +20 -0
- package/utils/mimeTypes.js.map +1 -0
- package/utils/prepareFileData.d.ts +2 -0
- package/utils/prepareFileData.js +62 -0
- package/utils/prepareFileData.js.map +1 -0
- package/utils/uploadFileToS3.d.ts +4 -1
- package/utils/uploadFileToS3.js +7 -12
- package/utils/uploadFileToS3.js.map +1 -0
package/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: () => (import("@webiny/handler-graphql/types").GraphQLSchemaPlugin<import("@webiny/api-file-manager/types").FileManagerContext> | import("@webiny/api-file-manager/plugins/
|
|
1
|
+
declare const _default: () => (import("@webiny/handler-graphql/types").GraphQLSchemaPlugin<import("@webiny/api-file-manager/types").FileManagerContext> | import("@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin").FilePhysicalStoragePlugin<import("@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin").FilePhysicalStoragePluginUploadParams, import("@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin").FilePhysicalStoragePluginDeleteParams>)[];
|
|
2
2
|
export default _default;
|
package/index.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
|
|
10
8
|
var _graphqlFileStorageS = _interopRequireDefault(require("./plugins/graphqlFileStorageS3"));
|
|
11
|
-
|
|
12
9
|
var _fileStorageS = _interopRequireDefault(require("./plugins/fileStorageS3"));
|
|
13
|
-
|
|
14
10
|
var _default = () => [(0, _fileStorageS.default)(), _graphqlFileStorageS.default];
|
|
15
|
-
|
|
16
11
|
exports.default = _default;
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["fileStorageS3","graphqlFileStorageS3"],"sources":["index.ts"],"sourcesContent":["import graphqlFileStorageS3 from \"./plugins/graphqlFileStorageS3\";\nimport fileStorageS3 from \"./plugins/fileStorageS3\";\n\nexport default () => [fileStorageS3(), graphqlFileStorageS3];\n"],"mappings":";;;;;;;AAAA;AACA;AAAoD,eAErC,MAAM,CAAC,IAAAA,qBAAa,GAAE,EAAEC,4BAAoB,CAAC;AAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import S3 from "aws-sdk/clients/s3";
|
|
2
|
+
interface CompleteMultiPartUploadParams {
|
|
3
|
+
fileKey: string;
|
|
4
|
+
uploadId: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class CompleteMultiPartUploadUseCase {
|
|
7
|
+
private readonly s3;
|
|
8
|
+
private readonly bucket;
|
|
9
|
+
constructor(bucket: string, s3Client: S3);
|
|
10
|
+
execute(params: CompleteMultiPartUploadParams): Promise<void>;
|
|
11
|
+
private getAllUploadParts;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.CompleteMultiPartUploadUseCase = void 0;
|
|
8
|
+
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
|
|
9
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
+
class CompleteMultiPartUploadUseCase {
|
|
11
|
+
constructor(bucket, s3Client) {
|
|
12
|
+
(0, _defineProperty2.default)(this, "s3", void 0);
|
|
13
|
+
(0, _defineProperty2.default)(this, "bucket", void 0);
|
|
14
|
+
this.bucket = bucket;
|
|
15
|
+
this.s3 = s3Client;
|
|
16
|
+
}
|
|
17
|
+
async execute(params) {
|
|
18
|
+
const uploadParams = {
|
|
19
|
+
Bucket: this.bucket,
|
|
20
|
+
Key: params.fileKey,
|
|
21
|
+
UploadId: params.uploadId
|
|
22
|
+
};
|
|
23
|
+
const allParts = await this.getAllUploadParts(uploadParams);
|
|
24
|
+
const s3Params = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, uploadParams), {}, {
|
|
25
|
+
MultipartUpload: {
|
|
26
|
+
Parts: allParts
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
this.s3.completeMultipartUpload(s3Params, (err, data) => {
|
|
31
|
+
if (err) {
|
|
32
|
+
console.error(err);
|
|
33
|
+
reject(err);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(data);
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async getAllUploadParts(params) {
|
|
42
|
+
const parts = [];
|
|
43
|
+
let marker = undefined;
|
|
44
|
+
while (true) {
|
|
45
|
+
const {
|
|
46
|
+
Parts,
|
|
47
|
+
PartNumberMarker
|
|
48
|
+
} = await this.s3.listParts((0, _objectSpread2.default)((0, _objectSpread2.default)({}, params), {}, {
|
|
49
|
+
PartNumberMarker: marker
|
|
50
|
+
})).promise();
|
|
51
|
+
if (Parts) {
|
|
52
|
+
Parts.forEach(part => parts.push(part));
|
|
53
|
+
}
|
|
54
|
+
marker = PartNumberMarker || undefined;
|
|
55
|
+
if (!marker) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return parts.map(part => ({
|
|
60
|
+
ETag: part.ETag,
|
|
61
|
+
PartNumber: part.PartNumber
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.CompleteMultiPartUploadUseCase = CompleteMultiPartUploadUseCase;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["CompleteMultiPartUploadUseCase","constructor","bucket","s3Client","s3","execute","params","uploadParams","Bucket","Key","fileKey","UploadId","uploadId","allParts","getAllUploadParts","s3Params","MultipartUpload","Parts","Promise","resolve","reject","completeMultipartUpload","err","data","console","error","log","parts","marker","undefined","PartNumberMarker","listParts","promise","forEach","part","push","map","ETag","PartNumber"],"sources":["CompleteMultiPartUploadUseCase.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\n\ninterface CompleteMultiPartUploadParams {\n fileKey: string;\n uploadId: string;\n}\n\ninterface GetAllUploadPartsParams {\n Bucket: string;\n Key: string;\n UploadId: string;\n}\n\nexport class CompleteMultiPartUploadUseCase {\n private readonly s3: S3;\n private readonly bucket: string;\n\n constructor(bucket: string, s3Client: S3) {\n this.bucket = bucket;\n this.s3 = s3Client;\n }\n\n async execute(params: CompleteMultiPartUploadParams) {\n const uploadParams = {\n Bucket: this.bucket,\n Key: params.fileKey,\n UploadId: params.uploadId\n };\n\n const allParts = await this.getAllUploadParts(uploadParams);\n\n const s3Params = {\n ...uploadParams,\n MultipartUpload: {\n Parts: allParts\n }\n };\n\n return new Promise<void>((resolve, reject) => {\n this.s3.completeMultipartUpload(s3Params, (err, data) => {\n if (err) {\n console.error(err);\n reject(err);\n return;\n }\n\n console.log(data);\n resolve();\n });\n });\n }\n\n private async getAllUploadParts(params: GetAllUploadPartsParams) {\n const parts: S3.Parts = [];\n\n let marker: number | undefined = undefined;\n while (true) {\n const { Parts, PartNumberMarker }: S3.ListPartsOutput = await this.s3\n .listParts({\n ...params,\n PartNumberMarker: marker\n })\n .promise();\n\n if (Parts) {\n Parts.forEach(part => parts.push(part));\n }\n\n marker = PartNumberMarker || undefined;\n if (!marker) {\n break;\n }\n }\n\n return parts.map(part => ({\n ETag: part.ETag as string,\n PartNumber: part.PartNumber as number\n }));\n }\n}\n"],"mappings":";;;;;;;;;AAaO,MAAMA,8BAA8B,CAAC;EAIxCC,WAAW,CAACC,MAAc,EAAEC,QAAY,EAAE;IAAA;IAAA;IACtC,IAAI,CAACD,MAAM,GAAGA,MAAM;IACpB,IAAI,CAACE,EAAE,GAAGD,QAAQ;EACtB;EAEA,MAAME,OAAO,CAACC,MAAqC,EAAE;IACjD,MAAMC,YAAY,GAAG;MACjBC,MAAM,EAAE,IAAI,CAACN,MAAM;MACnBO,GAAG,EAAEH,MAAM,CAACI,OAAO;MACnBC,QAAQ,EAAEL,MAAM,CAACM;IACrB,CAAC;IAED,MAAMC,QAAQ,GAAG,MAAM,IAAI,CAACC,iBAAiB,CAACP,YAAY,CAAC;IAE3D,MAAMQ,QAAQ,+DACPR,YAAY;MACfS,eAAe,EAAE;QACbC,KAAK,EAAEJ;MACX;IAAC,EACJ;IAED,OAAO,IAAIK,OAAO,CAAO,CAACC,OAAO,EAAEC,MAAM,KAAK;MAC1C,IAAI,CAAChB,EAAE,CAACiB,uBAAuB,CAACN,QAAQ,EAAE,CAACO,GAAG,EAAEC,IAAI,KAAK;QACrD,IAAID,GAAG,EAAE;UACLE,OAAO,CAACC,KAAK,CAACH,GAAG,CAAC;UAClBF,MAAM,CAACE,GAAG,CAAC;UACX;QACJ;QAEAE,OAAO,CAACE,GAAG,CAACH,IAAI,CAAC;QACjBJ,OAAO,EAAE;MACb,CAAC,CAAC;IACN,CAAC,CAAC;EACN;EAEA,MAAcL,iBAAiB,CAACR,MAA+B,EAAE;IAC7D,MAAMqB,KAAe,GAAG,EAAE;IAE1B,IAAIC,MAA0B,GAAGC,SAAS;IAC1C,OAAO,IAAI,EAAE;MACT,MAAM;QAAEZ,KAAK;QAAEa;MAAqC,CAAC,GAAG,MAAM,IAAI,CAAC1B,EAAE,CAChE2B,SAAS,6DACHzB,MAAM;QACTwB,gBAAgB,EAAEF;MAAM,GAC1B,CACDI,OAAO,EAAE;MAEd,IAAIf,KAAK,EAAE;QACPA,KAAK,CAACgB,OAAO,CAACC,IAAI,IAAIP,KAAK,CAACQ,IAAI,CAACD,IAAI,CAAC,CAAC;MAC3C;MAEAN,MAAM,GAAGE,gBAAgB,IAAID,SAAS;MACtC,IAAI,CAACD,MAAM,EAAE;QACT;MACJ;IACJ;IAEA,OAAOD,KAAK,CAACS,GAAG,CAACF,IAAI,KAAK;MACtBG,IAAI,EAAEH,IAAI,CAACG,IAAc;MACzBC,UAAU,EAAEJ,IAAI,CAACI;IACrB,CAAC,CAAC,CAAC;EACP;AACJ;AAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import S3 from "aws-sdk/clients/s3";
|
|
2
|
+
interface CreateMultiPartUploadParams {
|
|
3
|
+
file: {
|
|
4
|
+
name: string;
|
|
5
|
+
type: string;
|
|
6
|
+
size: number;
|
|
7
|
+
};
|
|
8
|
+
numberOfParts: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class CreateMultiPartUploadUseCase {
|
|
11
|
+
private readonly s3;
|
|
12
|
+
private readonly bucket;
|
|
13
|
+
constructor(bucket: string, s3Client: S3);
|
|
14
|
+
execute(params: CreateMultiPartUploadParams): Promise<{
|
|
15
|
+
file: import("../types").FileData;
|
|
16
|
+
uploadId: string | undefined;
|
|
17
|
+
parts: {
|
|
18
|
+
url: string;
|
|
19
|
+
partNumber: number;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.CreateMultiPartUploadUseCase = void 0;
|
|
8
|
+
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
|
|
9
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
+
var _prepareFileData = require("../utils/prepareFileData");
|
|
11
|
+
class CreateMultiPartUploadUseCase {
|
|
12
|
+
constructor(bucket, s3Client) {
|
|
13
|
+
(0, _defineProperty2.default)(this, "s3", void 0);
|
|
14
|
+
(0, _defineProperty2.default)(this, "bucket", void 0);
|
|
15
|
+
this.bucket = bucket;
|
|
16
|
+
this.s3 = s3Client;
|
|
17
|
+
}
|
|
18
|
+
async execute(params) {
|
|
19
|
+
const file = (0, _prepareFileData.prepareFileData)(params.file);
|
|
20
|
+
const s3Params = {
|
|
21
|
+
Bucket: this.bucket,
|
|
22
|
+
Key: file.key
|
|
23
|
+
};
|
|
24
|
+
const {
|
|
25
|
+
UploadId
|
|
26
|
+
} = await this.s3.createMultipartUpload(s3Params).promise();
|
|
27
|
+
const parts = await Promise.all(Array.from({
|
|
28
|
+
length: params.numberOfParts
|
|
29
|
+
}).map((_, index) => {
|
|
30
|
+
return this.s3.getSignedUrlPromise("uploadPart", (0, _objectSpread2.default)((0, _objectSpread2.default)({}, s3Params), {}, {
|
|
31
|
+
UploadId,
|
|
32
|
+
PartNumber: index + 1,
|
|
33
|
+
// URL expires after 24 hours.
|
|
34
|
+
Expires: 86400
|
|
35
|
+
})).then(url => ({
|
|
36
|
+
url,
|
|
37
|
+
partNumber: index + 1
|
|
38
|
+
}));
|
|
39
|
+
}));
|
|
40
|
+
return {
|
|
41
|
+
file,
|
|
42
|
+
uploadId: UploadId,
|
|
43
|
+
parts
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.CreateMultiPartUploadUseCase = CreateMultiPartUploadUseCase;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["CreateMultiPartUploadUseCase","constructor","bucket","s3Client","s3","execute","params","file","prepareFileData","s3Params","Bucket","Key","key","UploadId","createMultipartUpload","promise","parts","Promise","all","Array","from","length","numberOfParts","map","_","index","getSignedUrlPromise","PartNumber","Expires","then","url","partNumber","uploadId"],"sources":["CreateMultiPartUploadUseCase.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\nimport { prepareFileData } from \"~/utils/prepareFileData\";\n\ninterface CreateMultiPartUploadParams {\n file: {\n name: string;\n type: string;\n size: number;\n };\n numberOfParts: number;\n}\n\nexport class CreateMultiPartUploadUseCase {\n private readonly s3: S3;\n private readonly bucket: string;\n\n constructor(bucket: string, s3Client: S3) {\n this.bucket = bucket;\n this.s3 = s3Client;\n }\n\n async execute(params: CreateMultiPartUploadParams) {\n const file = prepareFileData(params.file);\n\n const s3Params = { Bucket: this.bucket, Key: file.key };\n\n const { UploadId } = await this.s3.createMultipartUpload(s3Params).promise();\n\n const parts = await Promise.all(\n Array.from({ length: params.numberOfParts }).map((_, index) => {\n return this.s3\n .getSignedUrlPromise(\"uploadPart\", {\n ...s3Params,\n UploadId,\n PartNumber: index + 1,\n // URL expires after 24 hours.\n Expires: 86400\n })\n .then(url => ({\n url,\n partNumber: index + 1\n }));\n })\n );\n\n return {\n file,\n uploadId: UploadId,\n parts\n };\n }\n}\n"],"mappings":";;;;;;;;;AACA;AAWO,MAAMA,4BAA4B,CAAC;EAItCC,WAAW,CAACC,MAAc,EAAEC,QAAY,EAAE;IAAA;IAAA;IACtC,IAAI,CAACD,MAAM,GAAGA,MAAM;IACpB,IAAI,CAACE,EAAE,GAAGD,QAAQ;EACtB;EAEA,MAAME,OAAO,CAACC,MAAmC,EAAE;IAC/C,MAAMC,IAAI,GAAG,IAAAC,gCAAe,EAACF,MAAM,CAACC,IAAI,CAAC;IAEzC,MAAME,QAAQ,GAAG;MAAEC,MAAM,EAAE,IAAI,CAACR,MAAM;MAAES,GAAG,EAAEJ,IAAI,CAACK;IAAI,CAAC;IAEvD,MAAM;MAAEC;IAAS,CAAC,GAAG,MAAM,IAAI,CAACT,EAAE,CAACU,qBAAqB,CAACL,QAAQ,CAAC,CAACM,OAAO,EAAE;IAE5E,MAAMC,KAAK,GAAG,MAAMC,OAAO,CAACC,GAAG,CAC3BC,KAAK,CAACC,IAAI,CAAC;MAAEC,MAAM,EAAEf,MAAM,CAACgB;IAAc,CAAC,CAAC,CAACC,GAAG,CAAC,CAACC,CAAC,EAAEC,KAAK,KAAK;MAC3D,OAAO,IAAI,CAACrB,EAAE,CACTsB,mBAAmB,CAAC,YAAY,8DAC1BjB,QAAQ;QACXI,QAAQ;QACRc,UAAU,EAAEF,KAAK,GAAG,CAAC;QACrB;QACAG,OAAO,EAAE;MAAK,GAChB,CACDC,IAAI,CAACC,GAAG,KAAK;QACVA,GAAG;QACHC,UAAU,EAAEN,KAAK,GAAG;MACxB,CAAC,CAAC,CAAC;IACX,CAAC,CAAC,CACL;IAED,OAAO;MACHlB,IAAI;MACJyB,QAAQ,EAAEnB,QAAQ;MAClBG;IACJ,CAAC;EACL;AACJ;AAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-file-manager-s3",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-unstable.085ff6572f",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,21 +10,25 @@
|
|
|
10
10
|
"author": "Webiny Ltd",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@webiny/api-file-manager": "0.0.0-
|
|
14
|
-
"@webiny/
|
|
15
|
-
"@webiny/
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
13
|
+
"@webiny/api-file-manager": "0.0.0-unstable.085ff6572f",
|
|
14
|
+
"@webiny/api-security": "0.0.0-unstable.085ff6572f",
|
|
15
|
+
"@webiny/error": "0.0.0-unstable.085ff6572f",
|
|
16
|
+
"@webiny/handler-graphql": "0.0.0-unstable.085ff6572f",
|
|
17
|
+
"@webiny/utils": "0.0.0-unstable.085ff6572f",
|
|
18
|
+
"@webiny/validation": "0.0.0-unstable.085ff6572f",
|
|
19
|
+
"form-data": "4.0.0",
|
|
20
|
+
"mime": "3.0.0",
|
|
21
|
+
"node-fetch": "2.6.9",
|
|
22
|
+
"sanitize-filename": "1.6.3"
|
|
20
23
|
},
|
|
21
24
|
"devDependencies": {
|
|
22
|
-
"@babel/cli": "
|
|
23
|
-
"@babel/core": "
|
|
24
|
-
"@
|
|
25
|
-
"@webiny/
|
|
26
|
-
"
|
|
27
|
-
"
|
|
25
|
+
"@babel/cli": "7.20.7",
|
|
26
|
+
"@babel/core": "7.20.12",
|
|
27
|
+
"@types/node-fetch": "2.6.2",
|
|
28
|
+
"@webiny/cli": "0.0.0-unstable.085ff6572f",
|
|
29
|
+
"@webiny/project-utils": "0.0.0-unstable.085ff6572f",
|
|
30
|
+
"rimraf": "3.0.2",
|
|
31
|
+
"typescript": "4.7.4"
|
|
28
32
|
},
|
|
29
33
|
"publishConfig": {
|
|
30
34
|
"access": "public",
|
|
@@ -34,5 +38,5 @@
|
|
|
34
38
|
"build": "yarn webiny run build",
|
|
35
39
|
"watch": "yarn webiny run watch"
|
|
36
40
|
},
|
|
37
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "085ff6572f6bb6a76d218088b06d9f4ef92bbea7"
|
|
38
42
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkPermissions = void 0;
|
|
7
|
+
var _apiSecurity = require("@webiny/api-security");
|
|
8
|
+
const checkPermissions = async (context, check = {}) => {
|
|
9
|
+
const filePermissions = await context.security.getPermissions("fm.file");
|
|
10
|
+
const relevantFilePermissions = filePermissions.filter(current => {
|
|
11
|
+
if (check.rwd && !hasRwd(current, check.rwd)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
});
|
|
16
|
+
if (relevantFilePermissions.length === 0) {
|
|
17
|
+
throw new _apiSecurity.NotAuthorizedError();
|
|
18
|
+
}
|
|
19
|
+
return relevantFilePermissions;
|
|
20
|
+
};
|
|
21
|
+
exports.checkPermissions = checkPermissions;
|
|
22
|
+
const hasRwd = (filesFilePermissions, rwd) => {
|
|
23
|
+
if (!Array.isArray(filesFilePermissions)) {
|
|
24
|
+
filesFilePermissions = [filesFilePermissions];
|
|
25
|
+
}
|
|
26
|
+
if (!rwd) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Is there a permission that doesn't restrict RWD permissions, that means all RWD permissions are allowed.
|
|
31
|
+
const permissionWithoutRwdRestrictions = filesFilePermissions.some(permission => {
|
|
32
|
+
return typeof permission.rwd !== "string";
|
|
33
|
+
});
|
|
34
|
+
if (permissionWithoutRwdRestrictions) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If there is no permission that doesn't restrict RWD permissions, that means we need to check if the RWD.
|
|
39
|
+
return filesFilePermissions.some(permission => {
|
|
40
|
+
return permission.rwd && permission.rwd.includes(rwd);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["checkPermissions","context","check","filePermissions","security","getPermissions","relevantFilePermissions","filter","current","rwd","hasRwd","length","NotAuthorizedError","filesFilePermissions","Array","isArray","permissionWithoutRwdRestrictions","some","permission","includes"],"sources":["checkPermissions.ts"],"sourcesContent":["import { FileManagerContext, FilePermission } from \"@webiny/api-file-manager/types\";\nimport { NotAuthorizedError } from \"@webiny/api-security\";\n\nexport const checkPermissions = async (\n context: FileManagerContext,\n check: { rwd?: string } = {}\n) => {\n const filePermissions = await context.security.getPermissions<FilePermission>(\"fm.file\");\n\n const relevantFilePermissions = filePermissions.filter(current => {\n if (check.rwd && !hasRwd(current, check.rwd)) {\n return false;\n }\n\n return true;\n });\n\n if (relevantFilePermissions.length === 0) {\n throw new NotAuthorizedError();\n }\n\n return relevantFilePermissions;\n};\n\nconst hasRwd = (filesFilePermissions: FilePermission | FilePermission[], rwd: string): boolean => {\n if (!Array.isArray(filesFilePermissions)) {\n filesFilePermissions = [filesFilePermissions];\n }\n\n if (!rwd) {\n return true;\n }\n\n // Is there a permission that doesn't restrict RWD permissions, that means all RWD permissions are allowed.\n const permissionWithoutRwdRestrictions = filesFilePermissions.some(permission => {\n return typeof permission.rwd !== \"string\";\n });\n\n if (permissionWithoutRwdRestrictions) {\n return true;\n }\n\n // If there is no permission that doesn't restrict RWD permissions, that means we need to check if the RWD.\n return filesFilePermissions.some(permission => {\n return permission.rwd && permission.rwd.includes(rwd);\n });\n};\n"],"mappings":";;;;;;AACA;AAEO,MAAMA,gBAAgB,GAAG,OAC5BC,OAA2B,EAC3BC,KAAuB,GAAG,CAAC,CAAC,KAC3B;EACD,MAAMC,eAAe,GAAG,MAAMF,OAAO,CAACG,QAAQ,CAACC,cAAc,CAAiB,SAAS,CAAC;EAExF,MAAMC,uBAAuB,GAAGH,eAAe,CAACI,MAAM,CAACC,OAAO,IAAI;IAC9D,IAAIN,KAAK,CAACO,GAAG,IAAI,CAACC,MAAM,CAACF,OAAO,EAAEN,KAAK,CAACO,GAAG,CAAC,EAAE;MAC1C,OAAO,KAAK;IAChB;IAEA,OAAO,IAAI;EACf,CAAC,CAAC;EAEF,IAAIH,uBAAuB,CAACK,MAAM,KAAK,CAAC,EAAE;IACtC,MAAM,IAAIC,+BAAkB,EAAE;EAClC;EAEA,OAAON,uBAAuB;AAClC,CAAC;AAAC;AAEF,MAAMI,MAAM,GAAG,CAACG,oBAAuD,EAAEJ,GAAW,KAAc;EAC9F,IAAI,CAACK,KAAK,CAACC,OAAO,CAACF,oBAAoB,CAAC,EAAE;IACtCA,oBAAoB,GAAG,CAACA,oBAAoB,CAAC;EACjD;EAEA,IAAI,CAACJ,GAAG,EAAE;IACN,OAAO,IAAI;EACf;;EAEA;EACA,MAAMO,gCAAgC,GAAGH,oBAAoB,CAACI,IAAI,CAACC,UAAU,IAAI;IAC7E,OAAO,OAAOA,UAAU,CAACT,GAAG,KAAK,QAAQ;EAC7C,CAAC,CAAC;EAEF,IAAIO,gCAAgC,EAAE;IAClC,OAAO,IAAI;EACf;;EAEA;EACA,OAAOH,oBAAoB,CAACI,IAAI,CAACC,UAAU,IAAI;IAC3C,OAAOA,UAAU,CAACT,GAAG,IAAIS,UAAU,CAACT,GAAG,CAACU,QAAQ,CAACV,GAAG,CAAC;EACzD,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { FilePhysicalStoragePlugin } from "@webiny/api-file-manager/plugins/
|
|
1
|
+
import { FilePhysicalStoragePlugin } from "@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin";
|
|
2
2
|
declare const _default: () => FilePhysicalStoragePlugin;
|
|
3
3
|
export default _default;
|
package/plugins/fileStorageS3.js
CHANGED
|
@@ -1,53 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
|
|
10
8
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
11
|
-
|
|
12
9
|
var _s = _interopRequireDefault(require("aws-sdk/clients/s3"));
|
|
13
|
-
|
|
14
|
-
var _getPresignedPostPayload = _interopRequireDefault(require("../utils/getPresignedPostPayload"));
|
|
15
|
-
|
|
10
|
+
var _getPresignedPostPayload = require("../utils/getPresignedPostPayload");
|
|
16
11
|
var _uploadFileToS = _interopRequireDefault(require("../utils/uploadFileToS3"));
|
|
17
|
-
|
|
18
|
-
var _FilePhysicalStoragePlugin = require("@webiny/api-file-manager/plugins/definitions/FilePhysicalStoragePlugin");
|
|
19
|
-
|
|
12
|
+
var _FilePhysicalStoragePlugin = require("@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin");
|
|
20
13
|
const _excluded = ["settings", "buffer"];
|
|
21
14
|
const S3_BUCKET = process.env.S3_BUCKET;
|
|
22
|
-
|
|
23
15
|
var _default = () => {
|
|
16
|
+
/**
|
|
17
|
+
* We need to extend the type for FilePhysicalStoragePlugin.
|
|
18
|
+
* Otherwise, the `getPresignedPostPayload` doesn't know it has all required values in params.
|
|
19
|
+
*/
|
|
24
20
|
return new _FilePhysicalStoragePlugin.FilePhysicalStoragePlugin({
|
|
25
|
-
upload: async
|
|
21
|
+
upload: async params => {
|
|
26
22
|
const {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
settings,
|
|
24
|
+
buffer
|
|
25
|
+
} = params,
|
|
26
|
+
data = (0, _objectWithoutProperties2.default)(params, _excluded);
|
|
31
27
|
const {
|
|
32
28
|
data: preSignedPostPayload,
|
|
33
29
|
file
|
|
34
|
-
} = await (0, _getPresignedPostPayload.
|
|
30
|
+
} = await (0, _getPresignedPostPayload.getPresignedPostPayload)(data, settings);
|
|
35
31
|
const response = await (0, _uploadFileToS.default)(buffer, preSignedPostPayload);
|
|
36
|
-
|
|
37
32
|
if (!response.ok) {
|
|
38
33
|
throw Error("Unable to upload file.");
|
|
39
34
|
}
|
|
40
|
-
|
|
41
35
|
return {
|
|
42
36
|
data: preSignedPostPayload,
|
|
43
37
|
file
|
|
44
38
|
};
|
|
45
39
|
},
|
|
46
|
-
delete: async
|
|
40
|
+
delete: async params => {
|
|
47
41
|
const {
|
|
48
42
|
key
|
|
49
|
-
} =
|
|
43
|
+
} = params;
|
|
50
44
|
const s3 = new _s.default();
|
|
45
|
+
if (!key || !S3_BUCKET) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
51
48
|
await s3.deleteObject({
|
|
52
49
|
Bucket: S3_BUCKET,
|
|
53
50
|
Key: key
|
|
@@ -55,5 +52,4 @@ var _default = () => {
|
|
|
55
52
|
}
|
|
56
53
|
});
|
|
57
54
|
};
|
|
58
|
-
|
|
59
55
|
exports.default = _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["S3_BUCKET","process","env","FilePhysicalStoragePlugin","upload","params","settings","buffer","data","preSignedPostPayload","file","getPresignedPostPayload","response","uploadFileToS3","ok","Error","delete","key","s3","S3","deleteObject","Bucket","Key","promise"],"sources":["fileStorageS3.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\nimport { getPresignedPostPayload } from \"~/utils/getPresignedPostPayload\";\nimport uploadFileToS3 from \"../utils/uploadFileToS3\";\nimport { FilePhysicalStoragePlugin } from \"@webiny/api-file-manager/plugins/FilePhysicalStoragePlugin\";\nimport { PresignedPostPayloadData } from \"~/types\";\n\nconst S3_BUCKET = process.env.S3_BUCKET;\n\nexport default (): FilePhysicalStoragePlugin => {\n /**\n * We need to extend the type for FilePhysicalStoragePlugin.\n * Otherwise, the `getPresignedPostPayload` doesn't know it has all required values in params.\n */\n return new FilePhysicalStoragePlugin({\n upload: async params => {\n const { settings, buffer, ...data } = params;\n\n const { data: preSignedPostPayload, file } = await getPresignedPostPayload(\n data as PresignedPostPayloadData,\n settings\n );\n\n const response = await uploadFileToS3(buffer, preSignedPostPayload);\n if (!response.ok) {\n throw Error(\"Unable to upload file.\");\n }\n\n return {\n data: preSignedPostPayload,\n file\n };\n },\n delete: async params => {\n const { key } = params;\n const s3 = new S3();\n\n if (!key || !S3_BUCKET) {\n return;\n }\n\n await s3\n .deleteObject({\n Bucket: S3_BUCKET,\n Key: key\n })\n .promise();\n }\n });\n};\n"],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AAAuG;AAGvG,MAAMA,SAAS,GAAGC,OAAO,CAACC,GAAG,CAACF,SAAS;AAAC,eAEzB,MAAiC;EAC5C;AACJ;AACA;AACA;EACI,OAAO,IAAIG,oDAAyB,CAAC;IACjCC,MAAM,EAAE,MAAMC,MAAM,IAAI;MACpB,MAAM;UAAEC,QAAQ;UAAEC;QAAgB,CAAC,GAAGF,MAAM;QAAfG,IAAI,0CAAKH,MAAM;MAE5C,MAAM;QAAEG,IAAI,EAAEC,oBAAoB;QAAEC;MAAK,CAAC,GAAG,MAAM,IAAAC,gDAAuB,EACtEH,IAAI,EACJF,QAAQ,CACX;MAED,MAAMM,QAAQ,GAAG,MAAM,IAAAC,sBAAc,EAACN,MAAM,EAAEE,oBAAoB,CAAC;MACnE,IAAI,CAACG,QAAQ,CAACE,EAAE,EAAE;QACd,MAAMC,KAAK,CAAC,wBAAwB,CAAC;MACzC;MAEA,OAAO;QACHP,IAAI,EAAEC,oBAAoB;QAC1BC;MACJ,CAAC;IACL,CAAC;IACDM,MAAM,EAAE,MAAMX,MAAM,IAAI;MACpB,MAAM;QAAEY;MAAI,CAAC,GAAGZ,MAAM;MACtB,MAAMa,EAAE,GAAG,IAAIC,UAAE,EAAE;MAEnB,IAAI,CAACF,GAAG,IAAI,CAACjB,SAAS,EAAE;QACpB;MACJ;MAEA,MAAMkB,EAAE,CACHE,YAAY,CAAC;QACVC,MAAM,EAAErB,SAAS;QACjBsB,GAAG,EAAEL;MACT,CAAC,CAAC,CACDM,OAAO,EAAE;IAClB;EACJ,CAAC,CAAC;AACN,CAAC;AAAA"}
|
|
@@ -1,53 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
|
|
8
|
+
var _s = _interopRequireDefault(require("aws-sdk/clients/s3"));
|
|
10
9
|
var _responses = require("@webiny/handler-graphql/responses");
|
|
11
|
-
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
const BATCH_UPLOAD_MAX_FILES = 20;
|
|
10
|
+
var _getPresignedPostPayload = require("../utils/getPresignedPostPayload");
|
|
11
|
+
var _error = _interopRequireDefault(require("@webiny/error"));
|
|
12
|
+
var _checkPermissions = require("./checkPermissions");
|
|
13
|
+
var _CreateMultiPartUploadUseCase = require("../multiPartUpload/CreateMultiPartUploadUseCase");
|
|
14
|
+
var _CompleteMultiPartUploadUseCase = require("../multiPartUpload/CompleteMultiPartUploadUseCase");
|
|
17
15
|
const plugin = {
|
|
18
16
|
type: "graphql-schema",
|
|
19
17
|
name: "graphql-schema-api-file-manager-s3",
|
|
20
18
|
schema: {
|
|
21
|
-
typeDefs:
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
typeDefs: /* GraphQL */`
|
|
20
|
+
type UploadFileResponseDataFile {
|
|
21
|
+
id: ID!
|
|
22
|
+
name: String!
|
|
23
|
+
type: String!
|
|
24
|
+
size: Long!
|
|
25
|
+
key: String!
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
input PreSignedPostPayloadInput {
|
|
25
29
|
name: String!
|
|
26
30
|
type: String!
|
|
27
|
-
size:
|
|
31
|
+
size: Long!
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
type GetPreSignedPostPayloadResponseDataFile {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
id: ID!
|
|
36
|
+
name: String!
|
|
37
|
+
type: String!
|
|
38
|
+
size: Long!
|
|
39
|
+
key: String!
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
type GetPreSignedPostPayloadResponseData {
|
|
38
43
|
# Contains data that is necessary for initiating a file upload.
|
|
39
|
-
data: JSON
|
|
40
|
-
file: UploadFileResponseDataFile
|
|
44
|
+
data: JSON!
|
|
45
|
+
file: UploadFileResponseDataFile!
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
type GetPreSignedPostPayloadResponse {
|
|
44
|
-
error:
|
|
49
|
+
error: FmError
|
|
45
50
|
data: GetPreSignedPostPayloadResponseData
|
|
46
51
|
}
|
|
47
52
|
|
|
53
|
+
type MultiPartUploadFilePart {
|
|
54
|
+
partNumber: Int!
|
|
55
|
+
url: String!
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type CreateMultiPartUploadResponseData {
|
|
59
|
+
file: GetPreSignedPostPayloadResponseDataFile!
|
|
60
|
+
uploadId: String!
|
|
61
|
+
parts: [MultiPartUploadFilePart!]!
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type CompleteMultiPartUploadResponse {
|
|
65
|
+
data: Boolean
|
|
66
|
+
error: FmError
|
|
67
|
+
}
|
|
68
|
+
|
|
48
69
|
type GetPreSignedPostPayloadsResponse {
|
|
49
|
-
error:
|
|
50
|
-
data: [GetPreSignedPostPayloadResponseData]!
|
|
70
|
+
error: FmError
|
|
71
|
+
data: [GetPreSignedPostPayloadResponseData!]!
|
|
51
72
|
}
|
|
52
73
|
|
|
53
74
|
extend type FmQuery {
|
|
@@ -58,20 +79,44 @@ const plugin = {
|
|
|
58
79
|
data: [PreSignedPostPayloadInput]!
|
|
59
80
|
): GetPreSignedPostPayloadsResponse
|
|
60
81
|
}
|
|
82
|
+
|
|
83
|
+
type CreateMultiPartUploadResponse {
|
|
84
|
+
data: CreateMultiPartUploadResponseData
|
|
85
|
+
error: FmError
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
input MultiPartUploadFilePartInput {
|
|
89
|
+
partNumber: Int!
|
|
90
|
+
etag: String!
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
extend type FmMutation {
|
|
94
|
+
createMultiPartUpload(
|
|
95
|
+
data: PreSignedPostPayloadInput!
|
|
96
|
+
numberOfParts: Number!
|
|
97
|
+
): CreateMultiPartUploadResponse
|
|
98
|
+
|
|
99
|
+
completeMultiPartUpload(
|
|
100
|
+
fileKey: String!
|
|
101
|
+
uploadId: String!
|
|
102
|
+
): CompleteMultiPartUploadResponse
|
|
103
|
+
}
|
|
61
104
|
`,
|
|
62
105
|
resolvers: {
|
|
63
106
|
FmQuery: {
|
|
64
107
|
getPreSignedPostPayload: async (_, args, context) => {
|
|
65
108
|
try {
|
|
66
|
-
await (0,
|
|
109
|
+
await (0, _checkPermissions.checkPermissions)(context, {
|
|
67
110
|
rwd: "w"
|
|
68
111
|
});
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
112
|
+
const file = args.data;
|
|
113
|
+
const settings = await context.fileManager.getSettings();
|
|
114
|
+
if (!settings) {
|
|
115
|
+
throw new _error.default("Missing File Manager Settings.", "FILE_MANAGER_SETTINGS_ERROR", {
|
|
116
|
+
file
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return new _responses.Response((0, _getPresignedPostPayload.getPresignedPostPayload)(file, settings));
|
|
75
120
|
} catch (e) {
|
|
76
121
|
return new _responses.ErrorResponse({
|
|
77
122
|
message: e.message,
|
|
@@ -81,44 +126,69 @@ const plugin = {
|
|
|
81
126
|
}
|
|
82
127
|
},
|
|
83
128
|
getPreSignedPostPayloads: async (_, args, context) => {
|
|
84
|
-
await (0,
|
|
129
|
+
await (0, _checkPermissions.checkPermissions)(context, {
|
|
85
130
|
rwd: "w"
|
|
86
131
|
});
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
132
|
+
const files = args.data;
|
|
133
|
+
try {
|
|
134
|
+
const settings = await context.fileManager.getSettings();
|
|
135
|
+
if (!settings) {
|
|
136
|
+
throw new _error.default("Missing File Manager Settings.", "FILE_MANAGER_SETTINGS_ERROR", {
|
|
137
|
+
files
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const presignedPayloads = files.map(file => {
|
|
141
|
+
return (0, _getPresignedPostPayload.getPresignedPostPayload)(file, settings);
|
|
95
142
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (files.length === 0) {
|
|
143
|
+
return new _responses.Response(presignedPayloads);
|
|
144
|
+
} catch (e) {
|
|
99
145
|
return new _responses.ErrorResponse({
|
|
100
|
-
|
|
101
|
-
|
|
146
|
+
message: e.message,
|
|
147
|
+
code: e.code,
|
|
148
|
+
data: e.data
|
|
102
149
|
});
|
|
103
150
|
}
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
FmMutation: {
|
|
154
|
+
createMultiPartUpload: async (_, args, context) => {
|
|
155
|
+
await (0, _checkPermissions.checkPermissions)(context, {
|
|
156
|
+
rwd: "w"
|
|
157
|
+
});
|
|
158
|
+
const s3Client = new _s.default({
|
|
159
|
+
region: process.env.AWS_REGION,
|
|
160
|
+
signatureVersion: "v4"
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
const useCase = new _CreateMultiPartUploadUseCase.CreateMultiPartUploadUseCase(String(process.env.S3_BUCKET), s3Client);
|
|
164
|
+
const multiPartUpload = await useCase.execute({
|
|
165
|
+
file: args.data,
|
|
166
|
+
numberOfParts: args.numberOfParts
|
|
167
|
+
});
|
|
168
|
+
return new _responses.Response(multiPartUpload);
|
|
169
|
+
} catch (e) {
|
|
106
170
|
return new _responses.ErrorResponse({
|
|
107
|
-
|
|
108
|
-
|
|
171
|
+
message: e.message,
|
|
172
|
+
code: e.code,
|
|
173
|
+
data: e.data
|
|
109
174
|
});
|
|
110
175
|
}
|
|
111
|
-
|
|
176
|
+
},
|
|
177
|
+
completeMultiPartUpload: async (_, args, context) => {
|
|
178
|
+
await (0, _checkPermissions.checkPermissions)(context, {
|
|
179
|
+
rwd: "w"
|
|
180
|
+
});
|
|
181
|
+
const s3Client = new _s.default({
|
|
182
|
+
region: process.env.AWS_REGION,
|
|
183
|
+
signatureVersion: "v4"
|
|
184
|
+
});
|
|
112
185
|
try {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return new _responses.Response(await Promise.all(promises));
|
|
186
|
+
const useCase = new _CompleteMultiPartUploadUseCase.CompleteMultiPartUploadUseCase(String(process.env.S3_BUCKET), s3Client);
|
|
187
|
+
await useCase.execute({
|
|
188
|
+
fileKey: args.fileKey,
|
|
189
|
+
uploadId: args.uploadId
|
|
190
|
+
});
|
|
191
|
+
return new _responses.Response(true);
|
|
122
192
|
} catch (e) {
|
|
123
193
|
return new _responses.ErrorResponse({
|
|
124
194
|
message: e.message,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["plugin","type","name","schema","typeDefs","resolvers","FmQuery","getPreSignedPostPayload","_","args","context","checkPermissions","rwd","file","data","settings","fileManager","getSettings","WebinyError","Response","getPresignedPostPayload","e","ErrorResponse","message","code","getPreSignedPostPayloads","files","presignedPayloads","map","FmMutation","createMultiPartUpload","s3Client","S3","region","process","env","AWS_REGION","signatureVersion","useCase","CreateMultiPartUploadUseCase","String","S3_BUCKET","multiPartUpload","execute","numberOfParts","completeMultiPartUpload","CompleteMultiPartUploadUseCase","fileKey","uploadId"],"sources":["graphqlFileStorageS3.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\nimport { GraphQLSchemaPlugin } from \"@webiny/handler-graphql/types\";\nimport { ErrorResponse, Response } from \"@webiny/handler-graphql/responses\";\nimport { FileManagerContext } from \"@webiny/api-file-manager/types\";\nimport { getPresignedPostPayload } from \"~/utils/getPresignedPostPayload\";\nimport WebinyError from \"@webiny/error\";\nimport { checkPermissions } from \"~/plugins/checkPermissions\";\nimport { PresignedPostPayloadData } from \"~/types\";\nimport { CreateMultiPartUploadUseCase } from \"~/multiPartUpload/CreateMultiPartUploadUseCase\";\nimport { CompleteMultiPartUploadUseCase } from \"~/multiPartUpload/CompleteMultiPartUploadUseCase\";\n\nconst plugin: GraphQLSchemaPlugin<FileManagerContext> = {\n type: \"graphql-schema\",\n name: \"graphql-schema-api-file-manager-s3\",\n schema: {\n typeDefs: /* GraphQL */ `\n type UploadFileResponseDataFile {\n id: ID!\n name: String!\n type: String!\n size: Long!\n key: String!\n }\n\n input PreSignedPostPayloadInput {\n name: String!\n type: String!\n size: Long!\n }\n\n type GetPreSignedPostPayloadResponseDataFile {\n id: ID!\n name: String!\n type: String!\n size: Long!\n key: String!\n }\n\n type GetPreSignedPostPayloadResponseData {\n # Contains data that is necessary for initiating a file upload.\n data: JSON!\n file: UploadFileResponseDataFile!\n }\n\n type GetPreSignedPostPayloadResponse {\n error: FmError\n data: GetPreSignedPostPayloadResponseData\n }\n\n type MultiPartUploadFilePart {\n partNumber: Int!\n url: String!\n }\n\n type CreateMultiPartUploadResponseData {\n file: GetPreSignedPostPayloadResponseDataFile!\n uploadId: String!\n parts: [MultiPartUploadFilePart!]!\n }\n\n type CompleteMultiPartUploadResponse {\n data: Boolean\n error: FmError\n }\n\n type GetPreSignedPostPayloadsResponse {\n error: FmError\n data: [GetPreSignedPostPayloadResponseData!]!\n }\n\n extend type FmQuery {\n getPreSignedPostPayload(\n data: PreSignedPostPayloadInput!\n ): GetPreSignedPostPayloadResponse\n getPreSignedPostPayloads(\n data: [PreSignedPostPayloadInput]!\n ): GetPreSignedPostPayloadsResponse\n }\n\n type CreateMultiPartUploadResponse {\n data: CreateMultiPartUploadResponseData\n error: FmError\n }\n\n input MultiPartUploadFilePartInput {\n partNumber: Int!\n etag: String!\n }\n\n extend type FmMutation {\n createMultiPartUpload(\n data: PreSignedPostPayloadInput!\n numberOfParts: Number!\n ): CreateMultiPartUploadResponse\n\n completeMultiPartUpload(\n fileKey: String!\n uploadId: String!\n ): CompleteMultiPartUploadResponse\n }\n `,\n resolvers: {\n FmQuery: {\n getPreSignedPostPayload: async (_, args: any, context) => {\n try {\n await checkPermissions(context, { rwd: \"w\" });\n\n const file = args.data as PresignedPostPayloadData;\n\n const settings = await context.fileManager.getSettings();\n if (!settings) {\n throw new WebinyError(\n \"Missing File Manager Settings.\",\n \"FILE_MANAGER_SETTINGS_ERROR\",\n { file }\n );\n }\n\n return new Response(getPresignedPostPayload(file, settings));\n } catch (e) {\n return new ErrorResponse({\n message: e.message,\n code: e.code,\n data: e.data\n });\n }\n },\n getPreSignedPostPayloads: async (_, args, context) => {\n await checkPermissions(context, { rwd: \"w\" });\n\n const files = args.data as PresignedPostPayloadData[];\n\n try {\n const settings = await context.fileManager.getSettings();\n if (!settings) {\n throw new WebinyError(\n \"Missing File Manager Settings.\",\n \"FILE_MANAGER_SETTINGS_ERROR\",\n { files }\n );\n }\n\n const presignedPayloads = files.map(file => {\n return getPresignedPostPayload(file, settings);\n });\n\n return new Response(presignedPayloads);\n } catch (e) {\n return new ErrorResponse({\n message: e.message,\n code: e.code,\n data: e.data\n });\n }\n }\n },\n FmMutation: {\n createMultiPartUpload: async (_, args, context) => {\n await checkPermissions(context, { rwd: \"w\" });\n\n const s3Client = new S3({\n region: process.env.AWS_REGION,\n signatureVersion: \"v4\"\n });\n\n try {\n const useCase = new CreateMultiPartUploadUseCase(\n String(process.env.S3_BUCKET),\n s3Client\n );\n\n const multiPartUpload = await useCase.execute({\n file: args.data,\n numberOfParts: args.numberOfParts\n });\n\n return new Response(multiPartUpload);\n } catch (e) {\n return new ErrorResponse({\n message: e.message,\n code: e.code,\n data: e.data\n });\n }\n },\n completeMultiPartUpload: async (_, args, context) => {\n await checkPermissions(context, { rwd: \"w\" });\n\n const s3Client = new S3({\n region: process.env.AWS_REGION,\n signatureVersion: \"v4\"\n });\n\n try {\n const useCase = new CompleteMultiPartUploadUseCase(\n String(process.env.S3_BUCKET),\n s3Client\n );\n\n await useCase.execute({\n fileKey: args.fileKey,\n uploadId: args.uploadId\n });\n\n return new Response(true);\n } catch (e) {\n return new ErrorResponse({\n message: e.message,\n code: e.code,\n data: e.data\n });\n }\n }\n }\n }\n }\n};\n\nexport default plugin;\n"],"mappings":";;;;;;;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA,MAAMA,MAA+C,GAAG;EACpDC,IAAI,EAAE,gBAAgB;EACtBC,IAAI,EAAE,oCAAoC;EAC1CC,MAAM,EAAE;IACJC,QAAQ,EAAE,aAAe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;IACDC,SAAS,EAAE;MACPC,OAAO,EAAE;QACLC,uBAAuB,EAAE,OAAOC,CAAC,EAAEC,IAAS,EAAEC,OAAO,KAAK;UACtD,IAAI;YACA,MAAM,IAAAC,kCAAgB,EAACD,OAAO,EAAE;cAAEE,GAAG,EAAE;YAAI,CAAC,CAAC;YAE7C,MAAMC,IAAI,GAAGJ,IAAI,CAACK,IAAgC;YAElD,MAAMC,QAAQ,GAAG,MAAML,OAAO,CAACM,WAAW,CAACC,WAAW,EAAE;YACxD,IAAI,CAACF,QAAQ,EAAE;cACX,MAAM,IAAIG,cAAW,CACjB,gCAAgC,EAChC,6BAA6B,EAC7B;gBAAEL;cAAK,CAAC,CACX;YACL;YAEA,OAAO,IAAIM,mBAAQ,CAAC,IAAAC,gDAAuB,EAACP,IAAI,EAAEE,QAAQ,CAAC,CAAC;UAChE,CAAC,CAAC,OAAOM,CAAC,EAAE;YACR,OAAO,IAAIC,wBAAa,CAAC;cACrBC,OAAO,EAAEF,CAAC,CAACE,OAAO;cAClBC,IAAI,EAAEH,CAAC,CAACG,IAAI;cACZV,IAAI,EAAEO,CAAC,CAACP;YACZ,CAAC,CAAC;UACN;QACJ,CAAC;QACDW,wBAAwB,EAAE,OAAOjB,CAAC,EAAEC,IAAI,EAAEC,OAAO,KAAK;UAClD,MAAM,IAAAC,kCAAgB,EAACD,OAAO,EAAE;YAAEE,GAAG,EAAE;UAAI,CAAC,CAAC;UAE7C,MAAMc,KAAK,GAAGjB,IAAI,CAACK,IAAkC;UAErD,IAAI;YACA,MAAMC,QAAQ,GAAG,MAAML,OAAO,CAACM,WAAW,CAACC,WAAW,EAAE;YACxD,IAAI,CAACF,QAAQ,EAAE;cACX,MAAM,IAAIG,cAAW,CACjB,gCAAgC,EAChC,6BAA6B,EAC7B;gBAAEQ;cAAM,CAAC,CACZ;YACL;YAEA,MAAMC,iBAAiB,GAAGD,KAAK,CAACE,GAAG,CAACf,IAAI,IAAI;cACxC,OAAO,IAAAO,gDAAuB,EAACP,IAAI,EAAEE,QAAQ,CAAC;YAClD,CAAC,CAAC;YAEF,OAAO,IAAII,mBAAQ,CAACQ,iBAAiB,CAAC;UAC1C,CAAC,CAAC,OAAON,CAAC,EAAE;YACR,OAAO,IAAIC,wBAAa,CAAC;cACrBC,OAAO,EAAEF,CAAC,CAACE,OAAO;cAClBC,IAAI,EAAEH,CAAC,CAACG,IAAI;cACZV,IAAI,EAAEO,CAAC,CAACP;YACZ,CAAC,CAAC;UACN;QACJ;MACJ,CAAC;MACDe,UAAU,EAAE;QACRC,qBAAqB,EAAE,OAAOtB,CAAC,EAAEC,IAAI,EAAEC,OAAO,KAAK;UAC/C,MAAM,IAAAC,kCAAgB,EAACD,OAAO,EAAE;YAAEE,GAAG,EAAE;UAAI,CAAC,CAAC;UAE7C,MAAMmB,QAAQ,GAAG,IAAIC,UAAE,CAAC;YACpBC,MAAM,EAAEC,OAAO,CAACC,GAAG,CAACC,UAAU;YAC9BC,gBAAgB,EAAE;UACtB,CAAC,CAAC;UAEF,IAAI;YACA,MAAMC,OAAO,GAAG,IAAIC,0DAA4B,CAC5CC,MAAM,CAACN,OAAO,CAACC,GAAG,CAACM,SAAS,CAAC,EAC7BV,QAAQ,CACX;YAED,MAAMW,eAAe,GAAG,MAAMJ,OAAO,CAACK,OAAO,CAAC;cAC1C9B,IAAI,EAAEJ,IAAI,CAACK,IAAI;cACf8B,aAAa,EAAEnC,IAAI,CAACmC;YACxB,CAAC,CAAC;YAEF,OAAO,IAAIzB,mBAAQ,CAACuB,eAAe,CAAC;UACxC,CAAC,CAAC,OAAOrB,CAAC,EAAE;YACR,OAAO,IAAIC,wBAAa,CAAC;cACrBC,OAAO,EAAEF,CAAC,CAACE,OAAO;cAClBC,IAAI,EAAEH,CAAC,CAACG,IAAI;cACZV,IAAI,EAAEO,CAAC,CAACP;YACZ,CAAC,CAAC;UACN;QACJ,CAAC;QACD+B,uBAAuB,EAAE,OAAOrC,CAAC,EAAEC,IAAI,EAAEC,OAAO,KAAK;UACjD,MAAM,IAAAC,kCAAgB,EAACD,OAAO,EAAE;YAAEE,GAAG,EAAE;UAAI,CAAC,CAAC;UAE7C,MAAMmB,QAAQ,GAAG,IAAIC,UAAE,CAAC;YACpBC,MAAM,EAAEC,OAAO,CAACC,GAAG,CAACC,UAAU;YAC9BC,gBAAgB,EAAE;UACtB,CAAC,CAAC;UAEF,IAAI;YACA,MAAMC,OAAO,GAAG,IAAIQ,8DAA8B,CAC9CN,MAAM,CAACN,OAAO,CAACC,GAAG,CAACM,SAAS,CAAC,EAC7BV,QAAQ,CACX;YAED,MAAMO,OAAO,CAACK,OAAO,CAAC;cAClBI,OAAO,EAAEtC,IAAI,CAACsC,OAAO;cACrBC,QAAQ,EAAEvC,IAAI,CAACuC;YACnB,CAAC,CAAC;YAEF,OAAO,IAAI7B,mBAAQ,CAAC,IAAI,CAAC;UAC7B,CAAC,CAAC,OAAOE,CAAC,EAAE;YACR,OAAO,IAAIC,wBAAa,CAAC;cACrBC,OAAO,EAAEF,CAAC,CAACE,OAAO;cAClBC,IAAI,EAAEH,CAAC,CAACG,IAAI;cACZV,IAAI,EAAEO,CAAC,CAACP;YACZ,CAAC,CAAC;UACN;QACJ;MACJ;IACJ;EACJ;AACJ,CAAC;AAAC,eAEad,MAAM;AAAA"}
|
package/types.d.ts
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import S3 from "aws-sdk/clients/s3";
|
|
2
|
+
export interface PresignedPostPayloadData {
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
size: number;
|
|
6
|
+
id?: string;
|
|
7
|
+
key?: string;
|
|
8
|
+
keyPrefix?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface FileData {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
key: string;
|
|
14
|
+
type: string;
|
|
15
|
+
size: number;
|
|
16
|
+
}
|
|
17
|
+
export interface PresignedPostPayloadDataResponse {
|
|
18
|
+
data: S3.PresignedPost;
|
|
19
|
+
file: FileData;
|
|
20
|
+
}
|
package/types.js
CHANGED
package/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\n\nexport interface PresignedPostPayloadData {\n name: string;\n type: string;\n size: number;\n id?: string;\n key?: string;\n keyPrefix?: string;\n}\n\nexport interface FileData {\n id: string;\n name: string;\n key: string;\n type: string;\n size: number;\n}\n\nexport interface PresignedPostPayloadDataResponse {\n data: S3.PresignedPost;\n file: FileData;\n}\n"],"mappings":""}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
file: {
|
|
5
|
-
name: string;
|
|
6
|
-
key: string;
|
|
7
|
-
type: any;
|
|
8
|
-
size: any;
|
|
9
|
-
};
|
|
10
|
-
}>;
|
|
11
|
-
export default _default;
|
|
1
|
+
import { FileManagerSettings } from "@webiny/api-file-manager/types";
|
|
2
|
+
import { PresignedPostPayloadData, PresignedPostPayloadDataResponse } from "../types";
|
|
3
|
+
export declare const getPresignedPostPayload: (data: PresignedPostPayloadData, settings: FileManagerSettings) => PresignedPostPayloadDataResponse;
|
|
@@ -1,86 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
|
-
exports.
|
|
9
|
-
|
|
10
|
-
var _uniqid = _interopRequireDefault(require("uniqid"));
|
|
11
|
-
|
|
12
|
-
var _sanitizeFilename = _interopRequireDefault(require("sanitize-filename"));
|
|
13
|
-
|
|
7
|
+
exports.getPresignedPostPayload = void 0;
|
|
14
8
|
var _s = _interopRequireDefault(require("aws-sdk/clients/s3"));
|
|
15
|
-
|
|
16
9
|
var _validation = require("@webiny/validation");
|
|
17
|
-
|
|
10
|
+
var _prepareFileData = require("./prepareFileData");
|
|
18
11
|
const S3_BUCKET = process.env.S3_BUCKET;
|
|
19
|
-
const UPLOAD_MAX_FILE_SIZE_DEFAULT =
|
|
12
|
+
const UPLOAD_MAX_FILE_SIZE_DEFAULT = 1099511627776; // 1TB
|
|
20
13
|
|
|
21
14
|
const sanitizeFileSizeValue = (value, defaultValue) => {
|
|
22
15
|
try {
|
|
23
16
|
_validation.validation.validateSync(value, "required,numeric,gte:0");
|
|
24
|
-
|
|
25
17
|
return value;
|
|
26
18
|
} catch (e) {
|
|
19
|
+
// TODO @ts-refactor No need to log the error?
|
|
27
20
|
return defaultValue;
|
|
28
21
|
}
|
|
29
22
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// If type is missing, let's use the default "application/octet-stream" type,
|
|
33
|
-
// which is also the default type that the Amazon S3 would use.
|
|
34
|
-
if (!data.type) {
|
|
35
|
-
data.type = "application/octet-stream";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const contentType = data.type;
|
|
39
|
-
|
|
40
|
-
if (!contentType) {
|
|
41
|
-
throw Error(`File's content type could not be resolved.`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let key = (0, _sanitizeFilename.default)(data.name);
|
|
45
|
-
|
|
46
|
-
if (key) {
|
|
47
|
-
key = (0, _uniqid.default)() + "-" + key;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (data.keyPrefix) {
|
|
51
|
-
key = `${(0, _sanitizeFilename.default)(data.keyPrefix)}-${key}`;
|
|
52
|
-
} // Replace all whitespace.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
key = key.replace(/\s/g, "");
|
|
23
|
+
const getPresignedPostPayload = (data, settings) => {
|
|
24
|
+
const file = (0, _prepareFileData.prepareFileData)(data);
|
|
56
25
|
const uploadMinFileSize = sanitizeFileSizeValue(settings.uploadMinFileSize, 0);
|
|
57
26
|
const uploadMaxFileSize = sanitizeFileSizeValue(settings.uploadMaxFileSize, UPLOAD_MAX_FILE_SIZE_DEFAULT);
|
|
58
27
|
const params = {
|
|
59
28
|
Expires: 60,
|
|
60
29
|
Bucket: S3_BUCKET,
|
|
61
30
|
Conditions: [["content-length-range", uploadMinFileSize, uploadMaxFileSize]],
|
|
62
|
-
// 0 Bytes - 25MB
|
|
63
31
|
Fields: {
|
|
64
|
-
"Content-Type":
|
|
65
|
-
key
|
|
32
|
+
"Content-Type": file.type,
|
|
33
|
+
key: file.key
|
|
66
34
|
}
|
|
67
35
|
};
|
|
68
|
-
|
|
69
36
|
if (params.Fields.key.startsWith("/")) {
|
|
70
|
-
params.Fields.key = params.Fields.key.
|
|
37
|
+
params.Fields.key = params.Fields.key.slice(1);
|
|
71
38
|
}
|
|
72
|
-
|
|
73
39
|
const s3 = new _s.default();
|
|
74
40
|
const payload = s3.createPresignedPost(params);
|
|
75
41
|
return {
|
|
76
42
|
data: payload,
|
|
77
|
-
file
|
|
78
|
-
name: key,
|
|
79
|
-
key,
|
|
80
|
-
type: contentType,
|
|
81
|
-
size: data.size
|
|
82
|
-
}
|
|
43
|
+
file
|
|
83
44
|
};
|
|
84
45
|
};
|
|
85
|
-
|
|
86
|
-
exports.default = _default;
|
|
46
|
+
exports.getPresignedPostPayload = getPresignedPostPayload;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["S3_BUCKET","process","env","UPLOAD_MAX_FILE_SIZE_DEFAULT","sanitizeFileSizeValue","value","defaultValue","validation","validateSync","e","getPresignedPostPayload","data","settings","file","prepareFileData","uploadMinFileSize","uploadMaxFileSize","params","Expires","Bucket","Conditions","Fields","type","key","startsWith","slice","s3","S3","payload","createPresignedPost"],"sources":["getPresignedPostPayload.ts"],"sourcesContent":["import S3 from \"aws-sdk/clients/s3\";\nimport { validation } from \"@webiny/validation\";\nimport { FileManagerSettings } from \"@webiny/api-file-manager/types\";\nimport { prepareFileData } from \"~/utils/prepareFileData\";\nimport { PresignedPostPayloadData, PresignedPostPayloadDataResponse } from \"~/types\";\n\nconst S3_BUCKET = process.env.S3_BUCKET;\nconst UPLOAD_MAX_FILE_SIZE_DEFAULT = 1099511627776; // 1TB\n\nconst sanitizeFileSizeValue = (value: number, defaultValue: number): number => {\n try {\n validation.validateSync(value, \"required,numeric,gte:0\");\n return value;\n } catch (e) {\n // TODO @ts-refactor No need to log the error?\n return defaultValue;\n }\n};\n\nexport const getPresignedPostPayload = (\n data: PresignedPostPayloadData,\n settings: FileManagerSettings\n): PresignedPostPayloadDataResponse => {\n const file = prepareFileData(data);\n\n const uploadMinFileSize = sanitizeFileSizeValue(settings.uploadMinFileSize, 0);\n const uploadMaxFileSize = sanitizeFileSizeValue(\n settings.uploadMaxFileSize,\n UPLOAD_MAX_FILE_SIZE_DEFAULT\n );\n\n const params = {\n Expires: 60,\n Bucket: S3_BUCKET,\n Conditions: [[\"content-length-range\", uploadMinFileSize, uploadMaxFileSize]],\n Fields: {\n \"Content-Type\": file.type,\n key: file.key\n }\n };\n\n if (params.Fields.key.startsWith(\"/\")) {\n params.Fields.key = params.Fields.key.slice(1);\n }\n\n const s3 = new S3();\n const payload = s3.createPresignedPost(params);\n\n return {\n data: payload,\n file\n };\n};\n"],"mappings":";;;;;;;AAAA;AACA;AAEA;AAGA,MAAMA,SAAS,GAAGC,OAAO,CAACC,GAAG,CAACF,SAAS;AACvC,MAAMG,4BAA4B,GAAG,aAAa,CAAC,CAAC;;AAEpD,MAAMC,qBAAqB,GAAG,CAACC,KAAa,EAAEC,YAAoB,KAAa;EAC3E,IAAI;IACAC,sBAAU,CAACC,YAAY,CAACH,KAAK,EAAE,wBAAwB,CAAC;IACxD,OAAOA,KAAK;EAChB,CAAC,CAAC,OAAOI,CAAC,EAAE;IACR;IACA,OAAOH,YAAY;EACvB;AACJ,CAAC;AAEM,MAAMI,uBAAuB,GAAG,CACnCC,IAA8B,EAC9BC,QAA6B,KACM;EACnC,MAAMC,IAAI,GAAG,IAAAC,gCAAe,EAACH,IAAI,CAAC;EAElC,MAAMI,iBAAiB,GAAGX,qBAAqB,CAACQ,QAAQ,CAACG,iBAAiB,EAAE,CAAC,CAAC;EAC9E,MAAMC,iBAAiB,GAAGZ,qBAAqB,CAC3CQ,QAAQ,CAACI,iBAAiB,EAC1Bb,4BAA4B,CAC/B;EAED,MAAMc,MAAM,GAAG;IACXC,OAAO,EAAE,EAAE;IACXC,MAAM,EAAEnB,SAAS;IACjBoB,UAAU,EAAE,CAAC,CAAC,sBAAsB,EAAEL,iBAAiB,EAAEC,iBAAiB,CAAC,CAAC;IAC5EK,MAAM,EAAE;MACJ,cAAc,EAAER,IAAI,CAACS,IAAI;MACzBC,GAAG,EAAEV,IAAI,CAACU;IACd;EACJ,CAAC;EAED,IAAIN,MAAM,CAACI,MAAM,CAACE,GAAG,CAACC,UAAU,CAAC,GAAG,CAAC,EAAE;IACnCP,MAAM,CAACI,MAAM,CAACE,GAAG,GAAGN,MAAM,CAACI,MAAM,CAACE,GAAG,CAACE,KAAK,CAAC,CAAC,CAAC;EAClD;EAEA,MAAMC,EAAE,GAAG,IAAIC,UAAE,EAAE;EACnB,MAAMC,OAAO,GAAGF,EAAE,CAACG,mBAAmB,CAACZ,MAAM,CAAC;EAE9C,OAAO;IACHN,IAAI,EAAEiB,OAAO;IACbf;EACJ,CAAC;AACL,CAAC;AAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.mimeTypes = void 0;
|
|
8
|
+
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
|
|
9
|
+
var _other = _interopRequireDefault(require("mime/types/other"));
|
|
10
|
+
var _standard = _interopRequireDefault(require("mime/types/standard"));
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* We need to get all extensions that are defined for a specific content type.
|
|
17
|
+
* This operation is not available via the `mime` package, so we create such an object ourselves.
|
|
18
|
+
*/
|
|
19
|
+
const mimeTypes = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, _other.default), _standard.default);
|
|
20
|
+
exports.mimeTypes = mimeTypes;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["mimeTypes","vendorTypes","standardTypes"],"sources":["mimeTypes.ts"],"sourcesContent":["// @ts-ignore\nimport vendorTypes from \"mime/types/other\";\n// @ts-ignore\nimport standardTypes from \"mime/types/standard\";\n\n/**\n * We need to get all extensions that are defined for a specific content type.\n * This operation is not available via the `mime` package, so we create such an object ourselves.\n */\nexport const mimeTypes: Record<string, string[]> = {\n ...vendorTypes,\n ...standardTypes\n};\n"],"mappings":";;;;;;;;AACA;AAEA;AAHA;;AAEA;;AAGA;AACA;AACA;AACA;AACO,MAAMA,SAAmC,+DACzCC,cAAW,GACXC,iBAAa,CACnB;AAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.prepareFileData = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _sanitizeFilename = _interopRequireDefault(require("sanitize-filename"));
|
|
10
|
+
var _utils = require("@webiny/utils");
|
|
11
|
+
var _mimeTypes = require("./mimeTypes");
|
|
12
|
+
class FileKey {
|
|
13
|
+
constructor(name, type) {
|
|
14
|
+
(0, _defineProperty2.default)(this, "name", void 0);
|
|
15
|
+
(0, _defineProperty2.default)(this, "type", void 0);
|
|
16
|
+
(0, _defineProperty2.default)(this, "prefix", void 0);
|
|
17
|
+
(0, _defineProperty2.default)(this, "id", void 0);
|
|
18
|
+
(0, _defineProperty2.default)(this, "extension", "");
|
|
19
|
+
this.type = type;
|
|
20
|
+
this.name = name;
|
|
21
|
+
}
|
|
22
|
+
setId(id) {
|
|
23
|
+
if (!this.name.startsWith(`${id}/`)) {
|
|
24
|
+
this.id = id;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
setPrefix(prefix) {
|
|
28
|
+
this.prefix = prefix;
|
|
29
|
+
}
|
|
30
|
+
getKey() {
|
|
31
|
+
const key = (0, _sanitizeFilename.default)(this.name).replace(/\s/g, "");
|
|
32
|
+
const maybeHasExtension = key.toLowerCase().includes(".");
|
|
33
|
+
if (maybeHasExtension) {
|
|
34
|
+
const maybeExt = key.toLowerCase().split(".").pop();
|
|
35
|
+
const extensions = _mimeTypes.mimeTypes[this.type];
|
|
36
|
+
if (extensions && !extensions.includes(maybeExt)) {
|
|
37
|
+
this.extension = extensions[0];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const keyWithExt = [key, this.extension].filter(Boolean).join(".");
|
|
41
|
+
return [this.prefix, this.id, keyWithExt].filter(Boolean).join("/");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const prepareFileData = data => {
|
|
45
|
+
// If type is missing, let's use the default "application/octet-stream" type,
|
|
46
|
+
// which is also the default type that the Amazon S3 would use.
|
|
47
|
+
const contentType = data.type || "application/octet-stream";
|
|
48
|
+
const id = data.id || (0, _utils.mdbid)();
|
|
49
|
+
const key = new FileKey(data.key || data.name, data.type);
|
|
50
|
+
key.setId(id);
|
|
51
|
+
if (data.keyPrefix) {
|
|
52
|
+
key.setPrefix(data.keyPrefix);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
name: data.name,
|
|
57
|
+
key: key.getKey(),
|
|
58
|
+
type: contentType,
|
|
59
|
+
size: data.size
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
exports.prepareFileData = prepareFileData;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["FileKey","constructor","name","type","setId","id","startsWith","setPrefix","prefix","getKey","key","sanitizeFilename","replace","maybeHasExtension","toLowerCase","includes","maybeExt","split","pop","extensions","mimeTypes","extension","keyWithExt","filter","Boolean","join","prepareFileData","data","contentType","mdbid","keyPrefix","size"],"sources":["prepareFileData.ts"],"sourcesContent":["import sanitizeFilename from \"sanitize-filename\";\nimport { mdbid } from \"@webiny/utils\";\nimport { PresignedPostPayloadData, FileData } from \"~/types\";\nimport { mimeTypes } from \"./mimeTypes\";\n\nclass FileKey {\n private readonly name: string;\n private readonly type: string;\n private prefix: string | undefined;\n private id: string | undefined;\n private extension = \"\";\n\n constructor(name: string, type: string) {\n this.type = type;\n this.name = name;\n }\n\n setId(id: string) {\n if (!this.name.startsWith(`${id}/`)) {\n this.id = id;\n }\n }\n\n setPrefix(prefix: string) {\n this.prefix = prefix;\n }\n\n getKey() {\n const key = sanitizeFilename(this.name).replace(/\\s/g, \"\");\n const maybeHasExtension = key.toLowerCase().includes(\".\");\n\n if (maybeHasExtension) {\n const maybeExt = key.toLowerCase().split(\".\").pop() as string;\n const extensions = mimeTypes[this.type];\n if (extensions && !extensions.includes(maybeExt)) {\n this.extension = extensions[0];\n }\n }\n\n const keyWithExt = [key, this.extension].filter(Boolean).join(\".\");\n return [this.prefix, this.id, keyWithExt].filter(Boolean).join(\"/\");\n }\n}\n\nexport const prepareFileData = (data: PresignedPostPayloadData): FileData => {\n // If type is missing, let's use the default \"application/octet-stream\" type,\n // which is also the default type that the Amazon S3 would use.\n const contentType = data.type || \"application/octet-stream\";\n\n const id = data.id || mdbid();\n const key = new FileKey(data.key || data.name, data.type);\n\n key.setId(id);\n\n if (data.keyPrefix) {\n key.setPrefix(data.keyPrefix);\n }\n\n return {\n id,\n name: data.name,\n key: key.getKey(),\n type: contentType,\n size: data.size\n };\n};\n"],"mappings":";;;;;;;;AAAA;AACA;AAEA;AAEA,MAAMA,OAAO,CAAC;EAOVC,WAAW,CAACC,IAAY,EAAEC,IAAY,EAAE;IAAA;IAAA;IAAA;IAAA;IAAA,iDAFpB,EAAE;IAGlB,IAAI,CAACA,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACD,IAAI,GAAGA,IAAI;EACpB;EAEAE,KAAK,CAACC,EAAU,EAAE;IACd,IAAI,CAAC,IAAI,CAACH,IAAI,CAACI,UAAU,CAAE,GAAED,EAAG,GAAE,CAAC,EAAE;MACjC,IAAI,CAACA,EAAE,GAAGA,EAAE;IAChB;EACJ;EAEAE,SAAS,CAACC,MAAc,EAAE;IACtB,IAAI,CAACA,MAAM,GAAGA,MAAM;EACxB;EAEAC,MAAM,GAAG;IACL,MAAMC,GAAG,GAAG,IAAAC,yBAAgB,EAAC,IAAI,CAACT,IAAI,CAAC,CAACU,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;IAC1D,MAAMC,iBAAiB,GAAGH,GAAG,CAACI,WAAW,EAAE,CAACC,QAAQ,CAAC,GAAG,CAAC;IAEzD,IAAIF,iBAAiB,EAAE;MACnB,MAAMG,QAAQ,GAAGN,GAAG,CAACI,WAAW,EAAE,CAACG,KAAK,CAAC,GAAG,CAAC,CAACC,GAAG,EAAY;MAC7D,MAAMC,UAAU,GAAGC,oBAAS,CAAC,IAAI,CAACjB,IAAI,CAAC;MACvC,IAAIgB,UAAU,IAAI,CAACA,UAAU,CAACJ,QAAQ,CAACC,QAAQ,CAAC,EAAE;QAC9C,IAAI,CAACK,SAAS,GAAGF,UAAU,CAAC,CAAC,CAAC;MAClC;IACJ;IAEA,MAAMG,UAAU,GAAG,CAACZ,GAAG,EAAE,IAAI,CAACW,SAAS,CAAC,CAACE,MAAM,CAACC,OAAO,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC;IAClE,OAAO,CAAC,IAAI,CAACjB,MAAM,EAAE,IAAI,CAACH,EAAE,EAAEiB,UAAU,CAAC,CAACC,MAAM,CAACC,OAAO,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC;EACvE;AACJ;AAEO,MAAMC,eAAe,GAAIC,IAA8B,IAAe;EACzE;EACA;EACA,MAAMC,WAAW,GAAGD,IAAI,CAACxB,IAAI,IAAI,0BAA0B;EAE3D,MAAME,EAAE,GAAGsB,IAAI,CAACtB,EAAE,IAAI,IAAAwB,YAAK,GAAE;EAC7B,MAAMnB,GAAG,GAAG,IAAIV,OAAO,CAAC2B,IAAI,CAACjB,GAAG,IAAIiB,IAAI,CAACzB,IAAI,EAAEyB,IAAI,CAACxB,IAAI,CAAC;EAEzDO,GAAG,CAACN,KAAK,CAACC,EAAE,CAAC;EAEb,IAAIsB,IAAI,CAACG,SAAS,EAAE;IAChBpB,GAAG,CAACH,SAAS,CAACoB,IAAI,CAACG,SAAS,CAAC;EACjC;EAEA,OAAO;IACHzB,EAAE;IACFH,IAAI,EAAEyB,IAAI,CAACzB,IAAI;IACfQ,GAAG,EAAEA,GAAG,CAACD,MAAM,EAAE;IACjBN,IAAI,EAAEyB,WAAW;IACjBG,IAAI,EAAEJ,IAAI,CAACI;EACf,CAAC;AACL,CAAC;AAAC"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Response } from "node-fetch";
|
|
3
|
+
import S3 from "aws-sdk/clients/s3";
|
|
4
|
+
declare const _default: (buffer: Buffer, preSignedPostPayload: S3.PresignedPost) => Promise<Response>;
|
|
2
5
|
export default _default;
|
package/utils/uploadFileToS3.js
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
|
|
10
8
|
var _formData = _interopRequireDefault(require("form-data"));
|
|
11
|
-
|
|
12
9
|
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
13
|
-
|
|
14
10
|
var _default = async (buffer, preSignedPostPayload) => {
|
|
15
|
-
const formData = new _formData.default();
|
|
16
|
-
|
|
11
|
+
const formData = new _formData.default();
|
|
12
|
+
// Add all pre signed payload field to "FormData".
|
|
17
13
|
Object.keys(preSignedPostPayload.fields).forEach(key => {
|
|
18
14
|
formData.append(key, preSignedPostPayload.fields[key]);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
formData.append("file", buffer);
|
|
22
|
-
|
|
15
|
+
});
|
|
16
|
+
// Add file content to "FormData".
|
|
17
|
+
formData.append("file", buffer);
|
|
18
|
+
// Finally make the upload request to S3.
|
|
23
19
|
return (0, _nodeFetch.default)(preSignedPostPayload.url, {
|
|
24
20
|
method: "POST",
|
|
25
21
|
body: formData
|
|
26
22
|
});
|
|
27
23
|
};
|
|
28
|
-
|
|
29
24
|
exports.default = _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["buffer","preSignedPostPayload","formData","FormData","Object","keys","fields","forEach","key","append","fetch","url","method","body"],"sources":["uploadFileToS3.ts"],"sourcesContent":["import FormData from \"form-data\";\nimport fetch, { Response } from \"node-fetch\";\nimport S3 from \"aws-sdk/clients/s3\";\n\nexport default async (\n buffer: Buffer,\n preSignedPostPayload: S3.PresignedPost\n): Promise<Response> => {\n const formData = new FormData();\n // Add all pre signed payload field to \"FormData\".\n Object.keys(preSignedPostPayload.fields).forEach(key => {\n formData.append(key, preSignedPostPayload.fields[key]);\n });\n // Add file content to \"FormData\".\n formData.append(\"file\", buffer);\n // Finally make the upload request to S3.\n return fetch(preSignedPostPayload.url, {\n method: \"POST\",\n body: formData\n });\n};\n"],"mappings":";;;;;;;AAAA;AACA;AAA6C,eAG9B,OACXA,MAAc,EACdC,oBAAsC,KAClB;EACpB,MAAMC,QAAQ,GAAG,IAAIC,iBAAQ,EAAE;EAC/B;EACAC,MAAM,CAACC,IAAI,CAACJ,oBAAoB,CAACK,MAAM,CAAC,CAACC,OAAO,CAACC,GAAG,IAAI;IACpDN,QAAQ,CAACO,MAAM,CAACD,GAAG,EAAEP,oBAAoB,CAACK,MAAM,CAACE,GAAG,CAAC,CAAC;EAC1D,CAAC,CAAC;EACF;EACAN,QAAQ,CAACO,MAAM,CAAC,MAAM,EAAET,MAAM,CAAC;EAC/B;EACA,OAAO,IAAAU,kBAAK,EAACT,oBAAoB,CAACU,GAAG,EAAE;IACnCC,MAAM,EAAE,MAAM;IACdC,IAAI,EAAEX;EACV,CAAC,CAAC;AACN,CAAC;AAAA"}
|