@payloadcms/storage-s3 4.0.0-internal.77191e5 → 4.0.0-internal.7d5c665

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
@@ -57,4 +57,4 @@ export default buildConfig({
57
57
 
58
58
  ### Configuration Options
59
59
 
60
- See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.
60
+ See the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,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,cAAc,CAAA;IACtB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,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,CAuE/B"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAGpB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,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,cAAc,CAAA;IACtB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,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,CAgF/B"}
package/dist/adapter.js CHANGED
@@ -1,8 +1,29 @@
1
1
  import { generateUploadInstructions } from './generateUploadInstructions.js';
2
2
  import { generateURL } from './generateURL.js';
3
3
  export function createS3Adapter({ acl, bucket, clientUploads, config, getStorageClient, signedDownloads, useCompositePrefixes = false }) {
4
+ const deleteStoredFile = async ({ storageFilePath })=>{
5
+ const { deleteFile } = await import('./deleteFile.js');
6
+ return deleteFile({
7
+ bucket,
8
+ client: getStorageClient(),
9
+ storageFilePath
10
+ });
11
+ };
4
12
  return ({ collection, prefix = '' })=>({
5
13
  name: 's3',
14
+ supportsTempFiles: true,
15
+ copyFile: async ({ from, req, to })=>{
16
+ const { copyS3File } = await import('./copyFile.js');
17
+ await copyS3File({
18
+ acl,
19
+ bucket,
20
+ client: getStorageClient(),
21
+ from,
22
+ req,
23
+ to
24
+ });
25
+ },
26
+ deleteFile: deleteStoredFile,
6
27
  generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
7
28
  bucket,
8
29
  collectionPrefix: prefix,
@@ -26,14 +47,7 @@ export function createS3Adapter({ acl, bucket, clientUploads, config, getStorage
26
47
  },
27
48
  // Helpers below dynamic-import their @aws-sdk dependencies so the SDK only
28
49
  // loads on the first request that actually needs it.
29
- handleDelete: async ({ storageFilePath })=>{
30
- const { deleteFile } = await import('./deleteFile.js');
31
- return deleteFile({
32
- bucket,
33
- client: getStorageClient(),
34
- storageFilePath
35
- });
36
- },
50
+ handleDelete: deleteStoredFile,
37
51
  handleUpload: async ({ data, file, storageFilePath })=>{
38
52
  const { uploadFile } = await import('./uploadFile.js');
39
53
  await uploadFile({
@@ -47,7 +61,7 @@ export function createS3Adapter({ acl, bucket, clientUploads, config, getStorage
47
61
  });
48
62
  return data;
49
63
  },
50
- staticHandler: async (req, { doc, headers, params: { filename, uploadReference } })=>{
64
+ staticHandler: async (req, { doc, headers, params: { filename, operation, uploadReference } })=>{
51
65
  const { getFile } = await import('./getFile.js');
52
66
  return getFile({
53
67
  bucket,
@@ -57,6 +71,7 @@ export function createS3Adapter({ acl, bucket, clientUploads, config, getStorage
57
71
  doc,
58
72
  filename,
59
73
  incomingHeaders: headers,
74
+ operation,
60
75
  req,
61
76
  signedDownloads,
62
77
  uploadReference,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { S3, S3ClientConfig } 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 { generateUploadInstructions } from './generateUploadInstructions.js'\nimport { generateURL } from './generateURL.js'\n\ninterface CreateS3AdapterArgs {\n acl?: 'private' | 'public-read'\n bucket: string\n clientUploads?: ClientUploadsConfig\n config: S3ClientConfig\n getStorageClient: () => 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\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 uploadInstructions: {\n enabled: Boolean(clientUploads),\n generate: generateUploadInstructions({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n acl,\n bucket,\n collectionPrefix: prefix,\n getStorageClient,\n useCompositePrefixes,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n // Helpers below dynamic-import their @aws-sdk dependencies so the SDK only\n // loads on the first request that actually needs it.\n handleDelete: async ({ storageFilePath }) => {\n const { deleteFile } = await import('./deleteFile.js')\n return deleteFile({\n bucket,\n client: getStorageClient(),\n storageFilePath,\n })\n },\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n const { uploadFile } = await import('./uploadFile.js')\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n tempFilePath: file.tempFilePath,\n })\n\n return data\n },\n\n staticHandler: async (req, { doc, headers, params: { filename, uploadReference } }) => {\n const { getFile } = await import('./getFile.js')\n return getFile({\n bucket,\n client: getStorageClient(),\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n req,\n signedDownloads,\n uploadReference,\n useCompositePrefixes,\n })\n },\n })\n}\n"],"names":["generateUploadInstructions","generateURL","createS3Adapter","acl","bucket","clientUploads","config","getStorageClient","signedDownloads","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","collectionPrefix","endpoint","uploadInstructions","enabled","Boolean","generate","access","undefined","requiresUploadReceipt","useInAdmin","handleDelete","storageFilePath","deleteFile","client","handleUpload","data","file","uploadFile","buffer","mimeType","tempFilePath","staticHandler","req","doc","headers","params","uploadReference","getFile","incomingHeaders"],"mappings":"AASA,SAASA,0BAA0B,QAAQ,kCAAiC;AAC5E,SAASC,WAAW,QAAQ,mBAAkB;AAY9C,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;YAENX,aAAa,CAAC,EAAEY,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDb,YAAY;oBACVG;oBACAW,kBAAkBJ;oBAClBK,UAAUV,OAAOU,QAAQ;oBACzBH;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,oBAAoB;gBAClBC,SAASC,QAAQd;gBACjBe,UAAUpB,2BAA2B;oBACnCqB,QAAQ,OAAOhB,kBAAkB,WAAWA,cAAcgB,MAAM,GAAGC;oBACnEnB;oBACAC;oBACAW,kBAAkBJ;oBAClBJ;oBACAE;gBACF;gBACAc,uBAAuB;gBACvBC,YAAY;YACd;YAEA,2EAA2E;YAC3E,qDAAqD;YACrDC,cAAc,OAAO,EAAEC,eAAe,EAAE;gBACtC,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,OAAOA,WAAW;oBAChBvB;oBACAwB,QAAQrB;oBACRmB;gBACF;YACF;YAEAG,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEL,eAAe,EAAE;gBAClD,MAAM,EAAEM,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,MAAMA,WAAW;oBACf7B;oBACAC;oBACA6B,QAAQF,KAAKE,MAAM;oBACnBL,QAAQrB;oBACR2B,UAAUH,KAAKG,QAAQ;oBACvBR;oBACAS,cAAcJ,KAAKI,YAAY;gBACjC;gBAEA,OAAOL;YACT;YAEAM,eAAe,OAAOC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAE3B,QAAQ,EAAE4B,eAAe,EAAE,EAAE;gBAChF,MAAM,EAAEC,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBACjC,OAAOA,QAAQ;oBACbtC;oBACAwB,QAAQrB;oBACRG;oBACAK,kBAAkBJ;oBAClB2B;oBACAzB;oBACA8B,iBAAiBJ;oBACjBF;oBACA7B;oBACAiC;oBACAhC;gBACF;YACF;QACF,CAAA;AACF"}
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { S3, S3ClientConfig } from '@aws-sdk/client-s3'\nimport type {\n Adapter,\n ClientUploadsConfig,\n DeleteFile,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport type { SignedDownloadsConfig } from './getFile.js'\n\nimport { generateUploadInstructions } from './generateUploadInstructions.js'\nimport { generateURL } from './generateURL.js'\n\ninterface CreateS3AdapterArgs {\n acl?: 'private' | 'public-read'\n bucket: string\n clientUploads?: ClientUploadsConfig\n config: S3ClientConfig\n getStorageClient: () => 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 const deleteStoredFile: DeleteFile = async ({ storageFilePath }) => {\n const { deleteFile } = await import('./deleteFile.js')\n return deleteFile({ bucket, client: getStorageClient(), storageFilePath })\n }\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 's3',\n supportsTempFiles: true,\n\n copyFile: async ({ from, req, to }) => {\n const { copyS3File } = await import('./copyFile.js')\n await copyS3File({ acl, bucket, client: getStorageClient(), from, req, to })\n },\n deleteFile: deleteStoredFile,\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 uploadInstructions: {\n enabled: Boolean(clientUploads),\n generate: generateUploadInstructions({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n acl,\n bucket,\n collectionPrefix: prefix,\n getStorageClient,\n useCompositePrefixes,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n // Helpers below dynamic-import their @aws-sdk dependencies so the SDK only\n // loads on the first request that actually needs it.\n handleDelete: deleteStoredFile,\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n const { uploadFile } = await import('./uploadFile.js')\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n tempFilePath: file.tempFilePath,\n })\n\n return data\n },\n\n staticHandler: async (\n req,\n { doc, headers, params: { filename, operation, uploadReference } },\n ) => {\n const { getFile } = await import('./getFile.js')\n return getFile({\n bucket,\n client: getStorageClient(),\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n req,\n signedDownloads,\n uploadReference,\n useCompositePrefixes,\n })\n },\n })\n}\n"],"names":["generateUploadInstructions","generateURL","createS3Adapter","acl","bucket","clientUploads","config","getStorageClient","signedDownloads","useCompositePrefixes","deleteStoredFile","storageFilePath","deleteFile","client","collection","prefix","name","supportsTempFiles","copyFile","from","req","to","copyS3File","filename","urlPrefix","collectionPrefix","endpoint","uploadInstructions","enabled","Boolean","generate","access","undefined","requiresUploadReceipt","useInAdmin","handleDelete","handleUpload","data","file","uploadFile","buffer","mimeType","tempFilePath","staticHandler","doc","headers","params","operation","uploadReference","getFile","incomingHeaders"],"mappings":"AAUA,SAASA,0BAA0B,QAAQ,kCAAiC;AAC5E,SAASC,WAAW,QAAQ,mBAAkB;AAY9C,OAAO,SAASC,gBAAgB,EAC9BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,MAAM,EACNC,gBAAgB,EAChBC,eAAe,EACfC,uBAAuB,KAAK,EACR;IACpB,MAAMC,mBAA+B,OAAO,EAAEC,eAAe,EAAE;QAC7D,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;QACpC,OAAOA,WAAW;YAAER;YAAQS,QAAQN;YAAoBI;QAAgB;IAC1E;IAEA,OAAO,CAAC,EAAEG,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNC,mBAAmB;YAEnBC,UAAU,OAAO,EAAEC,IAAI,EAAEC,GAAG,EAAEC,EAAE,EAAE;gBAChC,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,MAAMA,WAAW;oBAAEnB;oBAAKC;oBAAQS,QAAQN;oBAAoBY;oBAAMC;oBAAKC;gBAAG;YAC5E;YACAT,YAAYF;YAEZT,aAAa,CAAC,EAAEsB,QAAQ,EAAER,QAAQS,YAAY,EAAE,EAAE,GAChDvB,YAAY;oBACVG;oBACAqB,kBAAkBV;oBAClBW,UAAUpB,OAAOoB,QAAQ;oBACzBH;oBACAR,QAAQS;oBACRf;gBACF;YAEFkB,oBAAoB;gBAClBC,SAASC,QAAQxB;gBACjByB,UAAU9B,2BAA2B;oBACnC+B,QAAQ,OAAO1B,kBAAkB,WAAWA,cAAc0B,MAAM,GAAGC;oBACnE7B;oBACAC;oBACAqB,kBAAkBV;oBAClBR;oBACAE;gBACF;gBACAwB,uBAAuB;gBACvBC,YAAY;YACd;YAEA,2EAA2E;YAC3E,qDAAqD;YACrDC,cAAczB;YAEd0B,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE3B,eAAe,EAAE;gBAClD,MAAM,EAAE4B,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,MAAMA,WAAW;oBACfpC;oBACAC;oBACAoC,QAAQF,KAAKE,MAAM;oBACnB3B,QAAQN;oBACRkC,UAAUH,KAAKG,QAAQ;oBACvB9B;oBACA+B,cAAcJ,KAAKI,YAAY;gBACjC;gBAEA,OAAOL;YACT;YAEAM,eAAe,OACbvB,KACA,EAAEwB,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEvB,QAAQ,EAAEwB,SAAS,EAAEC,eAAe,EAAE,EAAE;gBAElE,MAAM,EAAEC,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBACjC,OAAOA,QAAQ;oBACb7C;oBACAS,QAAQN;oBACRO;oBACAW,kBAAkBV;oBAClB6B;oBACArB;oBACA2B,iBAAiBL;oBACjBE;oBACA3B;oBACAZ;oBACAwC;oBACAvC;gBACF;YACF;QACF,CAAA;AACF"}
@@ -0,0 +1,13 @@
1
+ import type { S3 } from '@aws-sdk/client-s3';
2
+ import type { PayloadRequest } from 'payload';
3
+ type CopyS3FileArgs = {
4
+ acl?: 'private' | 'public-read';
5
+ bucket: string;
6
+ client: S3;
7
+ from: string;
8
+ req?: PayloadRequest;
9
+ to: string;
10
+ };
11
+ export declare const copyS3File: ({ acl, bucket, client, from, req, to, }: CopyS3FileArgs) => Promise<void>;
12
+ export {};
13
+ //# sourceMappingURL=copyFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyFile.d.ts","sourceRoot":"","sources":["../src/copyFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,cAAc,GAAG;IACpB,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,EAAE,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAKD,eAAO,MAAM,UAAU,4CAOpB,cAAc,KAAG,OAAO,CAAC,IAAI,CAyI/B,CAAA"}
@@ -0,0 +1,145 @@
1
+ const singleCopyLimit = 5 * 1024 ** 3;
2
+ const partSize = 256 * 1024 ** 2;
3
+ export const copyS3File = async ({ acl, bucket, client, from, req, to })=>{
4
+ if (from === to) {
5
+ throw new Error('Storage copy requires different source and destination keys');
6
+ }
7
+ const source = await client.headObject({
8
+ Bucket: bucket,
9
+ Key: from
10
+ });
11
+ const size = source.ContentLength;
12
+ if (size === undefined) {
13
+ throw new Error('Storage source has no known length');
14
+ }
15
+ const copySource = `${encodeURIComponent(bucket)}/${from.split('/').map(encodeURIComponent).join('/')}`;
16
+ let hasCopied = false;
17
+ let destinationETag;
18
+ let destinationVersion;
19
+ try {
20
+ if (size <= singleCopyLimit) {
21
+ const copied = await client.copyObject({
22
+ ACL: acl,
23
+ Bucket: bucket,
24
+ CopySource: copySource,
25
+ CopySourceIfMatch: source.ETag,
26
+ IfNoneMatch: '*',
27
+ Key: to,
28
+ MetadataDirective: 'COPY',
29
+ ServerSideEncryption: source.ServerSideEncryption,
30
+ SSEKMSKeyId: source.SSEKMSKeyId,
31
+ TaggingDirective: 'COPY'
32
+ });
33
+ hasCopied = true;
34
+ destinationETag = copied.CopyObjectResult?.ETag;
35
+ destinationVersion = copied.VersionId;
36
+ } else {
37
+ const { TagSet = [] } = await client.getObjectTagging({
38
+ Bucket: bucket,
39
+ Key: from
40
+ });
41
+ const tagging = new URLSearchParams();
42
+ for (const tag of TagSet){
43
+ if (tag.Key && tag.Value !== undefined) {
44
+ tagging.set(tag.Key, tag.Value);
45
+ }
46
+ }
47
+ const upload = await client.createMultipartUpload({
48
+ ACL: acl,
49
+ Bucket: bucket,
50
+ CacheControl: source.CacheControl,
51
+ ContentDisposition: source.ContentDisposition,
52
+ ContentEncoding: source.ContentEncoding,
53
+ ContentLanguage: source.ContentLanguage,
54
+ ContentType: source.ContentType,
55
+ Expires: source.Expires,
56
+ Key: to,
57
+ Metadata: source.Metadata,
58
+ ServerSideEncryption: source.ServerSideEncryption,
59
+ SSEKMSKeyId: source.SSEKMSKeyId,
60
+ StorageClass: source.StorageClass,
61
+ Tagging: tagging.size ? tagging.toString() : undefined
62
+ });
63
+ if (!upload.UploadId) {
64
+ throw new Error('S3 did not return a multipart upload ID');
65
+ }
66
+ try {
67
+ const parts = [];
68
+ for(let partNumber = 1, start = 0; start < size; start += partSize, partNumber++){
69
+ const end = Math.min(start + partSize, size) - 1;
70
+ const part = await client.uploadPartCopy({
71
+ Bucket: bucket,
72
+ CopySource: copySource,
73
+ CopySourceIfMatch: source.ETag,
74
+ CopySourceRange: `bytes=${start}-${end}`,
75
+ Key: to,
76
+ PartNumber: partNumber,
77
+ UploadId: upload.UploadId
78
+ });
79
+ if (!part.CopyPartResult?.ETag) {
80
+ throw new Error(`S3 copy part ${partNumber} did not return an ETag`);
81
+ }
82
+ parts.push({
83
+ ETag: part.CopyPartResult.ETag,
84
+ PartNumber: partNumber
85
+ });
86
+ }
87
+ const copied = await client.completeMultipartUpload({
88
+ Bucket: bucket,
89
+ IfNoneMatch: '*',
90
+ Key: to,
91
+ MultipartUpload: {
92
+ Parts: parts
93
+ },
94
+ UploadId: upload.UploadId
95
+ });
96
+ hasCopied = true;
97
+ destinationETag = copied.ETag;
98
+ destinationVersion = copied.VersionId;
99
+ } catch (err) {
100
+ try {
101
+ await client.abortMultipartUpload({
102
+ Bucket: bucket,
103
+ Key: to,
104
+ UploadId: upload.UploadId
105
+ });
106
+ } catch (cleanupError) {
107
+ req?.payload.logger.error({
108
+ err: cleanupError,
109
+ msg: `Failed to abort S3 multipart copy at ${bucket}/${to} (upload ${upload.UploadId})`
110
+ });
111
+ }
112
+ throw err;
113
+ }
114
+ }
115
+ const destination = await client.headObject({
116
+ Bucket: bucket,
117
+ Key: to
118
+ });
119
+ if (destination.ContentLength !== size) {
120
+ throw new Error(`Copied storage object is not readable at its expected length: ${to}`);
121
+ }
122
+ } catch (err) {
123
+ if (hasCopied) {
124
+ try {
125
+ if (!destinationETag && (!destinationVersion || destinationVersion === 'null')) {
126
+ throw new Error('S3 copy did not return an ETag or version for safe cleanup');
127
+ }
128
+ await client.deleteObject({
129
+ Bucket: bucket,
130
+ IfMatch: destinationETag,
131
+ Key: to,
132
+ VersionId: destinationVersion
133
+ });
134
+ } catch (cleanupError) {
135
+ req?.payload.logger.error({
136
+ err: cleanupError,
137
+ msg: `Failed to remove unsuccessful S3 copy at ${bucket}/${to}`
138
+ });
139
+ }
140
+ }
141
+ throw err;
142
+ }
143
+ };
144
+
145
+ //# sourceMappingURL=copyFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/copyFile.ts"],"sourcesContent":["import type { S3 } from '@aws-sdk/client-s3'\nimport type { PayloadRequest } from 'payload'\n\ntype CopyS3FileArgs = {\n acl?: 'private' | 'public-read'\n bucket: string\n client: S3\n from: string\n req?: PayloadRequest\n to: string\n}\n\nconst singleCopyLimit = 5 * 1024 ** 3\nconst partSize = 256 * 1024 ** 2\n\nexport const copyS3File = async ({\n acl,\n bucket,\n client,\n from,\n req,\n to,\n}: CopyS3FileArgs): Promise<void> => {\n if (from === to) {\n throw new Error('Storage copy requires different source and destination keys')\n }\n\n const source = await client.headObject({ Bucket: bucket, Key: from })\n const size = source.ContentLength\n\n if (size === undefined) {\n throw new Error('Storage source has no known length')\n }\n\n const copySource = `${encodeURIComponent(bucket)}/${from.split('/').map(encodeURIComponent).join('/')}`\n\n let hasCopied = false\n let destinationETag: string | undefined\n let destinationVersion: string | undefined\n\n try {\n if (size <= singleCopyLimit) {\n const copied = await client.copyObject({\n ACL: acl,\n Bucket: bucket,\n CopySource: copySource,\n CopySourceIfMatch: source.ETag,\n IfNoneMatch: '*',\n Key: to,\n MetadataDirective: 'COPY',\n ServerSideEncryption: source.ServerSideEncryption,\n SSEKMSKeyId: source.SSEKMSKeyId,\n TaggingDirective: 'COPY',\n })\n hasCopied = true\n destinationETag = copied.CopyObjectResult?.ETag\n destinationVersion = copied.VersionId\n } else {\n const { TagSet = [] } = await client.getObjectTagging({ Bucket: bucket, Key: from })\n const tagging = new URLSearchParams()\n\n for (const tag of TagSet) {\n if (tag.Key && tag.Value !== undefined) {\n tagging.set(tag.Key, tag.Value)\n }\n }\n\n const upload = await client.createMultipartUpload({\n ACL: acl,\n Bucket: bucket,\n CacheControl: source.CacheControl,\n ContentDisposition: source.ContentDisposition,\n ContentEncoding: source.ContentEncoding,\n ContentLanguage: source.ContentLanguage,\n ContentType: source.ContentType,\n Expires: source.Expires,\n Key: to,\n Metadata: source.Metadata,\n ServerSideEncryption: source.ServerSideEncryption,\n SSEKMSKeyId: source.SSEKMSKeyId,\n StorageClass: source.StorageClass,\n Tagging: tagging.size ? tagging.toString() : undefined,\n })\n\n if (!upload.UploadId) {\n throw new Error('S3 did not return a multipart upload ID')\n }\n\n try {\n const parts = []\n\n for (let partNumber = 1, start = 0; start < size; start += partSize, partNumber++) {\n const end = Math.min(start + partSize, size) - 1\n const part = await client.uploadPartCopy({\n Bucket: bucket,\n CopySource: copySource,\n CopySourceIfMatch: source.ETag,\n CopySourceRange: `bytes=${start}-${end}`,\n Key: to,\n PartNumber: partNumber,\n UploadId: upload.UploadId,\n })\n\n if (!part.CopyPartResult?.ETag) {\n throw new Error(`S3 copy part ${partNumber} did not return an ETag`)\n }\n\n parts.push({ ETag: part.CopyPartResult.ETag, PartNumber: partNumber })\n }\n\n const copied = await client.completeMultipartUpload({\n Bucket: bucket,\n IfNoneMatch: '*',\n Key: to,\n MultipartUpload: { Parts: parts },\n UploadId: upload.UploadId,\n })\n hasCopied = true\n destinationETag = copied.ETag\n destinationVersion = copied.VersionId\n } catch (err) {\n try {\n await client.abortMultipartUpload({ Bucket: bucket, Key: to, UploadId: upload.UploadId })\n } catch (cleanupError) {\n req?.payload.logger.error({\n err: cleanupError,\n msg: `Failed to abort S3 multipart copy at ${bucket}/${to} (upload ${upload.UploadId})`,\n })\n }\n throw err\n }\n }\n\n const destination = await client.headObject({ Bucket: bucket, Key: to })\n\n if (destination.ContentLength !== size) {\n throw new Error(`Copied storage object is not readable at its expected length: ${to}`)\n }\n } catch (err) {\n if (hasCopied) {\n try {\n if (!destinationETag && (!destinationVersion || destinationVersion === 'null')) {\n throw new Error('S3 copy did not return an ETag or version for safe cleanup')\n }\n await client.deleteObject({\n Bucket: bucket,\n IfMatch: destinationETag,\n Key: to,\n VersionId: destinationVersion,\n })\n } catch (cleanupError) {\n req?.payload.logger.error({\n err: cleanupError,\n msg: `Failed to remove unsuccessful S3 copy at ${bucket}/${to}`,\n })\n }\n }\n throw err\n }\n}\n"],"names":["singleCopyLimit","partSize","copyS3File","acl","bucket","client","from","req","to","Error","source","headObject","Bucket","Key","size","ContentLength","undefined","copySource","encodeURIComponent","split","map","join","hasCopied","destinationETag","destinationVersion","copied","copyObject","ACL","CopySource","CopySourceIfMatch","ETag","IfNoneMatch","MetadataDirective","ServerSideEncryption","SSEKMSKeyId","TaggingDirective","CopyObjectResult","VersionId","TagSet","getObjectTagging","tagging","URLSearchParams","tag","Value","set","upload","createMultipartUpload","CacheControl","ContentDisposition","ContentEncoding","ContentLanguage","ContentType","Expires","Metadata","StorageClass","Tagging","toString","UploadId","parts","partNumber","start","end","Math","min","part","uploadPartCopy","CopySourceRange","PartNumber","CopyPartResult","push","completeMultipartUpload","MultipartUpload","Parts","err","abortMultipartUpload","cleanupError","payload","logger","error","msg","destination","deleteObject","IfMatch"],"mappings":"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"}
package/dist/getFile.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type * as AWS from '@aws-sdk/client-s3';
2
- import type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload';
2
+ import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload';
3
3
  export type SignedDownloadsConfig = {
4
4
  /** @default 7200 */
5
5
  expiresIn?: number;
@@ -17,11 +17,12 @@ interface GetFileArgs {
17
17
  doc?: TypeWithID;
18
18
  filename: string;
19
19
  incomingHeaders?: Headers;
20
+ operation?: FileHandlerOperation;
20
21
  req: PayloadRequest;
21
22
  signedDownloads: SignedDownloadsConfig;
22
23
  uploadReference?: unknown;
23
24
  useCompositePrefixes?: boolean;
24
25
  }
25
- export declare function getFile({ bucket, client, collection, collectionPrefix, doc, filename, incomingHeaders, req, signedDownloads, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
26
+ export declare function getFile({ bucket, client, collection, collectionPrefix, doc, filename, incomingHeaders, operation, req, signedDownloads, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
26
27
  export {};
27
28
  //# sourceMappingURL=getFile.d.ts.map
@@ -1 +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,UAAU,EAAE,MAAM,SAAS,CAAA;AAW3E,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,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,EAAE,qBAAqB,CAAA;IACtC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AA8BD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,GAAG,EACH,eAAe,EACf,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgKjC"}
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,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAWjG,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,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,SAAS,CAAC,EAAE,oBAAoB,CAAA;IAChC,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,EAAE,qBAAqB,CAAA;IACtC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AA8BD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,SAAkB,EAClB,GAAG,EACH,eAAe,EACf,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CA+JjC"}
package/dist/getFile.js CHANGED
@@ -14,7 +14,8 @@ const abortRequestAndDestroyStream = ({ abortController, object })=>{
14
14
  object.Body.destroy();
15
15
  }
16
16
  };
17
- export async function getFile({ bucket, client, collection, collectionPrefix = '', doc, filename, incomingHeaders, req, signedDownloads, uploadReference, useCompositePrefixes = false }) {
17
+ export async function getFile({ bucket, client, collection, collectionPrefix = '', doc, filename, incomingHeaders, operation = 'read', req, signedDownloads, uploadReference, useCompositePrefixes = false }) {
18
+ const isTransformSource = operation === 'transform';
18
19
  let object = undefined;
19
20
  let streamed = false;
20
21
  const abortController = new AbortController();
@@ -42,7 +43,7 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
42
43
  filename,
43
44
  useCompositePrefixes
44
45
  });
45
- if (signedDownloads && !uploadReference) {
46
+ if (signedDownloads && !uploadReference && !isTransformSource) {
46
47
  let useSignedURL = true;
47
48
  if (typeof signedDownloads === 'object' && typeof signedDownloads.shouldUseSignedURL === 'function') {
48
49
  useSignedURL = await signedDownloads.shouldUseSignedURL({
@@ -62,7 +63,6 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
62
63
  return Response.redirect(signedUrl, 302);
63
64
  }
64
65
  }
65
- // Get file size first for range validation and to set Content-Length header before streaming
66
66
  const headObject = await client.headObject({
67
67
  Bucket: bucket,
68
68
  Key: storageFilePath
@@ -73,8 +73,7 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
73
73
  status: 500
74
74
  });
75
75
  }
76
- // Handle range request
77
- const rangeHeader = req.headers.get('range');
76
+ const rangeHeader = isTransformSource ? null : req.headers.get('range');
78
77
  const rangeResult = getRangeRequestInfo({
79
78
  fileSize,
80
79
  rangeHeader
@@ -87,7 +86,6 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
87
86
  }
88
87
  const rangeForS3 = rangeResult.type === 'partial' ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}` : undefined;
89
88
  let headers = new Headers(incomingHeaders);
90
- // Add range-related headers from the result
91
89
  for (const [headerKey, value] of Object.entries(rangeResult.headers)){
92
90
  headers.append(headerKey, value);
93
91
  }
@@ -101,12 +99,12 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
101
99
  }
102
100
  const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
103
101
  const objectEtag = headObject.ETag;
104
- if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
102
+ if (!isTransformSource && collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
105
103
  headers = collection.upload.modifyResponseHeaders({
106
104
  headers
107
105
  }) || headers;
108
106
  }
109
- if (etagFromHeaders && etagFromHeaders === objectEtag) {
107
+ if (!isTransformSource && etagFromHeaders && etagFromHeaders === objectEtag) {
110
108
  return new Response(null, {
111
109
  headers,
112
110
  status: 304
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type { CollectionConfig, PayloadRequest, TypeWithID } 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 buildStoragePathData,\n getFilePrefix as getDocPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal'\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 collection: CollectionConfig\n collectionPrefix?: string\n doc?: TypeWithID\n filename: string\n incomingHeaders?: Headers\n req: PayloadRequest\n signedDownloads: SignedDownloadsConfig\n uploadReference?: unknown\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 collection,\n collectionPrefix = '',\n doc,\n filename,\n incomingHeaders,\n req,\n signedDownloads,\n uploadReference,\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 collection,\n collectionPrefix,\n doc,\n filename,\n req,\n uploadReference,\n useCompositePrefixes,\n })\n\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n if (signedDownloads && !uploadReference) {\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: storageFilePath })\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: storageFilePath,\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 // Apply a restrictive policy to XML-family responses served through Payload.\n if (isXmlMimeType(headObject.ContentType)) {\n headers.append('Content-Security-Policy', uploadContentSecurityPolicy)\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: storageFilePath,\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: storageFilePath,\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: storageFilePath,\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","buildStoragePathData","getFilePrefix","getDocPrefix","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","isNodeReadableStream","body","pipe","destroy","abortRequestAndDestroyStream","abortController","object","abort","Body","getFile","bucket","client","collection","collectionPrefix","doc","filename","incomingHeaders","req","signedDownloads","uploadReference","useCompositePrefixes","undefined","streamed","AbortController","signal","addEventListener","docPrefix","storageFilePath","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","key","msg","stream","on","err","name","httpStatusCode"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } 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 buildStoragePathData,\n getFilePrefix as getDocPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal'\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 collection: CollectionConfig\n collectionPrefix?: string\n doc?: TypeWithID\n filename: string\n incomingHeaders?: Headers\n operation?: FileHandlerOperation\n req: PayloadRequest\n signedDownloads: SignedDownloadsConfig\n uploadReference?: unknown\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 collection,\n collectionPrefix = '',\n doc,\n filename,\n incomingHeaders,\n operation = 'read',\n req,\n signedDownloads,\n uploadReference,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n const isTransformSource = operation === 'transform'\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 collection,\n collectionPrefix,\n doc,\n filename,\n req,\n uploadReference,\n useCompositePrefixes,\n })\n\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n if (signedDownloads && !uploadReference && !isTransformSource) {\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: storageFilePath })\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 const headObject = await client.headObject({\n Bucket: bucket,\n Key: storageFilePath,\n })\n const fileSize = headObject.ContentLength\n\n if (!fileSize) {\n return new Response('Internal Server Error', { status: 500 })\n }\n\n const rangeHeader = isTransformSource ? null : 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 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 // Apply a restrictive policy to XML-family responses served through Payload.\n if (isXmlMimeType(headObject.ContentType)) {\n headers.append('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = headObject.ETag\n\n if (\n !isTransformSource &&\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 (!isTransformSource && 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: storageFilePath,\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: storageFilePath,\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: storageFilePath,\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","buildStoragePathData","getFilePrefix","getDocPrefix","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","isNodeReadableStream","body","pipe","destroy","abortRequestAndDestroyStream","abortController","object","abort","Body","getFile","bucket","client","collection","collectionPrefix","doc","filename","incomingHeaders","operation","req","signedDownloads","uploadReference","useCompositePrefixes","isTransformSource","undefined","streamed","AbortController","signal","addEventListener","docPrefix","storageFilePath","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","key","msg","stream","on","err","name","httpStatusCode"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/storage-s3",
3
- "version": "4.0.0-internal.77191e5",
3
+ "version": "4.0.0-internal.7d5c665",
4
4
  "description": "Payload storage adapter for Amazon S3",
5
5
  "homepage": "https://payloadcms.com",
6
6
  "repository": {
@@ -24,6 +24,11 @@
24
24
  "import": "./dist/index.js",
25
25
  "types": "./dist/index.d.ts",
26
26
  "default": "./dist/index.js"
27
+ },
28
+ "./copy-file": {
29
+ "import": "./dist/copyFile.js",
30
+ "types": "./dist/copyFile.d.ts",
31
+ "default": "./dist/copyFile.js"
27
32
  }
28
33
  },
29
34
  "main": "./dist/index.js",
@@ -35,14 +40,14 @@
35
40
  "@aws-sdk/client-s3": "^3.614.0",
36
41
  "@aws-sdk/lib-storage": "^3.614.0",
37
42
  "@aws-sdk/s3-request-presigner": "^3.614.0",
38
- "@payloadcms/plugin-cloud-storage": "4.0.0-internal.77191e5"
43
+ "@payloadcms/plugin-cloud-storage": "4.0.0-internal.7d5c665"
39
44
  },
40
45
  "devDependencies": {
41
46
  "@smithy/node-http-handler": "4.0.3",
42
- "payload": "4.0.0-internal.77191e5"
47
+ "payload": "4.0.0-internal.7d5c665"
43
48
  },
44
49
  "peerDependencies": {
45
- "payload": "4.0.0-internal.77191e5"
50
+ "payload": "4.0.0-internal.7d5c665"
46
51
  },
47
52
  "engines": {
48
53
  "node": ">=24.15.0"