@payloadcms/storage-s3 3.83.0-internal.06ac84e → 3.83.0-internal.ddc1147
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/adapter.d.ts +15 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +56 -0
- package/dist/adapter.js.map +1 -0
- package/dist/client/S3ClientUploadHandler.d.ts.map +1 -1
- package/dist/client/S3ClientUploadHandler.js +4 -3
- package/dist/client/S3ClientUploadHandler.js.map +1 -1
- package/dist/deleteFile.d.ts +12 -0
- package/dist/deleteFile.d.ts.map +1 -0
- package/dist/deleteFile.js +15 -0
- package/dist/deleteFile.js.map +1 -0
- package/dist/generateSignedURL.d.ts +2 -1
- package/dist/generateSignedURL.d.ts.map +1 -1
- package/dist/generateSignedURL.js +11 -5
- package/dist/generateSignedURL.js.map +1 -1
- package/dist/generateURL.d.ts +7 -4
- package/dist/generateURL.d.ts.map +1 -1
- package/dist/generateURL.js +11 -5
- package/dist/generateURL.js.map +1 -1
- package/dist/getFile.d.ts +27 -0
- package/dist/getFile.d.ts.map +1 -0
- package/dist/getFile.js +174 -0
- package/dist/getFile.js.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -43
- package/dist/index.js.map +1 -1
- package/dist/uploadFile.d.ts +16 -0
- package/dist/uploadFile.d.ts.map +1 -0
- package/dist/uploadFile.js +39 -0
- package/dist/uploadFile.js.map +1 -0
- package/package.json +4 -4
- package/dist/handleDelete.d.ts +0 -9
- package/dist/handleDelete.d.ts.map +0 -1
- package/dist/handleDelete.js +0 -11
- package/dist/handleDelete.js.map +0 -1
- package/dist/handleUpload.d.ts +0 -13
- package/dist/handleUpload.d.ts.map +0 -1
- package/dist/handleUpload.js +0 -37
- package/dist/handleUpload.js.map +0 -1
- package/dist/staticHandler.d.ts +0 -21
- package/dist/staticHandler.d.ts.map +0 -1
- package/dist/staticHandler.js +0 -170
- package/dist/staticHandler.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as AWS from '@aws-sdk/client-s3';
|
|
2
|
+
import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
|
|
3
|
+
import type { SignedDownloadsConfig } from './getFile.js';
|
|
4
|
+
interface CreateS3AdapterArgs {
|
|
5
|
+
acl?: 'private' | 'public-read';
|
|
6
|
+
bucket: string;
|
|
7
|
+
clientUploads?: ClientUploadsConfig;
|
|
8
|
+
config: AWS.S3ClientConfig;
|
|
9
|
+
getStorageClient: () => AWS.S3;
|
|
10
|
+
signedDownloads: SignedDownloadsConfig;
|
|
11
|
+
useCompositePrefixes?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function createS3Adapter({ acl, bucket, clientUploads, config, getStorageClient, signedDownloads, useCompositePrefixes, }: CreateS3AdapterArgs): Adapter;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAC9C,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAOzD,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;IAC1B,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC9B,eAAe,EAAE,qBAAqB,CAAA;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,MAAM,EACN,aAAa,EACb,MAAM,EACN,gBAAgB,EAChB,eAAe,EACf,oBAA4B,GAC7B,EAAE,mBAAmB,GAAG,OAAO,CA4D/B"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { deleteFile } from './deleteFile.js';
|
|
2
|
+
import { generateURL } from './generateURL.js';
|
|
3
|
+
import { getFile } from './getFile.js';
|
|
4
|
+
import { uploadFile } from './uploadFile.js';
|
|
5
|
+
export function createS3Adapter({ acl, bucket, clientUploads, config, getStorageClient, signedDownloads, useCompositePrefixes = false }) {
|
|
6
|
+
return ({ collection, prefix = '' })=>({
|
|
7
|
+
name: 's3',
|
|
8
|
+
clientUploads,
|
|
9
|
+
generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
|
|
10
|
+
bucket,
|
|
11
|
+
collectionPrefix: prefix,
|
|
12
|
+
endpoint: config.endpoint,
|
|
13
|
+
filename,
|
|
14
|
+
prefix: urlPrefix,
|
|
15
|
+
useCompositePrefixes
|
|
16
|
+
}),
|
|
17
|
+
handleDelete: ({ doc: { prefix: docPrefix = '' }, filename })=>deleteFile({
|
|
18
|
+
bucket,
|
|
19
|
+
client: getStorageClient(),
|
|
20
|
+
collectionPrefix: prefix,
|
|
21
|
+
docPrefix,
|
|
22
|
+
filename,
|
|
23
|
+
useCompositePrefixes
|
|
24
|
+
}),
|
|
25
|
+
handleUpload: async ({ data, file })=>{
|
|
26
|
+
await uploadFile({
|
|
27
|
+
acl,
|
|
28
|
+
bucket,
|
|
29
|
+
buffer: file.buffer,
|
|
30
|
+
client: getStorageClient(),
|
|
31
|
+
collectionPrefix: prefix,
|
|
32
|
+
docPrefix: data.prefix,
|
|
33
|
+
filename: file.filename,
|
|
34
|
+
mimeType: file.mimeType,
|
|
35
|
+
tempFilePath: file.tempFilePath,
|
|
36
|
+
useCompositePrefixes
|
|
37
|
+
});
|
|
38
|
+
return data;
|
|
39
|
+
},
|
|
40
|
+
staticHandler: (req, { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>getFile({
|
|
41
|
+
bucket,
|
|
42
|
+
client: getStorageClient(),
|
|
43
|
+
clientUploadContext,
|
|
44
|
+
collection,
|
|
45
|
+
collectionPrefix: prefix,
|
|
46
|
+
filename,
|
|
47
|
+
incomingHeaders: headers,
|
|
48
|
+
prefixQueryParam,
|
|
49
|
+
req,
|
|
50
|
+
signedDownloads,
|
|
51
|
+
useCompositePrefixes
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport type { SignedDownloadsConfig } from './getFile.js'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateS3AdapterArgs {\n acl?: 'private' | 'public-read'\n bucket: string\n clientUploads?: ClientUploadsConfig\n config: AWS.S3ClientConfig\n getStorageClient: () => AWS.S3\n signedDownloads: SignedDownloadsConfig\n useCompositePrefixes?: boolean\n}\n\nexport function createS3Adapter({\n acl,\n bucket,\n clientUploads,\n config,\n getStorageClient,\n signedDownloads,\n useCompositePrefixes = false,\n}: CreateS3AdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 's3',\n clientUploads,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n bucket,\n collectionPrefix: prefix,\n endpoint: config.endpoint,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ doc: { prefix: docPrefix = '' }, filename }) =>\n deleteFile({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }),\n\n handleUpload: async ({ data, file }) => {\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix: data.prefix,\n filename: file.filename,\n mimeType: file.mimeType,\n tempFilePath: file.tempFilePath,\n useCompositePrefixes,\n })\n\n return data\n },\n\n staticHandler: (\n req,\n { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } },\n ) =>\n getFile({\n bucket,\n client: getStorageClient(),\n clientUploadContext,\n collection,\n collectionPrefix: prefix,\n filename,\n incomingHeaders: headers,\n prefixQueryParam,\n req,\n signedDownloads,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateURL","getFile","uploadFile","createS3Adapter","acl","bucket","clientUploads","config","getStorageClient","signedDownloads","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","collectionPrefix","endpoint","handleDelete","doc","docPrefix","client","handleUpload","data","file","buffer","mimeType","tempFilePath","staticHandler","req","headers","params","clientUploadContext","prefixQueryParam","incomingHeaders"],"mappings":"AASA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAY5C,OAAO,SAASC,gBAAgB,EAC9BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,MAAM,EACNC,gBAAgB,EAChBC,eAAe,EACfC,uBAAuB,KAAK,EACR;IACpB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNP;YAEAN,aAAa,CAAC,EAAEc,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDf,YAAY;oBACVK;oBACAW,kBAAkBJ;oBAClBK,UAAUV,OAAOU,QAAQ;oBACzBH;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,cAAc,CAAC,EAAEC,KAAK,EAAEP,QAAQQ,YAAY,EAAE,EAAE,EAAEN,QAAQ,EAAE,GAC1Df,WAAW;oBACTM;oBACAgB,QAAQb;oBACRQ,kBAAkBJ;oBAClBQ;oBACAN;oBACAJ;gBACF;YAEFY,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE;gBACjC,MAAMtB,WAAW;oBACfE;oBACAC;oBACAoB,QAAQD,KAAKC,MAAM;oBACnBJ,QAAQb;oBACRQ,kBAAkBJ;oBAClBQ,WAAWG,KAAKX,MAAM;oBACtBE,UAAUU,KAAKV,QAAQ;oBACvBY,UAAUF,KAAKE,QAAQ;oBACvBC,cAAcH,KAAKG,YAAY;oBAC/BjB;gBACF;gBAEA,OAAOa;YACT;YAEAK,eAAe,CACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAElB,QAAQ,EAAEF,QAAQqB,gBAAgB,EAAE,EAAE,GAEhFhC,QAAQ;oBACNI;oBACAgB,QAAQb;oBACRwB;oBACArB;oBACAK,kBAAkBJ;oBAClBE;oBACAoB,iBAAiBJ;oBACjBG;oBACAJ;oBACApB;oBACAC;gBACF;QACJ,CAAA;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"S3ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/S3ClientUploadHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"S3ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/S3ClientUploadHandler.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB;;;;;;;aAyBa,OAAO,aAgBpD,CAAA"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
|
|
3
3
|
import { formatAdminURL } from 'payload/shared';
|
|
4
4
|
export const S3ClientUploadHandler = createClientUploadHandler({
|
|
5
|
-
handler: async ({ apiRoute, collectionSlug,
|
|
5
|
+
handler: async ({ apiRoute, collectionSlug, docPrefix, file, serverHandlerPath, serverURL })=>{
|
|
6
6
|
const endpointRoute = formatAdminURL({
|
|
7
7
|
apiRoute,
|
|
8
8
|
path: serverHandlerPath,
|
|
@@ -11,6 +11,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
|
|
|
11
11
|
const response = await fetch(endpointRoute, {
|
|
12
12
|
body: JSON.stringify({
|
|
13
13
|
collectionSlug,
|
|
14
|
+
docPrefix,
|
|
14
15
|
filename: file.name,
|
|
15
16
|
filesize: file.size,
|
|
16
17
|
mimeType: file.type
|
|
@@ -22,7 +23,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
|
|
|
22
23
|
const { errors } = await response.json();
|
|
23
24
|
throw new Error(errors.reduce((acc, err)=>`${acc ? `${acc}, ` : ''}${err.message}`, ''));
|
|
24
25
|
}
|
|
25
|
-
const { url } = await response.json();
|
|
26
|
+
const { docPrefix: returnedDocPrefix, url } = await response.json();
|
|
26
27
|
await fetch(url, {
|
|
27
28
|
body: file,
|
|
28
29
|
headers: {
|
|
@@ -32,7 +33,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
|
|
|
32
33
|
method: 'PUT'
|
|
33
34
|
});
|
|
34
35
|
return {
|
|
35
|
-
prefix
|
|
36
|
+
prefix: returnedDocPrefix
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/S3ClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/client/S3ClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nexport const S3ClientUploadHandler = createClientUploadHandler({\n handler: async ({ apiRoute, collectionSlug, docPrefix, file, serverHandlerPath, serverURL }) => {\n const endpointRoute = formatAdminURL({\n apiRoute,\n path: serverHandlerPath,\n serverURL,\n })\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: file.name,\n filesize: file.size,\n mimeType: file.type,\n }),\n credentials: 'include',\n method: 'POST',\n })\n\n if (!response.ok) {\n const { errors } = (await response.json()) as {\n errors: { message: string }[]\n }\n\n throw new Error(errors.reduce((acc, err) => `${acc ? `${acc}, ` : ''}${err.message}`, ''))\n }\n\n const { docPrefix: returnedDocPrefix, url } = (await response.json()) as {\n docPrefix: string\n url: string\n }\n\n await fetch(url, {\n body: file,\n headers: { 'Content-Length': file.size.toString(), 'Content-Type': file.type },\n method: 'PUT',\n })\n\n return { prefix: returnedDocPrefix }\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","S3ClientUploadHandler","handler","apiRoute","collectionSlug","docPrefix","file","serverHandlerPath","serverURL","endpointRoute","path","response","fetch","body","JSON","stringify","filename","name","filesize","size","mimeType","type","credentials","method","ok","errors","json","Error","reduce","acc","err","message","returnedDocPrefix","url","headers","toString","prefix"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,OAAO,MAAMC,wBAAwBF,0BAA0B;IAC7DG,SAAS,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,SAAS,EAAEC,IAAI,EAAEC,iBAAiB,EAAEC,SAAS,EAAE;QACzF,MAAMC,gBAAgBT,eAAe;YACnCG;YACAO,MAAMH;YACNC;QACF;QAEA,MAAMG,WAAW,MAAMC,MAAMH,eAAe;YAC1CI,MAAMC,KAAKC,SAAS,CAAC;gBACnBX;gBACAC;gBACAW,UAAUV,KAAKW,IAAI;gBACnBC,UAAUZ,KAAKa,IAAI;gBACnBC,UAAUd,KAAKe,IAAI;YACrB;YACAC,aAAa;YACbC,QAAQ;QACV;QAEA,IAAI,CAACZ,SAASa,EAAE,EAAE;YAChB,MAAM,EAAEC,MAAM,EAAE,GAAI,MAAMd,SAASe,IAAI;YAIvC,MAAM,IAAIC,MAAMF,OAAOG,MAAM,CAAC,CAACC,KAAKC,MAAQ,GAAGD,MAAM,GAAGA,IAAI,EAAE,CAAC,GAAG,KAAKC,IAAIC,OAAO,EAAE,EAAE;QACxF;QAEA,MAAM,EAAE1B,WAAW2B,iBAAiB,EAAEC,GAAG,EAAE,GAAI,MAAMtB,SAASe,IAAI;QAKlE,MAAMd,MAAMqB,KAAK;YACfpB,MAAMP;YACN4B,SAAS;gBAAE,kBAAkB5B,KAAKa,IAAI,CAACgB,QAAQ;gBAAI,gBAAgB7B,KAAKe,IAAI;YAAC;YAC7EE,QAAQ;QACV;QAEA,OAAO;YAAEa,QAAQJ;QAAkB;IACrC;AACF,GAAE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type * as AWS from '@aws-sdk/client-s3';
|
|
2
|
+
interface DeleteArgs {
|
|
3
|
+
bucket: string;
|
|
4
|
+
client: AWS.S3;
|
|
5
|
+
collectionPrefix?: string;
|
|
6
|
+
docPrefix: string;
|
|
7
|
+
filename: string;
|
|
8
|
+
useCompositePrefixes?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function deleteFile({ bucket, client, collectionPrefix, docPrefix, filename, useCompositePrefixes, }: DeleteArgs): Promise<void>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=deleteFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAI9C,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,oBAA4B,GAC7B,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
+
export async function deleteFile({ bucket, client, collectionPrefix = '', docPrefix, filename, useCompositePrefixes = false }) {
|
|
3
|
+
const key = getFileKey({
|
|
4
|
+
collectionPrefix,
|
|
5
|
+
docPrefix,
|
|
6
|
+
filename,
|
|
7
|
+
useCompositePrefixes
|
|
8
|
+
});
|
|
9
|
+
await client.deleteObject({
|
|
10
|
+
Bucket: bucket,
|
|
11
|
+
Key: key
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=deleteFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface DeleteArgs {\n bucket: string\n client: AWS.S3\n collectionPrefix?: string\n docPrefix: string\n filename: string\n useCompositePrefixes?: boolean\n}\n\nexport async function deleteFile({\n bucket,\n client,\n collectionPrefix = '',\n docPrefix,\n filename,\n useCompositePrefixes = false,\n}: DeleteArgs): Promise<void> {\n const key = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await client.deleteObject({\n Bucket: bucket,\n Key: key,\n })\n}\n"],"names":["getFileKey","deleteFile","bucket","client","collectionPrefix","docPrefix","filename","useCompositePrefixes","key","deleteObject","Bucket","Key"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,uBAAuB,KAAK,EACjB;IACX,MAAMC,MAAMR,WAAW;QACrBI;QACAC;QACAC;QACAC;IACF;IAEA,MAAMJ,OAAOM,YAAY,CAAC;QACxBC,QAAQR;QACRS,KAAKH;IACP;AACF"}
|
|
@@ -8,7 +8,8 @@ interface Args {
|
|
|
8
8
|
bucket: string;
|
|
9
9
|
collections: S3StorageOptions['collections'];
|
|
10
10
|
getStorageClient: () => AWS.S3;
|
|
11
|
+
useCompositePrefixes?: boolean;
|
|
11
12
|
}
|
|
12
|
-
export declare const getGenerateSignedURLHandler: ({ access, acl, bucket, collections, getStorageClient, }: Args) => PayloadHandler;
|
|
13
|
+
export declare const getGenerateSignedURLHandler: ({ access, acl, bucket, collections, getStorageClient, useCompositePrefixes, }: Args) => PayloadHandler;
|
|
13
14
|
export {};
|
|
14
15
|
//# sourceMappingURL=generateSignedURL.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAMzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;CAC/B;AAID,eAAO,MAAM,2BAA2B,
|
|
1
|
+
{"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAMzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAID,eAAO,MAAM,2BAA2B,kFAOrC,IAAI,KAAG,cA0ET,CAAA"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as AWS from '@aws-sdk/client-s3';
|
|
2
2
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
|
-
import
|
|
3
|
+
import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
4
4
|
import { APIError, Forbidden } from 'payload';
|
|
5
5
|
import { sanitizeFilename } from 'payload/shared';
|
|
6
6
|
const bytesToMB = (bytes)=>{
|
|
7
7
|
return bytes / 1024 / 1024;
|
|
8
8
|
};
|
|
9
9
|
const defaultAccess = ({ req })=>!!req.user;
|
|
10
|
-
export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucket, collections, getStorageClient })=>{
|
|
10
|
+
export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucket, collections, getStorageClient, useCompositePrefixes = false })=>{
|
|
11
11
|
return async (req)=>{
|
|
12
12
|
if (!req.json) {
|
|
13
13
|
throw new APIError('Content-Type expected to be application/json', 400);
|
|
@@ -16,12 +16,12 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
|
|
|
16
16
|
if (filesizeLimit === Infinity) {
|
|
17
17
|
filesizeLimit = undefined;
|
|
18
18
|
}
|
|
19
|
-
const { collectionSlug, filename, filesize, mimeType } = await req.json();
|
|
19
|
+
const { collectionSlug, docPrefix, filename, filesize, mimeType } = await req.json();
|
|
20
20
|
const collectionS3Config = collections[collectionSlug];
|
|
21
21
|
if (!collectionS3Config) {
|
|
22
22
|
throw new APIError(`Collection ${collectionSlug} was not found in S3 options`);
|
|
23
23
|
}
|
|
24
|
-
const
|
|
24
|
+
const collectionPrefix = typeof collectionS3Config === 'object' && collectionS3Config.prefix || '';
|
|
25
25
|
if (!await access({
|
|
26
26
|
collectionSlug,
|
|
27
27
|
req
|
|
@@ -29,7 +29,12 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
|
|
|
29
29
|
throw new Forbidden();
|
|
30
30
|
}
|
|
31
31
|
const sanitizedFilename = sanitizeFilename(filename);
|
|
32
|
-
const fileKey =
|
|
32
|
+
const fileKey = getFileKey({
|
|
33
|
+
collectionPrefix,
|
|
34
|
+
docPrefix: docPrefix || '',
|
|
35
|
+
filename: sanitizedFilename,
|
|
36
|
+
useCompositePrefixes
|
|
37
|
+
});
|
|
33
38
|
const signableHeaders = new Set();
|
|
34
39
|
if (filesizeLimit) {
|
|
35
40
|
if (filesize > filesizeLimit) {
|
|
@@ -49,6 +54,7 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
|
|
|
49
54
|
signableHeaders
|
|
50
55
|
});
|
|
51
56
|
return Response.json({
|
|
57
|
+
docPrefix: docPrefix || '',
|
|
52
58
|
url
|
|
53
59
|
});
|
|
54
60
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generateSignedURL.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler } from 'payload'\n\nimport * as AWS from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport
|
|
1
|
+
{"version":3,"sources":["../src/generateSignedURL.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler } from 'payload'\n\nimport * as AWS from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\nimport { sanitizeFilename } from 'payload/shared'\n\nimport type { S3StorageOptions } from './index.js'\n\nconst bytesToMB = (bytes: number) => {\n return bytes / 1024 / 1024\n}\n\ninterface Args {\n access?: ClientUploadsAccess\n acl?: 'private' | 'public-read'\n bucket: string\n collections: S3StorageOptions['collections']\n getStorageClient: () => AWS.S3\n useCompositePrefixes?: boolean\n}\n\nconst defaultAccess: Args['access'] = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n acl,\n bucket,\n collections,\n getStorageClient,\n useCompositePrefixes = false,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n let filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (filesizeLimit === Infinity) {\n filesizeLimit = undefined\n }\n\n const { collectionSlug, docPrefix, filename, filesize, mimeType } = (await req.json()) as {\n collectionSlug: string\n docPrefix?: string\n filename: string\n filesize: number\n mimeType: string\n }\n\n const collectionS3Config = collections[collectionSlug]\n if (!collectionS3Config) {\n throw new APIError(`Collection ${collectionSlug} was not found in S3 options`)\n }\n\n const collectionPrefix =\n (typeof collectionS3Config === 'object' && collectionS3Config.prefix) || ''\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n const sanitizedFilename = sanitizeFilename(filename)\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: docPrefix || '',\n filename: sanitizedFilename,\n useCompositePrefixes,\n })\n\n const signableHeaders = new Set<string>()\n\n if (filesizeLimit) {\n if (filesize > filesizeLimit) {\n throw new APIError(\n `Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit).toFixed(2)}MB, got: ${bytesToMB(filesize).toFixed(2)}MB`,\n 400,\n )\n }\n\n // Still force S3 to validate\n signableHeaders.add('content-length')\n }\n\n const url = await getSignedUrl(\n getStorageClient(),\n new AWS.PutObjectCommand({\n ACL: acl,\n Bucket: bucket,\n ContentLength: filesizeLimit ? Math.min(filesize, filesizeLimit) : undefined,\n ContentType: mimeType,\n Key: fileKey,\n }),\n {\n expiresIn: 600,\n signableHeaders,\n },\n )\n\n return Response.json({\n docPrefix: docPrefix || '',\n url,\n })\n }\n}\n"],"names":["AWS","getSignedUrl","getFileKey","APIError","Forbidden","sanitizeFilename","bytesToMB","bytes","defaultAccess","req","user","getGenerateSignedURLHandler","access","acl","bucket","collections","getStorageClient","useCompositePrefixes","json","filesizeLimit","payload","config","upload","limits","fileSize","Infinity","undefined","collectionSlug","docPrefix","filename","filesize","mimeType","collectionS3Config","collectionPrefix","prefix","sanitizedFilename","fileKey","signableHeaders","Set","toFixed","add","url","PutObjectCommand","ACL","Bucket","ContentLength","Math","min","ContentType","Key","expiresIn","Response"],"mappings":"AAGA,YAAYA,SAAS,qBAAoB;AACzC,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,SAASC,UAAU,QAAQ,6CAA4C;AACvE,SAASC,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAC7C,SAASC,gBAAgB,QAAQ,iBAAgB;AAIjD,MAAMC,YAAY,CAACC;IACjB,OAAOA,QAAQ,OAAO;AACxB;AAWA,MAAMC,gBAAgC,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;AAE7D,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,SAASJ,aAAa,EACtBK,GAAG,EACHC,MAAM,EACNC,WAAW,EACXC,gBAAgB,EAChBC,uBAAuB,KAAK,EACvB;IACL,OAAO,OAAOR;QACZ,IAAI,CAACA,IAAIS,IAAI,EAAE;YACb,MAAM,IAAIf,SAAS,gDAAgD;QACrE;QAEA,IAAIgB,gBAAgBV,IAAIW,OAAO,CAACC,MAAM,CAACC,MAAM,CAACC,MAAM,EAAEC;QAEtD,IAAIL,kBAAkBM,UAAU;YAC9BN,gBAAgBO;QAClB;QAEA,MAAM,EAAEC,cAAc,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMtB,IAAIS,IAAI;QAQnF,MAAMc,qBAAqBjB,WAAW,CAACY,eAAe;QACtD,IAAI,CAACK,oBAAoB;YACvB,MAAM,IAAI7B,SAAS,CAAC,WAAW,EAAEwB,eAAe,4BAA4B,CAAC;QAC/E;QAEA,MAAMM,mBACJ,AAAC,OAAOD,uBAAuB,YAAYA,mBAAmBE,MAAM,IAAK;QAE3E,IAAI,CAAE,MAAMtB,OAAO;YAAEe;YAAgBlB;QAAI,IAAK;YAC5C,MAAM,IAAIL;QACZ;QAEA,MAAM+B,oBAAoB9B,iBAAiBwB;QAC3C,MAAMO,UAAUlC,WAAW;YACzB+B;YACAL,WAAWA,aAAa;YACxBC,UAAUM;YACVlB;QACF;QAEA,MAAMoB,kBAAkB,IAAIC;QAE5B,IAAInB,eAAe;YACjB,IAAIW,WAAWX,eAAe;gBAC5B,MAAM,IAAIhB,SACR,CAAC,iCAAiC,EAAEG,UAAUa,eAAeoB,OAAO,CAAC,GAAG,SAAS,EAAEjC,UAAUwB,UAAUS,OAAO,CAAC,GAAG,EAAE,CAAC,EACrH;YAEJ;YAEA,6BAA6B;YAC7BF,gBAAgBG,GAAG,CAAC;QACtB;QAEA,MAAMC,MAAM,MAAMxC,aAChBe,oBACA,IAAIhB,IAAI0C,gBAAgB,CAAC;YACvBC,KAAK9B;YACL+B,QAAQ9B;YACR+B,eAAe1B,gBAAgB2B,KAAKC,GAAG,CAACjB,UAAUX,iBAAiBO;YACnEsB,aAAajB;YACbkB,KAAKb;QACP,IACA;YACEc,WAAW;YACXb;QACF;QAGF,OAAOc,SAASjC,IAAI,CAAC;YACnBU,WAAWA,aAAa;YACxBa;QACF;IACF;AACF,EAAC"}
|
package/dist/generateURL.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type * as AWS from '@aws-sdk/client-s3';
|
|
2
|
-
|
|
3
|
-
interface Args {
|
|
2
|
+
interface GenerateURLArgs {
|
|
4
3
|
bucket: string;
|
|
5
|
-
|
|
4
|
+
collectionPrefix?: string;
|
|
5
|
+
endpoint?: AWS.S3ClientConfig['endpoint'];
|
|
6
|
+
filename: string;
|
|
7
|
+
prefix: string;
|
|
8
|
+
useCompositePrefixes?: boolean;
|
|
6
9
|
}
|
|
7
|
-
export declare
|
|
10
|
+
export declare function generateURL({ bucket, collectionPrefix, endpoint, filename, prefix, useCompositePrefixes, }: GenerateURLArgs): string;
|
|
8
11
|
export {};
|
|
9
12
|
//# sourceMappingURL=generateURL.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAI9C,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,gBAAqB,EACrB,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAW1B"}
|
package/dist/generateURL.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
+
export function generateURL({ bucket, collectionPrefix = '', endpoint, filename, prefix, useCompositePrefixes = false }) {
|
|
3
|
+
const fileKey = getFileKey({
|
|
4
|
+
collectionPrefix,
|
|
5
|
+
docPrefix: prefix,
|
|
6
|
+
filename: encodeURIComponent(filename),
|
|
7
|
+
useCompositePrefixes
|
|
8
|
+
});
|
|
9
|
+
const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString();
|
|
10
|
+
return `${stringifiedEndpoint}/${bucket}/${fileKey}`;
|
|
11
|
+
}
|
|
6
12
|
|
|
7
13
|
//# sourceMappingURL=generateURL.js.map
|
package/dist/generateURL.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport
|
|
1
|
+
{"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface GenerateURLArgs {\n bucket: string\n collectionPrefix?: string\n endpoint?: AWS.S3ClientConfig['endpoint']\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\nexport function generateURL({\n bucket,\n collectionPrefix = '',\n endpoint,\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: prefix,\n filename: encodeURIComponent(filename),\n useCompositePrefixes,\n })\n\n const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString()\n\n return `${stringifiedEndpoint}/${bucket}/${fileKey}`\n}\n"],"names":["getFileKey","generateURL","bucket","collectionPrefix","endpoint","filename","prefix","useCompositePrefixes","fileKey","docPrefix","encodeURIComponent","stringifiedEndpoint","toString"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,SAASC,YAAY,EAC1BC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAMC,UAAUR,WAAW;QACzBG;QACAM,WAAWH;QACXD,UAAUK,mBAAmBL;QAC7BE;IACF;IAEA,MAAMI,sBAAsB,OAAOP,aAAa,WAAWA,WAAWA,UAAUQ;IAEhF,OAAO,GAAGD,oBAAoB,CAAC,EAAET,OAAO,CAAC,EAAEM,SAAS;AACtD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type * as AWS from '@aws-sdk/client-s3';
|
|
2
|
+
import type { CollectionConfig, PayloadRequest } from 'payload';
|
|
3
|
+
export type SignedDownloadsConfig = {
|
|
4
|
+
/** @default 7200 */
|
|
5
|
+
expiresIn?: number;
|
|
6
|
+
shouldUseSignedURL?(args: {
|
|
7
|
+
collection: CollectionConfig;
|
|
8
|
+
filename: string;
|
|
9
|
+
req: PayloadRequest;
|
|
10
|
+
}): boolean | Promise<boolean>;
|
|
11
|
+
} | boolean;
|
|
12
|
+
interface GetFileArgs {
|
|
13
|
+
bucket: string;
|
|
14
|
+
client: AWS.S3;
|
|
15
|
+
clientUploadContext?: unknown;
|
|
16
|
+
collection: CollectionConfig;
|
|
17
|
+
collectionPrefix?: string;
|
|
18
|
+
filename: string;
|
|
19
|
+
incomingHeaders?: Headers;
|
|
20
|
+
prefixQueryParam?: string;
|
|
21
|
+
req: PayloadRequest;
|
|
22
|
+
signedDownloads: SignedDownloadsConfig;
|
|
23
|
+
useCompositePrefixes?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix, filename, incomingHeaders, prefixQueryParam, req, signedDownloads, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=getFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAY/D,MAAM,MAAM,qBAAqB,GAC7B;IACE,oBAAoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,CAAC,IAAI,EAAE;QACxB,UAAU,EAAE,gBAAgB,CAAA;QAC5B,QAAQ,EAAE,MAAM,CAAA;QAChB,GAAG,EAAE,cAAc,CAAA;KACpB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC/B,GACD,OAAO,CAAA;AAEX,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,EAAE,qBAAqB,CAAA;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AA8BD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,gBAAqB,EACrB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8JjC"}
|
package/dist/getFile.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { GetObjectCommand } from '@aws-sdk/client-s3';
|
|
2
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
|
+
import { getFilePrefix as getDocPrefix, getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
4
|
+
import { getRangeRequestInfo } from 'payload/internal';
|
|
5
|
+
import { sanitizeFilename } from 'payload/shared';
|
|
6
|
+
const isNodeReadableStream = (body)=>{
|
|
7
|
+
return typeof body === 'object' && body !== null && 'pipe' in body && typeof body.pipe === 'function' && 'destroy' in body && typeof body.destroy === 'function';
|
|
8
|
+
};
|
|
9
|
+
const abortRequestAndDestroyStream = ({ abortController, object })=>{
|
|
10
|
+
try {
|
|
11
|
+
abortController.abort();
|
|
12
|
+
} catch {
|
|
13
|
+
/* noop */ }
|
|
14
|
+
if (object?.Body && isNodeReadableStream(object.Body)) {
|
|
15
|
+
object.Body.destroy();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export async function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix = '', filename, incomingHeaders, prefixQueryParam, req, signedDownloads, useCompositePrefixes = false }) {
|
|
19
|
+
let object = undefined;
|
|
20
|
+
let streamed = false;
|
|
21
|
+
const abortController = new AbortController();
|
|
22
|
+
if (req.signal) {
|
|
23
|
+
req.signal.addEventListener('abort', ()=>{
|
|
24
|
+
abortRequestAndDestroyStream({
|
|
25
|
+
abortController,
|
|
26
|
+
object
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const docPrefix = await getDocPrefix({
|
|
32
|
+
clientUploadContext,
|
|
33
|
+
collection,
|
|
34
|
+
filename,
|
|
35
|
+
prefixQueryParam,
|
|
36
|
+
req
|
|
37
|
+
});
|
|
38
|
+
const key = getFileKey({
|
|
39
|
+
collectionPrefix,
|
|
40
|
+
docPrefix,
|
|
41
|
+
filename: sanitizeFilename(filename),
|
|
42
|
+
useCompositePrefixes
|
|
43
|
+
});
|
|
44
|
+
if (signedDownloads && !clientUploadContext) {
|
|
45
|
+
let useSignedURL = true;
|
|
46
|
+
if (typeof signedDownloads === 'object' && typeof signedDownloads.shouldUseSignedURL === 'function') {
|
|
47
|
+
useSignedURL = await signedDownloads.shouldUseSignedURL({
|
|
48
|
+
collection,
|
|
49
|
+
filename,
|
|
50
|
+
req
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (useSignedURL) {
|
|
54
|
+
const command = new GetObjectCommand({
|
|
55
|
+
Bucket: bucket,
|
|
56
|
+
Key: key
|
|
57
|
+
});
|
|
58
|
+
const signedUrl = await getSignedUrl(client, command, typeof signedDownloads === 'object' ? signedDownloads : {
|
|
59
|
+
expiresIn: 7200
|
|
60
|
+
});
|
|
61
|
+
return Response.redirect(signedUrl, 302);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Get file size first for range validation and to set Content-Length header before streaming
|
|
65
|
+
const headObject = await client.headObject({
|
|
66
|
+
Bucket: bucket,
|
|
67
|
+
Key: key
|
|
68
|
+
});
|
|
69
|
+
const fileSize = headObject.ContentLength;
|
|
70
|
+
if (!fileSize) {
|
|
71
|
+
return new Response('Internal Server Error', {
|
|
72
|
+
status: 500
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Handle range request
|
|
76
|
+
const rangeHeader = req.headers.get('range');
|
|
77
|
+
const rangeResult = getRangeRequestInfo({
|
|
78
|
+
fileSize,
|
|
79
|
+
rangeHeader
|
|
80
|
+
});
|
|
81
|
+
if (rangeResult.type === 'invalid') {
|
|
82
|
+
return new Response(null, {
|
|
83
|
+
headers: new Headers(rangeResult.headers),
|
|
84
|
+
status: rangeResult.status
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const rangeForS3 = rangeResult.type === 'partial' ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}` : undefined;
|
|
88
|
+
let headers = new Headers(incomingHeaders);
|
|
89
|
+
// Add range-related headers from the result
|
|
90
|
+
for (const [headerKey, value] of Object.entries(rangeResult.headers)){
|
|
91
|
+
headers.append(headerKey, value);
|
|
92
|
+
}
|
|
93
|
+
headers.append('Content-Type', String(headObject.ContentType));
|
|
94
|
+
if (headObject.ETag) {
|
|
95
|
+
headers.append('ETag', headObject.ETag);
|
|
96
|
+
}
|
|
97
|
+
// Add Content-Security-Policy header for SVG files to prevent executable code
|
|
98
|
+
if (headObject.ContentType === 'image/svg+xml') {
|
|
99
|
+
headers.append('Content-Security-Policy', "script-src 'none'");
|
|
100
|
+
}
|
|
101
|
+
const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
|
|
102
|
+
const objectEtag = headObject.ETag;
|
|
103
|
+
if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
|
|
104
|
+
headers = collection.upload.modifyResponseHeaders({
|
|
105
|
+
headers
|
|
106
|
+
}) || headers;
|
|
107
|
+
}
|
|
108
|
+
if (etagFromHeaders && etagFromHeaders === objectEtag) {
|
|
109
|
+
return new Response(null, {
|
|
110
|
+
headers,
|
|
111
|
+
status: 304
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
object = await client.getObject({
|
|
115
|
+
Bucket: bucket,
|
|
116
|
+
Key: key,
|
|
117
|
+
Range: rangeForS3
|
|
118
|
+
}, {
|
|
119
|
+
abortSignal: abortController.signal
|
|
120
|
+
});
|
|
121
|
+
if (!object.Body) {
|
|
122
|
+
return new Response(null, {
|
|
123
|
+
status: 404,
|
|
124
|
+
statusText: 'Not Found'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (!isNodeReadableStream(object.Body)) {
|
|
128
|
+
req.payload.logger.error({
|
|
129
|
+
key,
|
|
130
|
+
msg: 'S3 object body is not a readable stream'
|
|
131
|
+
});
|
|
132
|
+
return new Response('Internal Server Error', {
|
|
133
|
+
status: 500
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const stream = object.Body;
|
|
137
|
+
stream.on('error', (err)=>{
|
|
138
|
+
req.payload.logger.error({
|
|
139
|
+
err,
|
|
140
|
+
key,
|
|
141
|
+
msg: 'Error while streaming S3 object (aborting)'
|
|
142
|
+
});
|
|
143
|
+
abortRequestAndDestroyStream({
|
|
144
|
+
abortController,
|
|
145
|
+
object
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
streamed = true;
|
|
149
|
+
return new Response(stream, {
|
|
150
|
+
headers,
|
|
151
|
+
status: rangeResult.status
|
|
152
|
+
});
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err && typeof err === 'object' && ('name' in err && (err.name === 'NoSuchKey' || err.name === 'NotFound') || 'httpStatusCode' in err && err.httpStatusCode === 404)) {
|
|
155
|
+
return new Response(null, {
|
|
156
|
+
status: 404,
|
|
157
|
+
statusText: 'Not Found'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
req.payload.logger.error(err);
|
|
161
|
+
return new Response('Internal Server Error', {
|
|
162
|
+
status: 500
|
|
163
|
+
});
|
|
164
|
+
} finally{
|
|
165
|
+
if (!streamed) {
|
|
166
|
+
abortRequestAndDestroyStream({
|
|
167
|
+
abortController,
|
|
168
|
+
object
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=getFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type { CollectionConfig, PayloadRequest } from 'payload'\nimport type { Readable } from 'stream'\n\nimport { GetObjectCommand } from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport {\n getFilePrefix as getDocPrefix,\n getFileKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo } from 'payload/internal'\nimport { sanitizeFilename } from 'payload/shared'\n\nexport type SignedDownloadsConfig =\n | {\n /** @default 7200 */\n expiresIn?: number\n shouldUseSignedURL?(args: {\n collection: CollectionConfig\n filename: string\n req: PayloadRequest\n }): boolean | Promise<boolean>\n }\n | boolean\n\ninterface GetFileArgs {\n bucket: string\n client: AWS.S3\n clientUploadContext?: unknown\n collection: CollectionConfig\n collectionPrefix?: string\n filename: string\n incomingHeaders?: Headers\n prefixQueryParam?: string\n req: PayloadRequest\n signedDownloads: SignedDownloadsConfig\n useCompositePrefixes?: boolean\n}\n\nconst isNodeReadableStream = (body: AWS.GetObjectOutput['Body']): body is Readable => {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'pipe' in body &&\n typeof body.pipe === 'function' &&\n 'destroy' in body &&\n typeof body.destroy === 'function'\n )\n}\n\nconst abortRequestAndDestroyStream = ({\n abortController,\n object,\n}: {\n abortController: AbortController\n object?: AWS.GetObjectOutput\n}) => {\n try {\n abortController.abort()\n } catch {\n /* noop */\n }\n if (object?.Body && isNodeReadableStream(object.Body)) {\n object.Body.destroy()\n }\n}\n\nexport async function getFile({\n bucket,\n client,\n clientUploadContext,\n collection,\n collectionPrefix = '',\n filename,\n incomingHeaders,\n prefixQueryParam,\n req,\n signedDownloads,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n let object: AWS.GetObjectOutput | undefined = undefined\n let streamed = false\n\n const abortController = new AbortController()\n if (req.signal) {\n req.signal.addEventListener('abort', () => {\n abortRequestAndDestroyStream({ abortController, object })\n })\n }\n\n try {\n const docPrefix = await getDocPrefix({\n clientUploadContext,\n collection,\n filename,\n prefixQueryParam,\n req,\n })\n\n const key = getFileKey({\n collectionPrefix,\n docPrefix,\n filename: sanitizeFilename(filename),\n useCompositePrefixes,\n })\n\n if (signedDownloads && !clientUploadContext) {\n let useSignedURL = true\n if (\n typeof signedDownloads === 'object' &&\n typeof signedDownloads.shouldUseSignedURL === 'function'\n ) {\n useSignedURL = await signedDownloads.shouldUseSignedURL({ collection, filename, req })\n }\n\n if (useSignedURL) {\n const command = new GetObjectCommand({ Bucket: bucket, Key: key })\n const signedUrl = await getSignedUrl(\n client,\n command,\n typeof signedDownloads === 'object' ? signedDownloads : { expiresIn: 7200 },\n )\n return Response.redirect(signedUrl, 302)\n }\n }\n\n // Get file size first for range validation and to set Content-Length header before streaming\n const headObject = await client.headObject({\n Bucket: bucket,\n Key: key,\n })\n const fileSize = headObject.ContentLength\n\n if (!fileSize) {\n return new Response('Internal Server Error', { status: 500 })\n }\n\n // Handle range request\n const rangeHeader = req.headers.get('range')\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const rangeForS3 =\n rangeResult.type === 'partial'\n ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}`\n : undefined\n\n let headers = new Headers(incomingHeaders)\n\n // Add range-related headers from the result\n for (const [headerKey, value] of Object.entries(rangeResult.headers)) {\n headers.append(headerKey, value)\n }\n\n headers.append('Content-Type', String(headObject.ContentType))\n if (headObject.ETag) {\n headers.append('ETag', headObject.ETag)\n }\n\n // Add Content-Security-Policy header for SVG files to prevent executable code\n if (headObject.ContentType === 'image/svg+xml') {\n headers.append('Content-Security-Policy', \"script-src 'none'\")\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = headObject.ETag\n\n if (\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (etagFromHeaders && etagFromHeaders === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n object = await client.getObject(\n {\n Bucket: bucket,\n Key: key,\n Range: rangeForS3,\n },\n { abortSignal: abortController.signal },\n )\n\n if (!object.Body) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n if (!isNodeReadableStream(object.Body)) {\n req.payload.logger.error({\n key,\n msg: 'S3 object body is not a readable stream',\n })\n return new Response('Internal Server Error', { status: 500 })\n }\n\n const stream = object.Body\n stream.on('error', (err: Error) => {\n req.payload.logger.error({\n err,\n key,\n msg: 'Error while streaming S3 object (aborting)',\n })\n abortRequestAndDestroyStream({ abortController, object })\n })\n\n streamed = true\n return new Response(stream, { headers, status: rangeResult.status })\n } catch (err) {\n if (\n err &&\n typeof err === 'object' &&\n (('name' in err && (err.name === 'NoSuchKey' || err.name === 'NotFound')) ||\n ('httpStatusCode' in err && err.httpStatusCode === 404))\n ) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n } finally {\n if (!streamed) {\n abortRequestAndDestroyStream({ abortController, object })\n }\n }\n}\n"],"names":["GetObjectCommand","getSignedUrl","getFilePrefix","getDocPrefix","getFileKey","getRangeRequestInfo","sanitizeFilename","isNodeReadableStream","body","pipe","destroy","abortRequestAndDestroyStream","abortController","object","abort","Body","getFile","bucket","client","clientUploadContext","collection","collectionPrefix","filename","incomingHeaders","prefixQueryParam","req","signedDownloads","useCompositePrefixes","undefined","streamed","AbortController","signal","addEventListener","docPrefix","key","useSignedURL","shouldUseSignedURL","command","Bucket","Key","signedUrl","expiresIn","Response","redirect","headObject","fileSize","ContentLength","status","rangeHeader","headers","get","rangeResult","type","Headers","rangeForS3","rangeStart","rangeEnd","headerKey","value","Object","entries","append","String","ContentType","ETag","etagFromHeaders","objectEtag","upload","modifyResponseHeaders","getObject","Range","abortSignal","statusText","payload","logger","error","msg","stream","on","err","name","httpStatusCode"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClientUploadsConfig, CollectionOptions } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
2
|
import type { Plugin, UploadCollectionSlug } from 'payload';
|
|
3
3
|
import * as AWS from '@aws-sdk/client-s3';
|
|
4
|
-
import type { SignedDownloadsConfig } from './
|
|
4
|
+
import type { SignedDownloadsConfig } from './getFile.js';
|
|
5
5
|
export type S3StorageOptions = {
|
|
6
6
|
/**
|
|
7
7
|
* Access control list for uploaded files.
|
|
@@ -62,6 +62,20 @@ export type S3StorageOptions = {
|
|
|
62
62
|
* Use pre-signed URLs for files downloading. Can be overriden per-collection.
|
|
63
63
|
*/
|
|
64
64
|
signedDownloads?: SignedDownloadsConfig;
|
|
65
|
+
/**
|
|
66
|
+
* When true, the collection-level prefix and document-level prefix are combined
|
|
67
|
+
* (compositional). When false (default), document prefix overrides collection
|
|
68
|
+
* prefix entirely.
|
|
69
|
+
*
|
|
70
|
+
* Example:
|
|
71
|
+
* - collection prefix: `collection-prefix/`
|
|
72
|
+
* - document prefix: `document-prefix/`
|
|
73
|
+
* - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`
|
|
74
|
+
* - resulting prefix with useCompositePrefixes=false: `document-prefix/`
|
|
75
|
+
*
|
|
76
|
+
* @default false
|
|
77
|
+
*/
|
|
78
|
+
useCompositePrefixes?: boolean;
|
|
65
79
|
};
|
|
66
80
|
type S3StoragePlugin = (storageS3Args: S3StorageOptions) => Plugin;
|
|
67
81
|
export declare const s3Storage: S3StoragePlugin;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAU,MAAM,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAIzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAE/B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B;;;;OAIG;IAEH,MAAM,EAAE,MAAM,CAAA;IAEd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAClB,MAAM,CACJ,oBAAoB,EAClB,CAAC;QACC,eAAe,CAAC,EAAE,qBAAqB,CAAA;KACxC,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,GACvC,IAAI,CACP,CACF,CAAA;IACD;;;;OAIG;IACH,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;IAE1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAA;IACvC;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,KAAK,eAAe,GAAG,CAAC,aAAa,EAAE,gBAAgB,KAAK,MAAM,CAAA;AAelE,eAAO,MAAM,SAAS,EAAE,eAmIrB,CAAA"}
|