@payloadcms/storage-s3 4.0.0-internal.e55ccef → 4.0.0-internal.ea6e8f8

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter.d.ts.map +1 -1
  3. package/dist/adapter.js +41 -19
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/copyFile.d.ts +13 -0
  6. package/dist/copyFile.d.ts.map +1 -0
  7. package/dist/copyFile.js +145 -0
  8. package/dist/copyFile.js.map +1 -0
  9. package/dist/deleteFile.d.ts +2 -5
  10. package/dist/deleteFile.d.ts.map +1 -1
  11. package/dist/deleteFile.js +2 -9
  12. package/dist/deleteFile.js.map +1 -1
  13. package/dist/generateURL.js +4 -4
  14. package/dist/generateURL.js.map +1 -1
  15. package/dist/generateUploadInstructions.d.ts +13 -0
  16. package/dist/generateUploadInstructions.d.ts.map +1 -0
  17. package/dist/generateUploadInstructions.js +70 -0
  18. package/dist/generateUploadInstructions.js.map +1 -0
  19. package/dist/getFile.d.ts +5 -4
  20. package/dist/getFile.d.ts.map +1 -1
  21. package/dist/getFile.js +22 -22
  22. package/dist/getFile.js.map +1 -1
  23. package/dist/index.d.ts +9 -18
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +14 -36
  26. package/dist/index.js.map +1 -1
  27. package/dist/uploadFile.d.ts +2 -5
  28. package/dist/uploadFile.d.ts.map +1 -1
  29. package/dist/uploadFile.js +3 -10
  30. package/dist/uploadFile.js.map +1 -1
  31. package/package.json +8 -8
  32. package/dist/client/S3ClientUploadHandler.d.ts +0 -9
  33. package/dist/client/S3ClientUploadHandler.d.ts.map +0 -1
  34. package/dist/client/S3ClientUploadHandler.js +0 -47
  35. package/dist/client/S3ClientUploadHandler.js.map +0 -1
  36. package/dist/exports/client.d.ts +0 -2
  37. package/dist/exports/client.d.ts.map +0 -1
  38. package/dist/exports/client.js +0 -3
  39. package/dist/exports/client.js.map +0 -1
  40. package/dist/generateSignedURL.d.ts +0 -15
  41. package/dist/generateSignedURL.d.ts.map +0 -1
  42. package/dist/generateSignedURL.js +0 -64
  43. package/dist/generateSignedURL.js.map +0 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@ pnpm add @payloadcms/storage-s3
15
15
  - Configure the `collections` object to specify which collections should use the AWS S3 adapter. The slug _must_ match one of your existing collection slugs.
16
16
  - The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.
17
17
  - When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
18
- - When deploying to Vercel, server uploads are limited with 4.5MB. Set `clientUploads` to `true` to do uploads directly on the client. You must allow CORS PUT method for the bucket to your website.
18
+ - When deploying to Vercel, server uploads are limited to 4.5MB. Set `clientUploads` to `true` to use upload instructions and send files directly to S3. You must allow CORS `PUT` requests from your website.
19
19
  - Configure `signedDownloads` (either globally of per-collection in `collections`) to use [presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) for files downloading. This can improve performance for large files (like videos) while still respecting your access control. Additionally, with `signedDownloads.shouldUseSignedURL` you can specify a condition whether Payload should use a presigned URL, if you want to use this feature only for specific files.
20
20
 
