@strapi/provider-upload-aws-s3 0.0.0-next.dff425769af4d4d006725a10c395f59637403653 → 0.0.0-next.e41415e8ff5f565ff959667d5c5ba4f20bee013c

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/README.md CHANGED
@@ -150,14 +150,14 @@ module.exports = [
150
150
  "'self'",
151
151
  'data:',
152
152
  'blob:',
153
- 'dl.airtable.com',
153
+ 'market-assets.strapi.io',
154
154
  'yourBucketName.s3.yourRegion.amazonaws.com',
155
155
  ],
156
156
  'media-src': [
157
157
  "'self'",
158
158
  'data:',
159
159
  'blob:',
160
- 'dl.airtable.com',
160
+ 'market-assets.strapi.io',
161
161
  'yourBucketName.s3.yourRegion.amazonaws.com',
162
162
  ],
163
163
  upgradeInsecureRequests: null,
@@ -0,0 +1,46 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import type { ReadStream } from 'node:fs';
4
+ import AWS from 'aws-sdk';
5
+ interface File {
6
+ name: string;
7
+ alternativeText?: string;
8
+ caption?: string;
9
+ width?: number;
10
+ height?: number;
11
+ formats?: Record<string, unknown>;
12
+ hash: string;
13
+ ext?: string;
14
+ mime: string;
15
+ size: number;
16
+ url: string;
17
+ previewUrl?: string;
18
+ path?: string;
19
+ provider?: string;
20
+ provider_metadata?: Record<string, unknown>;
21
+ stream?: ReadStream;
22
+ buffer?: Buffer;
23
+ }
24
+ interface InitOptions extends Partial<AWS.S3.ClientConfiguration> {
25
+ baseUrl?: string;
26
+ rootPath?: string;
27
+ s3Options: AWS.S3.ClientConfiguration & {
28
+ params: {
29
+ Bucket: string;
30
+ ACL?: string;
31
+ signedUrlExpires?: string;
32
+ };
33
+ };
34
+ }
35
+ declare const _default: {
36
+ init({ baseUrl, rootPath, s3Options, ...legacyS3Options }: InitOptions): {
37
+ isPrivate(): boolean;
38
+ getSignedUrl(file: File): Promise<{
39
+ url: string;
40
+ }>;
41
+ uploadStream(file: File, customParams?: {}): Promise<void>;
42
+ upload(file: File, customParams?: {}): Promise<void>;
43
+ delete(file: File, customParams?: {}): Promise<void>;
44
+ };
45
+ };
46
+ export = _default;
package/dist/index.js ADDED
@@ -0,0 +1,117 @@
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 hasUrlProtocol(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
+ // if ACL is private and baseUrl is set, we need to warn the user
32
+ // signed url's will not have the baseUrl prefix
33
+ if (ACL === 'private' && baseUrl) {
34
+ process.emitWarning('You are using a private ACL with a baseUrl. This is not recommended as the files will be accessible without the baseUrl prefix.');
35
+ }
36
+ const upload = (file, customParams = {}) => new Promise((resolve, reject) => {
37
+ const fileKey = getFileKey(file);
38
+ if (!file.stream && !file.buffer) {
39
+ reject(new Error('Missing file stream or buffer'));
40
+ return;
41
+ }
42
+ const params = {
43
+ Key: fileKey,
44
+ Bucket: config.params.Bucket,
45
+ Body: file.stream || file.buffer,
46
+ ACL,
47
+ ContentType: file.mime,
48
+ ...customParams,
49
+ };
50
+ const onUploaded = (err, data) => {
51
+ if (err) {
52
+ return reject(err);
53
+ }
54
+ // set the bucket file url
55
+ if (baseUrl) {
56
+ // Construct the url with the baseUrl
57
+ file.url = `${baseUrl}/${fileKey}`;
58
+ }
59
+ else {
60
+ // Add the protocol if it is missing
61
+ // Some providers like DigitalOcean Spaces return the url without the protocol
62
+ file.url = hasUrlProtocol(data.Location) ? data.Location : `https://${data.Location}`;
63
+ }
64
+ resolve();
65
+ };
66
+ S3.upload(params, onUploaded);
67
+ });
68
+ return {
69
+ isPrivate() {
70
+ return ACL === 'private';
71
+ },
72
+ async getSignedUrl(file) {
73
+ // Do not sign the url if it does not come from the same bucket.
74
+ if (!(0, utils_1.isUrlFromBucket)(file.url, config.params.Bucket, baseUrl)) {
75
+ return { url: file.url };
76
+ }
77
+ const signedUrlExpires = (0, fp_1.getOr)(15 * 60, ['params', 'signedUrlExpires'], config); // 15 minutes
78
+ return new Promise((resolve, reject) => {
79
+ const fileKey = getFileKey(file);
80
+ S3.getSignedUrl('getObject', {
81
+ Bucket: config.params.Bucket,
82
+ Key: fileKey,
83
+ Expires: parseInt(signedUrlExpires, 10),
84
+ }, (err, url) => {
85
+ if (err) {
86
+ return reject(err);
87
+ }
88
+ resolve({ url });
89
+ });
90
+ });
91
+ },
92
+ uploadStream(file, customParams = {}) {
93
+ return upload(file, customParams);
94
+ },
95
+ upload(file, customParams = {}) {
96
+ return upload(file, customParams);
97
+ },
98
+ delete(file, customParams = {}) {
99
+ return new Promise((resolve, reject) => {
100
+ // delete file on S3 bucket
101
+ const fileKey = getFileKey(file);
102
+ S3.deleteObject({
103
+ Key: fileKey,
104
+ Bucket: config.params.Bucket,
105
+ ...customParams,
106
+ }, (err) => {
107
+ if (err) {
108
+ return reject(err);
109
+ }
110
+ resolve();
111
+ });
112
+ });
113
+ },
114
+ };
115
+ },
116
+ };
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function isUrlFromBucket(fileUrl: string, bucketName: string, bucketBaseUrl?: string): boolean;
package/dist/utils.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isUrlFromBucket = void 0;
4
+ const ENDPOINT_PATTERN = /^(.+\.)?s3[.-]([a-z0-9-]+)\./;
5
+ function isUrlFromBucket(fileUrl, bucketName, bucketBaseUrl = '') {
6
+ const url = new URL(fileUrl);
7
+ // Check if the file URL is using a base URL (e.g. a CDN).
8
+ // In this case, check if the file URL starts with the same base URL as the bucket URL.
9
+ if (bucketBaseUrl) {
10
+ const baseUrl = new URL(bucketBaseUrl);
11
+ return url.href.startsWith(baseUrl.href);
12
+ }
13
+ const { bucket } = getBucketFromAwsUrl(fileUrl);
14
+ if (bucket) {
15
+ return bucket === bucketName;
16
+ }
17
+ // File URL might be of an S3-compatible provider. (or an invalid URL)
18
+ // In this case, check if the bucket name appears in the URL host or path.
19
+ // e.g. https://minio.example.com/bucket-name/object-key
20
+ // e.g. https://bucket.nyc3.digitaloceanspaces.com/folder/img.png
21
+ return url.host.startsWith(`${bucketName}.`) || url.pathname.includes(`/${bucketName}/`);
22
+ }
23
+ exports.isUrlFromBucket = isUrlFromBucket;
24
+ /**
25
+ * Parse the bucket name from a URL.
26
+ * See all URL formats in https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html
27
+ *
28
+ * @param {string} fileUrl - the URL to parse
29
+ * @returns {object} result
30
+ * @returns {string} result.bucket - the bucket name
31
+ * @returns {string} result.err - if any
32
+ */
33
+ function getBucketFromAwsUrl(fileUrl) {
34
+ const url = new URL(fileUrl);
35
+ // S3://<bucket-name>/<key>
36
+ if (url.protocol === 's3:') {
37
+ const bucket = url.host;
38
+ if (!bucket) {
39
+ return { err: `Invalid S3 url: no bucket: ${url}` };
40
+ }
41
+ return { bucket };
42
+ }
43
+ if (!url.host) {
44
+ return { err: `Invalid S3 url: no hostname: ${url}` };
45
+ }
46
+ const matches = url.host.match(ENDPOINT_PATTERN);
47
+ if (!matches) {
48
+ return { err: `Invalid S3 url: hostname does not appear to be a valid S3 endpoint: ${url}` };
49
+ }
50
+ const prefix = matches[1];
51
+ // https://s3.amazonaws.com/<bucket-name>
52
+ if (!prefix) {
53
+ if (url.pathname === '/') {
54
+ return { bucket: null };
55
+ }
56
+ const index = url.pathname.indexOf('/', 1);
57
+ // https://s3.amazonaws.com/<bucket-name>
58
+ if (index === -1) {
59
+ return { bucket: url.pathname.substring(1) };
60
+ }
61
+ // https://s3.amazonaws.com/<bucket-name>/
62
+ if (index === url.pathname.length - 1) {
63
+ return { bucket: url.pathname.substring(1, index) };
64
+ }
65
+ // https://s3.amazonaws.com/<bucket-name>/key
66
+ return { bucket: url.pathname.substring(1, index) };
67
+ }
68
+ // https://<bucket-name>.s3.amazonaws.com/
69
+ return { bucket: prefix.substring(0, prefix.length - 1) };
70
+ }
71
+ //# 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,SAAgB,eAAe,CAAC,OAAe,EAAE,UAAkB,EAAE,aAAa,GAAG,EAAE;IACrF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAE7B,0DAA0D;IAC1D,uFAAuF;IACvF,IAAI,aAAa,EAAE;QACjB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1C;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAEhD,IAAI,MAAM,EAAE;QACV,OAAO,MAAM,KAAK,UAAU,CAAC;KAC9B;IAED,sEAAsE;IACtE,0EAA0E;IAC1E,wDAAwD;IACxD,iEAAiE;IACjE,OAAO,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC;AAC3F,CAAC;AArBD,0CAqBC;AAED;;;;;;;;GAQG;AACH,SAAS,mBAAmB,CAAC,OAAe;IAC1C,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/provider-upload-aws-s3",
3
- "version": "0.0.0-next.dff425769af4d4d006725a10c395f59637403653",
3
+ "version": "0.0.0-next.e41415e8ff5f565ff959667d5c5ba4f20bee013c",
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": "./lib",
33
- "directories": {
34
- "lib": "./lib"
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.1351.0",
48
+ "aws-sdk": "2.1372.0",
43
49
  "lodash": "4.17.21"
44
50
  },
