@strapi/provider-upload-aws-s3 4.9.1 → 4.9.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/dist/index.d.ts +45 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.js +53 -0
- package/dist/utils.js.map +1 -0
- package/package.json +18 -7
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -4
- package/lib/index.js +0 -137
- package/lib/utils.js +0 -63
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { ReadStream } from 'node:fs';
|
|
3
|
+
import AWS from 'aws-sdk';
|
|
4
|
+
interface File {
|
|
5
|
+
name: string;
|
|
6
|
+
alternativeText?: string;
|
|
7
|
+
caption?: string;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
formats?: Record<string, unknown>;
|
|
11
|
+
hash: string;
|
|
12
|
+
ext?: string;
|
|
13
|
+
mime: string;
|
|
14
|
+
size: number;
|
|
15
|
+
url: string;
|
|
16
|
+
previewUrl?: string;
|
|
17
|
+
path?: string;
|
|
18
|
+
provider?: string;
|
|
19
|
+
provider_metadata?: Record<string, unknown>;
|
|
20
|
+
stream?: ReadStream;
|
|
21
|
+
buffer?: Buffer;
|
|
22
|
+
}
|
|
23
|
+
interface InitOptions extends Partial<AWS.S3.ClientConfiguration> {
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
rootPath?: string;
|
|
26
|
+
s3Options: AWS.S3.ClientConfiguration & {
|
|
27
|
+
params: {
|
|
28
|
+
Bucket: string;
|
|
29
|
+
ACL?: string;
|
|
30
|
+
signedUrlExpires?: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
declare const _default: {
|
|
35
|
+
init({ baseUrl, rootPath, s3Options, ...legacyS3Options }: InitOptions): {
|
|
36
|
+
isPrivate(): boolean;
|
|
37
|
+
getSignedUrl(file: File): Promise<{
|
|
38
|
+
url: string;
|
|
39
|
+
}>;
|
|
40
|
+
uploadStream(file: File, customParams?: {}): Promise<void>;
|
|
41
|
+
upload(file: File, customParams?: {}): Promise<void>;
|
|
42
|
+
delete(file: File, customParams?: {}): Promise<void>;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export = _default;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
const fp_1 = require("lodash/fp");
|
|
6
|
+
const aws_sdk_1 = __importDefault(require("aws-sdk"));
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
// TODO V5: Migrate to aws-sdk v3
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
require('aws-sdk/lib/maintenance_mode_message').suppress = true;
|
|
11
|
+
function assertUrlProtocol(url) {
|
|
12
|
+
// Regex to test protocol like "http://", "https://"
|
|
13
|
+
return /^\w*:\/\//.test(url);
|
|
14
|
+
}
|
|
15
|
+
module.exports = {
|
|
16
|
+
init({ baseUrl, rootPath, s3Options, ...legacyS3Options }) {
|
|
17
|
+
if (Object.keys(legacyS3Options).length > 0) {
|
|
18
|
+
process.emitWarning("S3 configuration options passed at root level of the plugin's providerOptions is deprecated and will be removed in a future release. Please wrap them inside the 's3Options:{}' property.");
|
|
19
|
+
}
|
|
20
|
+
const config = { ...s3Options, ...legacyS3Options };
|
|
21
|
+
const S3 = new aws_sdk_1.default.S3({
|
|
22
|
+
apiVersion: '2006-03-01',
|
|
23
|
+
...config,
|
|
24
|
+
});
|
|
25
|
+
const filePrefix = rootPath ? `${rootPath.replace(/\/+$/, '')}/` : '';
|
|
26
|
+
const getFileKey = (file) => {
|
|
27
|
+
const path = file.path ? `${file.path}/` : '';
|
|
28
|
+
return `${filePrefix}${path}${file.hash}${file.ext}`;
|
|
29
|
+
};
|
|
30
|
+
const ACL = (0, fp_1.getOr)('public-read', ['params', 'ACL'], config);
|
|
31
|
+
const upload = (file, customParams = {}) => new Promise((resolve, reject) => {
|
|
32
|
+
const fileKey = getFileKey(file);
|
|
33
|
+
if (!file.stream && !file.buffer) {
|
|
34
|
+
reject(new Error('Missing file stream or buffer'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const params = {
|
|
38
|
+
Key: fileKey,
|
|
39
|
+
Bucket: config.params.Bucket,
|
|
40
|
+
Body: file.stream || file.buffer,
|
|
41
|
+
ACL,
|
|
42
|
+
ContentType: file.mime,
|
|
43
|
+
...customParams,
|
|
44
|
+
};
|
|
45
|
+
const onUploaded = (err, data) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
return reject(err);
|
|
48
|
+
}
|
|
49
|
+
// set the bucket file url
|
|
50
|
+
if (assertUrlProtocol(data.Location)) {
|
|
51
|
+
file.url = baseUrl ? `${baseUrl}/${fileKey}` : data.Location;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Default protocol to https protocol
|
|
55
|
+
file.url = `https://${data.Location}`;
|
|
56
|
+
}
|
|
57
|
+
resolve();
|
|
58
|
+
};
|
|
59
|
+
S3.upload(params, onUploaded);
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
isPrivate() {
|
|
63
|
+
return ACL === 'private';
|
|
64
|
+
},
|
|
65
|
+
async getSignedUrl(file) {
|
|
66
|
+
// Do not sign the url if it does not come from the same bucket.
|
|
67
|
+
const { bucket } = (0, utils_1.getBucketFromUrl)(file.url);
|
|
68
|
+
if (bucket !== config.params.Bucket) {
|
|
69
|
+
return { url: file.url };
|
|
70
|
+
}
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const fileKey = getFileKey(file);
|
|
73
|
+
S3.getSignedUrl('getObject', {
|
|
74
|
+
Bucket: config.params.Bucket,
|
|
75
|
+
Key: fileKey,
|
|
76
|
+
Expires: (0, fp_1.getOr)(15 * 60, ['params', 'signedUrlExpires'], config), // 15 minutes
|
|
77
|
+
}, (err, url) => {
|
|
78
|
+
if (err) {
|
|
79
|
+
return reject(err);
|
|
80
|
+
}
|
|
81
|
+
resolve({ url });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
uploadStream(file, customParams = {}) {
|
|
86
|
+
return upload(file, customParams);
|
|
87
|
+
},
|
|
88
|
+
upload(file, customParams = {}) {
|
|
89
|
+
return upload(file, customParams);
|
|
90
|
+
},
|
|
91
|
+
delete(file, customParams = {}) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
// delete file on S3 bucket
|
|
94
|
+
const fileKey = getFileKey(file);
|
|
95
|
+
S3.deleteObject({
|
|
96
|
+
Key: fileKey,
|
|
97
|
+
Bucket: config.params.Bucket,
|
|
98
|
+
...customParams,
|
|
99
|
+
}, (err) => {
|
|
100
|
+
if (err) {
|
|
101
|
+
return reject(err);
|
|
102
|
+
}
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface BucketInfo {
|
|
2
|
+
bucket?: string | null;
|
|
3
|
+
err?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse the bucket name from a URL.
|
|
7
|
+
* See all URL formats in https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html
|
|
8
|
+
*
|
|
9
|
+
* @param {string} fileUrl - the URL to parse
|
|
10
|
+
* @returns {object} result
|
|
11
|
+
* @returns {string} result.bucket - the bucket name
|
|
12
|
+
* @returns {string} result.err - if any
|
|
13
|
+
*/
|
|
14
|
+
export declare function getBucketFromUrl(fileUrl: string): BucketInfo;
|
|
15
|
+
export {};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBucketFromUrl = void 0;
|
|
4
|
+
const ENDPOINT_PATTERN = /^(.+\.)?s3[.-]([a-z0-9-]+)\./;
|
|
5
|
+
/**
|
|
6
|
+
* Parse the bucket name from a URL.
|
|
7
|
+
* See all URL formats in https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html
|
|
8
|
+
*
|
|
9
|
+
* @param {string} fileUrl - the URL to parse
|
|
10
|
+
* @returns {object} result
|
|
11
|
+
* @returns {string} result.bucket - the bucket name
|
|
12
|
+
* @returns {string} result.err - if any
|
|
13
|
+
*/
|
|
14
|
+
function getBucketFromUrl(fileUrl) {
|
|
15
|
+
const uri = new URL(fileUrl);
|
|
16
|
+
// S3://<bucket-name>/<key>
|
|
17
|
+
if (uri.protocol === 's3:') {
|
|
18
|
+
const bucket = uri.host;
|
|
19
|
+
if (!bucket) {
|
|
20
|
+
return { err: `Invalid S3 URI: no bucket: ${uri}` };
|
|
21
|
+
}
|
|
22
|
+
return { bucket };
|
|
23
|
+
}
|
|
24
|
+
if (!uri.host) {
|
|
25
|
+
return { err: `Invalid S3 URI: no hostname: ${uri}` };
|
|
26
|
+
}
|
|
27
|
+
const matches = uri.host.match(ENDPOINT_PATTERN);
|
|
28
|
+
if (!matches) {
|
|
29
|
+
return { err: `Invalid S3 URI: hostname does not appear to be a valid S3 endpoint: ${uri}` };
|
|
30
|
+
}
|
|
31
|
+
const prefix = matches[1];
|
|
32
|
+
// https://s3.amazonaws.com/<bucket-name>
|
|
33
|
+
if (!prefix) {
|
|
34
|
+
if (uri.pathname === '/') {
|
|
35
|
+
return { bucket: null };
|
|
36
|
+
}
|
|
37
|
+
const index = uri.pathname.indexOf('/', 1);
|
|
38
|
+
// https://s3.amazonaws.com/<bucket-name>
|
|
39
|
+
if (index === -1) {
|
|
40
|
+
return { bucket: uri.pathname.substring(1) };
|
|
41
|
+
}
|
|
42
|
+
// https://s3.amazonaws.com/<bucket-name>/
|
|
43
|
+
if (index === uri.pathname.length - 1) {
|
|
44
|
+
return { bucket: uri.pathname.substring(1, index) };
|
|
45
|
+
}
|
|
46
|
+
// https://s3.amazonaws.com/<bucket-name>/key
|
|
47
|
+
return { bucket: uri.pathname.substring(1, index) };
|
|
48
|
+
}
|
|
49
|
+
// https://<bucket-name>.s3.amazonaws.com/
|
|
50
|
+
return { bucket: prefix.substring(0, prefix.length - 1) };
|
|
51
|
+
}
|
|
52
|
+
exports.getBucketFromUrl = getBucketFromUrl;
|
|
53
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAOxD;;;;;;;;GAQG;AACH,SAAgB,gBAAgB,CAAC,OAAe;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAE7B,2BAA2B;IAC3B,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,EAAE;QAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC;QAExB,IAAI,CAAC,MAAM,EAAE;YACX,OAAO,EAAE,GAAG,EAAE,8BAA8B,GAAG,EAAE,EAAE,CAAC;SACrD;QACD,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QACb,OAAO,EAAE,GAAG,EAAE,gCAAgC,GAAG,EAAE,EAAE,CAAC;KACvD;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,EAAE,GAAG,EAAE,uEAAuE,GAAG,EAAE,EAAE,CAAC;KAC9F;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,yCAAyC;IACzC,IAAI,CAAC,MAAM,EAAE;QACX,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE;YACxB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SACzB;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAE3C,yCAAyC;QACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE;YAChB,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C;QAED,0CAA0C;QAC1C,IAAI,KAAK,KAAK,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YACrC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;SACrD;QAED,6CAA6C;QAC7C,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;KACrD;IAED,0CAA0C;IAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC;AA/CD,4CA+CC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/provider-upload-aws-s3",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.2",
|
|
4
4
|
"description": "AWS S3 provider for strapi upload",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"upload",
|
|
@@ -29,22 +29,33 @@
|
|
|
29
29
|
"url": "https://strapi.io"
|
|
30
30
|
}
|
|
31
31
|
],
|
|
32
|
-
"main": "./
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"./dist"
|
|
36
|
+
],
|
|
36
37
|
"scripts": {
|
|
38
|
+
"build": "run -T tsc",
|
|
39
|
+
"build:ts": "run -T tsc",
|
|
40
|
+
"watch": "run -T tsc -w --preserveWatchOutput",
|
|
41
|
+
"clean": "run -T rimraf ./dist",
|
|
42
|
+
"prepublishOnly": "yarn clean && yarn build",
|
|
37
43
|
"test:unit": "run -T jest",
|
|
38
44
|
"test:unit:watch": "run -T jest --watch",
|
|
39
45
|
"lint": "run -T eslint ."
|
|
40
46
|
},
|
|
41
47
|
"dependencies": {
|
|
42
|
-
"aws-sdk": "2.
|
|
48
|
+
"aws-sdk": "2.1351.0",
|
|
43
49
|
"lodash": "4.17.21"
|
|
44
50
|
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/jest": "29.2.0",
|
|
53
|
+
"eslint-config-custom": "4.9.2",
|
|
54
|
+
"tsconfig": "4.9.2"
|
|
55
|
+
},
|
|
45
56
|
"engines": {
|
|
46
57
|
"node": ">=14.19.1 <=18.x.x",
|
|
47
58
|
"npm": ">=6.0.0"
|
|
48
59
|
},
|
|
49
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "91e0be2708e4d1e8ec731c75e73e54c0dfacb67d"
|
|
50
61
|
}
|
package/.eslintignore
DELETED
package/.eslintrc.js
DELETED
package/lib/index.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Module dependencies
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/* eslint-disable no-unused-vars */
|
|
8
|
-
// Public node modules.
|
|
9
|
-
const { getOr } = require('lodash/fp');
|
|
10
|
-
const AWS = require('aws-sdk');
|
|
11
|
-
const { getBucketFromUrl } = require('./utils');
|
|
12
|
-
|
|
13
|
-
function assertUrlProtocol(url) {
|
|
14
|
-
// Regex to test protocol like "http://", "https://"
|
|
15
|
-
return /^\w*:\/\//.test(url);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
init({ baseUrl = null, rootPath = null, s3Options, ...legacyS3Options }) {
|
|
20
|
-
if (legacyS3Options) {
|
|
21
|
-
process.emitWarning(
|
|
22
|
-
"S3 configuration options passed at root level of the plugin's providerOptions is deprecated and will be removed in a future release. Please wrap them inside the 's3Options:{}' property."
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const config = { ...s3Options, ...legacyS3Options };
|
|
27
|
-
|
|
28
|
-
const S3 = new AWS.S3({
|
|
29
|
-
apiVersion: '2006-03-01',
|
|
30
|
-
...config,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const filePrefix = rootPath ? `${rootPath.replace(/\/+$/, '')}/` : '';
|
|
34
|
-
|
|
35
|
-
const getFileKey = (file) => {
|
|
36
|
-
const path = file.path ? `${file.path}/` : '';
|
|
37
|
-
|
|
38
|
-
return `${filePrefix}${path}${file.hash}${file.ext}`;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const ACL = getOr('public-read', ['params', 'ACL'], config);
|
|
42
|
-
|
|
43
|
-
const upload = (file, customParams = {}) =>
|
|
44
|
-
new Promise((resolve, reject) => {
|
|
45
|
-
// upload file on S3 bucket
|
|
46
|
-
const fileKey = getFileKey(file);
|
|
47
|
-
S3.upload(
|
|
48
|
-
{
|
|
49
|
-
Key: fileKey,
|
|
50
|
-
Body: file.stream || Buffer.from(file.buffer, 'binary'),
|
|
51
|
-
ACL,
|
|
52
|
-
ContentType: file.mime,
|
|
53
|
-
...customParams,
|
|
54
|
-
},
|
|
55
|
-
(err, data) => {
|
|
56
|
-
if (err) {
|
|
57
|
-
return reject(err);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// set the bucket file url
|
|
61
|
-
if (assertUrlProtocol(data.Location)) {
|
|
62
|
-
file.url = baseUrl ? `${baseUrl}/${fileKey}` : data.Location;
|
|
63
|
-
} else {
|
|
64
|
-
// Default protocol to https protocol
|
|
65
|
-
file.url = `https://${data.Location}`;
|
|
66
|
-
}
|
|
67
|
-
resolve();
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
isPrivate() {
|
|
74
|
-
return ACL === 'private';
|
|
75
|
-
},
|
|
76
|
-
/**
|
|
77
|
-
* @param {Object} file
|
|
78
|
-
* @param {string} file.path
|
|
79
|
-
* @param {string} file.hash
|
|
80
|
-
* @param {string} file.ext
|
|
81
|
-
* @param {Object} customParams
|
|
82
|
-
* @returns {Promise<{url: string}>}
|
|
83
|
-
*/
|
|
84
|
-
getSignedUrl(file, customParams = {}) {
|
|
85
|
-
// Do not sign the url if it does not come from the same bucket.
|
|
86
|
-
const { bucket } = getBucketFromUrl(file.url);
|
|
87
|
-
if (bucket !== config.params.Bucket) {
|
|
88
|
-
return { url: file.url };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
const fileKey = getFileKey(file);
|
|
93
|
-
|
|
94
|
-
S3.getSignedUrl(
|
|
95
|
-
'getObject',
|
|
96
|
-
{
|
|
97
|
-
Bucket: config.params.Bucket,
|
|
98
|
-
Key: fileKey,
|
|
99
|
-
Expires: getOr(15 * 60, ['params', 'signedUrlExpires'], config), // 15 minutes
|
|
100
|
-
},
|
|
101
|
-
(err, url) => {
|
|
102
|
-
if (err) {
|
|
103
|
-
return reject(err);
|
|
104
|
-
}
|
|
105
|
-
resolve({ url });
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
},
|
|
110
|
-
uploadStream(file, customParams = {}) {
|
|
111
|
-
return upload(file, customParams);
|
|
112
|
-
},
|
|
113
|
-
upload(file, customParams = {}) {
|
|
114
|
-
return upload(file, customParams);
|
|
115
|
-
},
|
|
116
|
-
delete(file, customParams = {}) {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
// delete file on S3 bucket
|
|
119
|
-
const fileKey = getFileKey(file);
|
|
120
|
-
S3.deleteObject(
|
|
121
|
-
{
|
|
122
|
-
Key: fileKey,
|
|
123
|
-
...customParams,
|
|
124
|
-
},
|
|
125
|
-
(err) => {
|
|
126
|
-
if (err) {
|
|
127
|
-
return reject(err);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
resolve();
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
},
|
|
137
|
-
};
|
package/lib/utils.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const ENDPOINT_PATTERN = /^(.+\.)?s3[.-]([a-z0-9-]+)\./;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse the bucket name from a URL.
|
|
7
|
-
* See all URL formats in https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html
|
|
8
|
-
*
|
|
9
|
-
* @param {string} fileUrl - the URL to parse
|
|
10
|
-
* @returns {object} result
|
|
11
|
-
* @returns {string} result.bucket - the bucket name
|
|
12
|
-
* @returns {string} result.error - if any
|
|
13
|
-
*/
|
|
14
|
-
function getBucketFromUrl(fileUrl) {
|
|
15
|
-
const uri = new URL(fileUrl);
|
|
16
|
-
|
|
17
|
-
// S3://<bucket-name>/<key>
|
|
18
|
-
if (uri.protocol === 's3:') {
|
|
19
|
-
const bucket = uri.host;
|
|
20
|
-
|
|
21
|
-
if (!bucket) {
|
|
22
|
-
return { err: `Invalid S3 URI: no bucket: ${uri}` };
|
|
23
|
-
}
|
|
24
|
-
return { bucket };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!uri.host) {
|
|
28
|
-
return { err: `Invalid S3 URI: no hostname: ${uri}` };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const matches = uri.host.match(ENDPOINT_PATTERN);
|
|
32
|
-
if (!matches) {
|
|
33
|
-
return { err: `Invalid S3 URI: hostname does not appear to be a valid S3 endpoint: ${uri}` };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const prefix = matches[1];
|
|
37
|
-
// https://s3.amazonaws.com/<bucket-name>
|
|
38
|
-
if (!prefix) {
|
|
39
|
-
if (uri.pathname === '/') {
|
|
40
|
-
return { bucket: null };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const index = uri.pathname.indexOf('/', 1);
|
|
44
|
-
|
|
45
|
-
// https://s3.amazonaws.com/<bucket-name>
|
|
46
|
-
if (index === -1) {
|
|
47
|
-
return { bucket: uri.pathname.substring(1) };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// https://s3.amazonaws.com/<bucket-name>/
|
|
51
|
-
if (index === uri.pathname.length - 1) {
|
|
52
|
-
return { bucket: uri.pathname.substring(1, index) };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// https://s3.amazonaws.com/<bucket-name>/key
|
|
56
|
-
return { bucket: uri.pathname.substring(1, index) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// https://<bucket-name>.s3.amazonaws.com/
|
|
60
|
-
return { bucket: prefix.substring(0, prefix.length - 1) };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = { getBucketFromUrl };
|