From 5d3c0f9b7720268021b16fa788bcf42e4566ec4d Mon Sep 17 00:00:00 2001 From: Tobias Arends Date: Wed, 8 Jan 2025 11:03:39 +0100 Subject: [PATCH] feat(storage-s3): add cacheControlMaxAge option --- docs/upload/storage-adapters.mdx | 9 +++++++++ packages/storage-s3/src/index.ts | 23 ++++++++++++++++++++--- packages/storage-s3/src/staticHandler.ts | 14 +++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/docs/upload/storage-adapters.mdx b/docs/upload/storage-adapters.mdx index 1c7bff85f7d..74124ff448d 100644 --- a/docs/upload/storage-adapters.mdx +++ b/docs/upload/storage-adapters.mdx @@ -113,6 +113,15 @@ export default buildConfig({ 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. + +| Option | Description | Default | +| -------------------- | -------------------------------------------------------------------- | ------------------------------------------ | +| `enabled` | Whether or not to enable the plugin | `true` | +| `collections` | Collections to apply the s3 adapter to | | +| `cacheControlMaxAge` | Cache-Control max-age in seconds | `undefined` (Cache-Control header omitted) | + + + ## Azure Blob Storage [`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure) diff --git a/packages/storage-s3/src/index.ts b/packages/storage-s3/src/index.ts index d2a2efbe7cf..00c810c562a 100644 --- a/packages/storage-s3/src/index.ts +++ b/packages/storage-s3/src/index.ts @@ -20,14 +20,21 @@ export type S3StorageOptions = { */ acl?: 'private' | 'public-read' + /** * Bucket name to upload files to. * * Must follow [AWS S3 bucket naming conventions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html). */ - bucket: string + /** + * Cache-Control max-age in seconds + * + * @defaultvalue undefined + */ + cacheControlMaxAge?: number + /** * Collection options to apply the S3 adapter to. */ @@ -102,7 +109,12 @@ export const s3Storage: S3StoragePlugin = })(config) } -function s3StorageInternal({ acl, bucket, config = {} }: S3StorageOptions): Adapter { +function s3StorageInternal({ + acl, + bucket, + cacheControlMaxAge, + config = {}, +}: S3StorageOptions): Adapter { return ({ collection, prefix }): GeneratedAdapter => { let storageClient: AWS.S3 | null = null const getStorageClient: () => AWS.S3 = () => { @@ -124,7 +136,12 @@ function s3StorageInternal({ acl, bucket, config = {} }: S3StorageOptions): Adap getStorageClient, prefix, }), - staticHandler: getHandler({ bucket, collection, getStorageClient }), + staticHandler: getHandler({ + bucket, + cacheControlMaxAge, + collection, + getStorageClient, + }), } } } diff --git a/packages/storage-s3/src/staticHandler.ts b/packages/storage-s3/src/staticHandler.ts index 0ad3d47cc48..185d5327d44 100644 --- a/packages/storage-s3/src/staticHandler.ts +++ b/packages/storage-s3/src/staticHandler.ts @@ -7,6 +7,7 @@ import path from 'path' interface Args { bucket: string + cacheControlMaxAge?: number collection: CollectionConfig getStorageClient: () => AWS.S3 } @@ -21,7 +22,12 @@ const streamToBuffer = async (readableStream: any) => { return Buffer.concat(chunks) } -export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => { +export const getHandler = ({ + bucket, + cacheControlMaxAge, + collection, + getStorageClient, +}: Args): StaticHandler => { return async (req, { params: { filename } }) => { try { const prefix = await getFilePrefix({ collection, filename, req }) @@ -42,6 +48,9 @@ export const getHandler = ({ bucket, collection, getStorageClient }: Args): Stat return new Response(null, { headers: new Headers({ 'Accept-Ranges': String(object.AcceptRanges), + ...(cacheControlMaxAge + ? { 'Cache-Control': `public, max-age=${cacheControlMaxAge}` } + : {}), 'Content-Length': String(object.ContentLength), 'Content-Type': String(object.ContentType), ETag: String(object.ETag), @@ -55,6 +64,9 @@ export const getHandler = ({ bucket, collection, getStorageClient }: Args): Stat return new Response(bodyBuffer, { headers: new Headers({ 'Accept-Ranges': String(object.AcceptRanges), + ...(cacheControlMaxAge + ? { 'Cache-Control': `public, max-age=${cacheControlMaxAge}` } + : {}), 'Content-Length': String(object.ContentLength), 'Content-Type': String(object.ContentType), ETag: String(object.ETag),