51
+ "devDependencies": {
52
+ "@types/jest": "29.2.0",
53
+ "eslint-config-custom": "0.0.0-next.e41415e8ff5f565ff959667d5c5ba4f20bee013c",
54
+ "tsconfig": "0.0.0-next.e41415e8ff5f565ff959667d5c5ba4f20bee013c"
55
+ },
45
56
  "engines": {
46
57
  "node": ">=14.19.1 <=18.x.x",
47
58
  "npm": ">=6.0.0"
48
59
  },
49
- "gitHead": "dff425769af4d4d006725a10c395f59637403653"
60
+ "gitHead": "e41415e8ff5f565ff959667d5c5ba4f20bee013c"
50
61
  }
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules/
2
- .eslintrc.js
package/.eslintrc.js DELETED
@@ -1,4 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- extends: ['custom/back'],
4
- };
package/lib/index.js DELETED
@@ -1,139 +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
- // TODO V5: Migrate to aws-sdk v3
11
- require('aws-sdk/lib/maintenance_mode_message').suppress = true;
12
- const AWS = require('aws-sdk');
13
- const { getBucketFromUrl } = require('./utils');
14
-
15
- function assertUrlProtocol(url) {
16
- // Regex to test protocol like "http://", "https://"
17
- return /^\w*:\/\//.test(url);
18
- }
19
-
20
- module.exports = {
21
- init({ baseUrl = null, rootPath = null, s3Options, ...legacyS3Options }) {
22
- if (legacyS3Options) {
23
- process.emitWarning(
24
- "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."
25
- );
26
- }
27
-
28
- const config = { ...s3Options, ...legacyS3Options };
29
-
30
- const S3 = new AWS.S3({
31
- apiVersion: '2006-03-01',
32
- ...config,
33
- });
34
-
35
- const filePrefix = rootPath ? `${rootPath.replace(/\/+$/, '')}/` : '';
36
-
37
- const getFileKey = (file) => {
38
- const path = file.path ? `${file.path}/` : '';
39
-
40
- return `${filePrefix}${path}${file.hash}${file.ext}`;
41
- };
42
-
43
- const ACL = getOr('public-read', ['params', 'ACL'], config);
44
-
45
- const upload = (file, customParams = {}) =>
46
- new Promise((resolve, reject) => {
47
- // upload file on S3 bucket
48
- const fileKey = getFileKey(file);
49
- S3.upload(
50
- {
51
- Key: fileKey,
52
- Body: file.stream || Buffer.from(file.buffer, 'binary'),
53
- ACL,
54
- ContentType: file.mime,
55
- ...customParams,
56
- },
57
- (err, data) => {
58
- if (err) {
59
- return reject(err);
60
- }
61
-
62
- // set the bucket file url
63
- if (assertUrlProtocol(data.Location)) {
64
- file.url = baseUrl ? `${baseUrl}/${fileKey}` : data.Location;
65
- } else {
66
- // Default protocol to https protocol
67
- file.url = `https://${data.Location}`;
68
- }
69
- resolve();
70
- }
71
- );
72
- });
73
-
74
- return {
75
- isPrivate() {
76
- return ACL === 'private';
77
- },
78
- /**
79
- * @param {Object} file
80
- * @param {string} file.path
81
- * @param {string} file.hash
82
- * @param {string} file.ext
83
- * @param {Object} customParams
84
- * @returns {Promise<{url: string}>}
85
- */
86
- getSignedUrl(file, customParams = {}) {
87
- // Do not sign the url if it does not come from the same bucket.
88
- const { bucket } = getBucketFromUrl(file.url);
89
- if (bucket !== config.params.Bucket) {
90
- return { url: file.url };
91
- }
92
-
93
- return new Promise((resolve, reject) => {
94
- const fileKey = getFileKey(file);
95
-
96
- S3.getSignedUrl(
97
- 'getObject',
98
- {
99
- Bucket: config.params.Bucket,
100
- Key: fileKey,
101
- Expires: getOr(15 * 60, ['params', 'signedUrlExpires'], config), // 15 minutes
102
- },
103
- (err, url) => {
104
- if (err) {
105
- return reject(err);
106
- }
107
- resolve({ url });
108
- }
109
- );
110
- });
111
- },
112
- uploadStream(file, customParams = {}) {
113
- return upload(file, customParams);
114
- },
115
- upload(file, customParams = {}) {
116
- return upload(file, customParams);
117
- },
118
- delete(file, customParams = {}) {
119
- return new Promise((resolve, reject) => {
120
- // delete file on S3 bucket
121
- const fileKey = getFileKey(file);
122
- S3.deleteObject(
123
- {
124
- Key: fileKey,
125
- ...customParams,
126
- },
127
- (err) => {
128
- if (err) {
129
- return reject(err);
130
- }
131
-
132
- resolve();
133
- }
134
- );
135
- });
136
- },
137
- };
138
- },
139
- };
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 };