21
21
  ```ts
@@ -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;AAIzD,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,CAmE/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
+ import { generateUploadInstructions } from './generateUploadInstructions.js';
1
2
  import { generateURL } from './generateURL.js';
2
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
+ };
3
12
  return ({ collection, prefix = '' })=>({
4
13
  name: 's3',
5
- clientUploads,
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,
@@ -11,48 +32,49 @@ export function createS3Adapter({ acl, bucket, clientUploads, config, getStorage
11
32
  prefix: urlPrefix,
12
33
  useCompositePrefixes
13
34
  }),
14
- // Helpers below dynamic-import their @aws-sdk dependencies so the SDK only
15
- // loads on the first request that actually needs it.
16
- handleDelete: async ({ doc: { prefix: docPrefix = '' }, filename })=>{
17
- const { deleteFile } = await import('./deleteFile.js');
18
- return deleteFile({
35
+ uploadInstructions: {
36
+ enabled: Boolean(clientUploads),
37
+ generate: generateUploadInstructions({
38
+ access: typeof clientUploads === 'object' ? clientUploads.access : undefined,
39
+ acl,
19
40
  bucket,
20
- client: getStorageClient(),
21
41
  collectionPrefix: prefix,
22
- docPrefix,
23
- filename,
42
+ getStorageClient,
24
43
  useCompositePrefixes
25
- });
44
+ }),
45
+ requiresUploadReceipt: true,
46
+ useInAdmin: true
26
47
  },
27
- handleUpload: async ({ data, file })=>{
48
+ // Helpers below dynamic-import their @aws-sdk dependencies so the SDK only
49
+ // loads on the first request that actually needs it.
50
+ handleDelete: deleteStoredFile,
51
+ handleUpload: async ({ data, file, storageFilePath })=>{
28
52
  const { uploadFile } = await import('./uploadFile.js');
29
53
  await uploadFile({
30
54
  acl,
31
55
  bucket,
32
56
  buffer: file.buffer,
33
57
  client: getStorageClient(),
34
- collectionPrefix: prefix,
35
- docPrefix: data.prefix,
36
- filename: file.filename,
37
58
  mimeType: file.mimeType,
38
- tempFilePath: file.tempFilePath,
39
- useCompositePrefixes
59
+ storageFilePath,
60
+ tempFilePath: file.tempFilePath
40
61
  });
41
62
  return data;
42
63
  },
43
- staticHandler: async (req, { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>{
64
+ staticHandler: async (req, { doc, headers, params: { filename, operation, uploadReference } })=>{
44
65
  const { getFile } = await import('./getFile.js');
45
66
  return getFile({
46
67
  bucket,
47
68
  client: getStorageClient(),
48
- clientUploadContext,
49
69
  collection,
50
70
  collectionPrefix: prefix,
71
+ doc,
51
72
  filename,
52
73
  incomingHeaders: headers,
53
- prefixQueryParam,
74
+ operation,
54
75
  req,
55
76
  signedDownloads,
77
+ uploadReference,
56
78
  useCompositePrefixes
57
79
  });
58
80
  }
@@ -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 { 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 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 // 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 ({ doc: { prefix: docPrefix = '' }, filename }) => {\n const { deleteFile } = await import('./deleteFile.js')\n return deleteFile({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n },\n\n handleUpload: async ({ data, file }) => {\n const { uploadFile } = await import('./uploadFile.js')\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: async (\n req,\n { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } },\n ) => {\n const { getFile } = await import('./getFile.js')\n return 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}\n"],"names":["generateURL","createS3Adapter","acl","bucket","clientUploads","config","getStorageClient","signedDownloads","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","collectionPrefix","endpoint","handleDelete","doc","docPrefix","deleteFile","client","handleUpload","data","file","uploadFile","buffer","mimeType","tempFilePath","staticHandler","req","headers","params","clientUploadContext","prefixQueryParam","getFile","incomingHeaders"],"mappings":"AASA,SAASA,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;YACNP;YAEAJ,aAAa,CAAC,EAAEY,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDb,YAAY;oBACVG;oBACAW,kBAAkBJ;oBAClBK,UAAUV,OAAOU,QAAQ;oBACzBH;oBACAF,QAAQG;oBACRL;gBACF;YAEF,2EAA2E;YAC3E,qDAAqD;YACrDQ,cAAc,OAAO,EAAEC,KAAK,EAAEP,QAAQQ,YAAY,EAAE,EAAE,EAAEN,QAAQ,EAAE;gBAChE,MAAM,EAAEO,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,OAAOA,WAAW;oBAChBhB;oBACAiB,QAAQd;oBACRQ,kBAAkBJ;oBAClBQ;oBACAN;oBACAJ;gBACF;YACF;YAEAa,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE;gBACjC,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBACpC,MAAMA,WAAW;oBACftB;oBACAC;oBACAsB,QAAQF,KAAKE,MAAM;oBACnBL,QAAQd;oBACRQ,kBAAkBJ;oBAClBQ,WAAWI,KAAKZ,MAAM;oBACtBE,UAAUW,KAAKX,QAAQ;oBACvBc,UAAUH,KAAKG,QAAQ;oBACvBC,cAAcJ,KAAKI,YAAY;oBAC/BnB;gBACF;gBAEA,OAAOc;YACT;YAEAM,eAAe,OACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEpB,QAAQ,EAAEF,QAAQuB,gBAAgB,EAAE,EAAE;gBAEhF,MAAM,EAAEC,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBACjC,OAAOA,QAAQ;oBACb/B;oBACAiB,QAAQd;oBACR0B;oBACAvB;oBACAK,kBAAkBJ;oBAClBE;oBACAuB,iBAAiBL;oBACjBG;oBACAJ;oBACAtB;oBACAC;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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"}
@@ -2,11 +2,8 @@ import type * as AWS from '@aws-sdk/client-s3';
2
2
  interface DeleteArgs {
3
3
  bucket: string;
4
4
  client: AWS.S3;
5
- collectionPrefix?: string;
6
- docPrefix: string;
7
- filename: string;
8
- useCompositePrefixes?: boolean;
5
+ storageFilePath: string;
9
6
  }
10
- export declare function deleteFile({ bucket, client, collectionPrefix, docPrefix, filename, useCompositePrefixes, }: DeleteArgs): Promise<void>;
7
+ export declare function deleteFile({ bucket, client, storageFilePath }: DeleteArgs): Promise<void>;
11
8
  export {};
12
9
  //# sourceMappingURL=deleteFile.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAE9C,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,wBAAsB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/F"}
@@ -1,14 +1,7 @@
1
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
- export async function deleteFile({ bucket, client, collectionPrefix = '', docPrefix, filename, useCompositePrefixes = false }) {
3
- const { fileKey } = getFileKey({
4
- collectionPrefix,
5
- docPrefix,
6
- filename,
7
- useCompositePrefixes
8
- });
1
+ export async function deleteFile({ bucket, client, storageFilePath }) {
9
2
  await client.deleteObject({
10
3
  Bucket: bucket,
11
- Key: fileKey
4
+ Key: storageFilePath
12
5
  });
13
6
  }
14
7
 
@@ -1 +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 { fileKey } = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await client.deleteObject({\n Bucket: bucket,\n Key: fileKey,\n })\n}\n"],"names":["getFileKey","deleteFile","bucket","client","collectionPrefix","docPrefix","filename","useCompositePrefixes","fileKey","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,MAAM,EAAEC,OAAO,EAAE,GAAGR,WAAW;QAC7BI;QACAC;QACAC;QACAC;IACF;IAEA,MAAMJ,OAAOM,YAAY,CAAC;QACxBC,QAAQR;QACRS,KAAKH;IACP;AACF"}
1
+ {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\ninterface DeleteArgs {\n bucket: string\n client: AWS.S3\n storageFilePath: string\n}\n\nexport async function deleteFile({ bucket, client, storageFilePath }: DeleteArgs): Promise<void> {\n await client.deleteObject({\n Bucket: bucket,\n Key: storageFilePath,\n })\n}\n"],"names":["deleteFile","bucket","client","storageFilePath","deleteObject","Bucket","Key"],"mappings":"AAQA,OAAO,eAAeA,WAAW,EAAEC,MAAM,EAAEC,MAAM,EAAEC,eAAe,EAAc;IAC9E,MAAMD,OAAOE,YAAY,CAAC;QACxBC,QAAQJ;QACRK,KAAKH;IACP;AACF"}
@@ -1,14 +1,14 @@
1
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
1
+ import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
2
2
  import path from 'path';
3
3
  export function generateURL({ bucket, collectionPrefix = '', endpoint, filename, prefix, useCompositePrefixes = false }) {
4
- const { fileKey: rawFileKey } = getFileKey({
4
+ const { storageFilePath } = buildStoragePathData({
5
5
  collectionPrefix,
6
6
  docPrefix: prefix,
7
7
  filename,
8
8
  useCompositePrefixes
9
9
  });
10
- const dir = path.posix.dirname(rawFileKey);
11
- const encodedFilename = encodeURIComponent(path.posix.basename(rawFileKey));
10
+ const dir = path.posix.dirname(storageFilePath);
11
+ const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath));
12
12
  const fileKey = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename);
13
13
  const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString();
14
14
  return `${stringifiedEndpoint}/${bucket}/${fileKey}`;
@@ -1 +1 @@
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'\nimport path from 'path'\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: rawFileKey } = getFileKey({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n const dir = path.posix.dirname(rawFileKey)\n const encodedFilename = encodeURIComponent(path.posix.basename(rawFileKey))\n const fileKey = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename)\n\n const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString()\n return `${stringifiedEndpoint}/${bucket}/${fileKey}`\n}\n"],"names":["getFileKey","path","generateURL","bucket","collectionPrefix","endpoint","filename","prefix","useCompositePrefixes","fileKey","rawFileKey","docPrefix","dir","posix","dirname","encodedFilename","encodeURIComponent","basename","join","stringifiedEndpoint","toString"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AACvE,OAAOC,UAAU,OAAM;AAWvB,OAAO,SAASC,YAAY,EAC1BC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAM,EAAEC,SAASC,UAAU,EAAE,GAAGV,WAAW;QACzCI;QACAO,WAAWJ;QACXD;QACAE;IACF;IACA,MAAMI,MAAMX,KAAKY,KAAK,CAACC,OAAO,CAACJ;IAC/B,MAAMK,kBAAkBC,mBAAmBf,KAAKY,KAAK,CAACI,QAAQ,CAACP;IAC/D,MAAMD,UAAUG,QAAQ,MAAMG,kBAAkBd,KAAKY,KAAK,CAACK,IAAI,CAACN,KAAKG;IAErE,MAAMI,sBAAsB,OAAOd,aAAa,WAAWA,WAAWA,UAAUe;IAChF,OAAO,GAAGD,oBAAoB,CAAC,EAAEhB,OAAO,CAAC,EAAEM,SAAS;AACtD"}
1
+ {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\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 { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n const dir = path.posix.dirname(storageFilePath)\n const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath))\n const fileKey = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename)\n\n const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString()\n return `${stringifiedEndpoint}/${bucket}/${fileKey}`\n}\n"],"names":["buildStoragePathData","path","generateURL","bucket","collectionPrefix","endpoint","filename","prefix","useCompositePrefixes","storageFilePath","docPrefix","dir","posix","dirname","encodedFilename","encodeURIComponent","basename","fileKey","join","stringifiedEndpoint","toString"],"mappings":"AAEA,SAASA,oBAAoB,QAAQ,6CAA4C;AACjF,OAAOC,UAAU,OAAM;AAWvB,OAAO,SAASC,YAAY,EAC1BC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAM,EAAEC,eAAe,EAAE,GAAGT,qBAAqB;QAC/CI;QACAM,WAAWH;QACXD;QACAE;IACF;IACA,MAAMG,MAAMV,KAAKW,KAAK,CAACC,OAAO,CAACJ;IAC/B,MAAMK,kBAAkBC,mBAAmBd,KAAKW,KAAK,CAACI,QAAQ,CAACP;IAC/D,MAAMQ,UAAUN,QAAQ,MAAMG,kBAAkBb,KAAKW,KAAK,CAACM,IAAI,CAACP,KAAKG;IAErE,MAAMK,sBAAsB,OAAOd,aAAa,WAAWA,WAAWA,UAAUe;IAChF,OAAO,GAAGD,oBAAoB,CAAC,EAAEhB,OAAO,CAAC,EAAEc,SAAS;AACtD"}
@@ -0,0 +1,13 @@
1
+ import type { S3 } from '@aws-sdk/client-s3';
2
+ import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload';
3
+ interface Args {
4
+ access?: UploadInstructionsAccess;
5
+ acl?: 'private' | 'public-read';
6
+ bucket: string;
7
+ collectionPrefix: string;
8
+ getStorageClient: () => S3;
9
+ useCompositePrefixes?: boolean;
10
+ }
11
+ export declare const generateUploadInstructions: ({ access, acl, bucket, collectionPrefix, getStorageClient, useCompositePrefixes, }: Args) => GenerateUploadInstructions;
12
+ export {};
13
+ //# sourceMappingURL=generateUploadInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateUploadInstructions.d.ts","sourceRoot":"","sources":["../src/generateUploadInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,KAAK,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAQnF,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,wBAAwB,CAAA;IACjC,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,0BAA0B,uFAOpC,IAAI,KAAG,0BA4ET,CAAA"}
@@ -0,0 +1,70 @@
1
+ import { PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+ import { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities';
4
+ import { Forbidden } from 'payload';
5
+ import { assertClientUploadAllowed } from 'payload/internal';
6
+ export const generateUploadInstructions = ({ access, acl, bucket, collectionPrefix, getStorageClient, useCompositePrefixes = false })=>{
7
+ return async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req })=>{
8
+ if (!overrideAccess && (access ? !await access({
9
+ collectionSlug,
10
+ req
11
+ }) : !req.user)) {
12
+ throw new Forbidden(req.t);
13
+ }
14
+ assertClientUploadAllowed({
15
+ collection: req.payload.collections[collectionSlug]?.config,
16
+ filename,
17
+ mimeType
18
+ });
19
+ let filesizeLimit = req.payload.config.upload.limits?.fileSize;
20
+ if (filesizeLimit === Infinity) {
21
+ filesizeLimit = undefined;
22
+ }
23
+ const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({
24
+ collectionPrefix,
25
+ collectionSlug,
26
+ docPrefix,
27
+ filename,
28
+ req,
29
+ useCompositePrefixes
30
+ });
31
+ const signableHeaders = new Set([
32
+ 'content-type'
33
+ ]);
34
+ if (filesizeLimit) {
35
+ // Still force S3 to validate
36
+ signableHeaders.add('content-length');
37
+ }
38
+ const url = await getSignedUrl(getStorageClient(), new PutObjectCommand({
39
+ ACL: acl,
40
+ Bucket: bucket,
41
+ ContentLength: filesizeLimit ? Math.min(filesize, filesizeLimit) : undefined,
42
+ ContentType: mimeType,
43
+ IfNoneMatch: '*',
44
+ Key: storageFilePath
45
+ }), {
46
+ expiresIn: 600,
47
+ signableHeaders
48
+ });
49
+ return {
50
+ type: 'http',
51
+ file: {
52
+ filename: sanitizedFilename,
53
+ mimeType,
54
+ size: filesize,
55
+ uploadReference
56
+ },
57
+ request: {
58
+ headers: {
59
+ 'Content-Length': String(filesize),
60
+ 'Content-Type': mimeType,
61
+ 'If-None-Match': '*'
62
+ },
63
+ method: 'PUT',
64
+ url
65
+ }
66
+ };
67
+ };
68
+ };
69
+
70
+ //# sourceMappingURL=generateUploadInstructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generateUploadInstructions.ts"],"sourcesContent":["import type { S3 } from '@aws-sdk/client-s3'\nimport type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { PutObjectCommand } from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { Forbidden } from 'payload'\nimport { assertClientUploadAllowed } from 'payload/internal'\n\ninterface Args {\n access?: UploadInstructionsAccess\n acl?: 'private' | 'public-read'\n bucket: string\n collectionPrefix: string\n getStorageClient: () => S3\n useCompositePrefixes?: boolean\n}\n\nexport const generateUploadInstructions = ({\n access,\n acl,\n bucket,\n collectionPrefix,\n getStorageClient,\n useCompositePrefixes = false,\n}: Args): GenerateUploadInstructions => {\n return async ({\n collectionSlug,\n docPrefix,\n filename,\n filesize,\n mimeType,\n overrideAccess,\n req,\n }) => {\n if (!overrideAccess && (access ? !(await access({ collectionSlug, req })) : !req.user)) {\n throw new Forbidden(req.t)\n }\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename,\n mimeType,\n })\n let filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (filesizeLimit === Infinity) {\n filesizeLimit = undefined\n }\n\n const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n\n const signableHeaders = new Set<string>(['content-type'])\n\n if (filesizeLimit) {\n // Still force S3 to validate\n signableHeaders.add('content-length')\n }\n\n const url = await getSignedUrl(\n getStorageClient(),\n new PutObjectCommand({\n ACL: acl,\n Bucket: bucket,\n ContentLength: filesizeLimit ? Math.min(filesize, filesizeLimit) : undefined,\n ContentType: mimeType,\n IfNoneMatch: '*',\n Key: storageFilePath,\n }),\n {\n expiresIn: 600,\n signableHeaders,\n },\n )\n\n return {\n type: 'http',\n file: {\n filename: sanitizedFilename,\n mimeType,\n size: filesize,\n uploadReference,\n },\n request: {\n headers: {\n 'Content-Length': String(filesize),\n 'Content-Type': mimeType,\n 'If-None-Match': '*',\n },\n method: 'PUT',\n url,\n },\n }\n }\n}\n"],"names":["PutObjectCommand","getSignedUrl","resolveSignedURLKey","Forbidden","assertClientUploadAllowed","generateUploadInstructions","access","acl","bucket","collectionPrefix","getStorageClient","useCompositePrefixes","collectionSlug","docPrefix","filename","filesize","mimeType","overrideAccess","req","user","t","collection","payload","collections","config","filesizeLimit","upload","limits","fileSize","Infinity","undefined","sanitizedFilename","storageFilePath","uploadReference","signableHeaders","Set","add","url","ACL","Bucket","ContentLength","Math","min","ContentType","IfNoneMatch","Key","expiresIn","type","file","size","request","headers","String","method"],"mappings":"AAGA,SAASA,gBAAgB,QAAQ,qBAAoB;AACrD,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,SAASC,mBAAmB,QAAQ,6CAA4C;AAChF,SAASC,SAAS,QAAQ,UAAS;AACnC,SAASC,yBAAyB,QAAQ,mBAAkB;AAW5D,OAAO,MAAMC,6BAA6B,CAAC,EACzCC,MAAM,EACNC,GAAG,EACHC,MAAM,EACNC,gBAAgB,EAChBC,gBAAgB,EAChBC,uBAAuB,KAAK,EACvB;IACL,OAAO,OAAO,EACZC,cAAc,EACdC,SAAS,EACTC,QAAQ,EACRC,QAAQ,EACRC,QAAQ,EACRC,cAAc,EACdC,GAAG,EACJ;QACC,IAAI,CAACD,kBAAmBX,CAAAA,SAAS,CAAE,MAAMA,OAAO;YAAEM;YAAgBM;QAAI,KAAM,CAACA,IAAIC,IAAI,AAAD,GAAI;YACtF,MAAM,IAAIhB,UAAUe,IAAIE,CAAC;QAC3B;QAEAhB,0BAA0B;YACxBiB,YAAYH,IAAII,OAAO,CAACC,WAAW,CAACX,eAAe,EAAEY;YACrDV;YACAE;QACF;QACA,IAAIS,gBAAgBP,IAAII,OAAO,CAACE,MAAM,CAACE,MAAM,CAACC,MAAM,EAAEC;QAEtD,IAAIH,kBAAkBI,UAAU;YAC9BJ,gBAAgBK;QAClB;QAEA,MAAM,EAAEC,iBAAiB,EAAEC,eAAe,EAAEC,eAAe,EAAE,GAAG,MAAM/B,oBAAoB;YACxFO;YACAG;YACAC;YACAC;YACAI;YACAP;QACF;QAEA,MAAMuB,kBAAkB,IAAIC,IAAY;YAAC;SAAe;QAExD,IAAIV,eAAe;YACjB,6BAA6B;YAC7BS,gBAAgBE,GAAG,CAAC;QACtB;QAEA,MAAMC,MAAM,MAAMpC,aAChBS,oBACA,IAAIV,iBAAiB;YACnBsC,KAAK/B;YACLgC,QAAQ/B;YACRgC,eAAef,gBAAgBgB,KAAKC,GAAG,CAAC3B,UAAUU,iBAAiBK;YACnEa,aAAa3B;YACb4B,aAAa;YACbC,KAAKb;QACP,IACA;YACEc,WAAW;YACXZ;QACF;QAGF,OAAO;YACLa,MAAM;YACNC,MAAM;gBACJlC,UAAUiB;gBACVf;gBACAiC,MAAMlC;gBACNkB;YACF;YACAiB,SAAS;gBACPC,SAAS;oBACP,kBAAkBC,OAAOrC;oBACzB,gBAAgBC;oBAChB,iBAAiB;gBACnB;gBACAqC,QAAQ;gBACRhB;YACF;QACF;IACF;AACF,EAAC"}
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 } from 'payload';
2
+ import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload';
3
3
  export type SignedDownloadsConfig = {
4
4
  /** @default 7200 */
5
5
  expiresIn?: number;
@@ -12,16 +12,17 @@ export type SignedDownloadsConfig = {
12
12
  interface GetFileArgs {
13
13
  bucket: string;
14
14
  client: AWS.S3;
15
- clientUploadContext?: unknown;
16
15
  collection: CollectionConfig;
17
16
  collectionPrefix?: string;
17
+ doc?: TypeWithID;
18
18
  filename: string;
19
19
  incomingHeaders?: Headers;
20
- prefixQueryParam?: string;
20
+ operation?: FileHandlerOperation;
21
21
  req: PayloadRequest;
22
22
  signedDownloads: SignedDownloadsConfig;
23
+ uploadReference?: unknown;
23
24
  useCompositePrefixes?: boolean;
24
25
  }
25
- export declare function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix, filename, incomingHeaders, prefixQueryParam, req, signedDownloads, 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,MAAM,SAAS,CAAA;AAW/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"}
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"}