Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework image and file fields to support arbitrary storage endpoints #9529

Merged
merged 5 commits into from
Mar 19, 2025

Conversation

emmatown
Copy link
Member

@emmatown emmatown commented Mar 4, 2025

This replaces the global storage config with storage config on image and file fields that directly defines how to upload/delete/get a url for a file so it can be used with any kind of storage provider.

export type StorageStrategy<TypeInfo extends BaseKeystoneTypeInfo> = {
  put(
    key: string,
    stream: Readable,
    meta: { contentType: string },
    context: KeystoneContext<TypeInfo>
  ): Promise<void>
  delete(key: string, context: KeystoneContext<TypeInfo>): Promise<void>
  url(key: string, context: KeystoneContext<TypeInfo>): MaybePromise<string>
}

The assets-local and assets-s3 examples have been updated to work with this though with this API, you could use any other storage provider.

Fixes #9419

@emmatown emmatown force-pushed the image-files-refactor branch 4 times, most recently from 9ee8eaa to e2bc89b Compare March 4, 2025 07:26
@gautamsi
Copy link
Member

gautamsi commented Mar 4, 2025

@emmatown since we are here again refactoring it, can you help us with something to create custom storage easily? Like if I want to have Azure storage or Google cloud storage?

It would be good to be able to register custom storage and use it with the file/image field.

I have been using my own field for s3 which generates 3-4 copy of image processed with sharp to help reduce any compute cost in dynamic image optimization. - https://github.com/keystonejs-contrib/k6-contrib/tree/main/packages/fields-s3images

do you see a way to do that with this new refactoring?

@emmatown
Copy link
Member Author

emmatown commented Mar 4, 2025

It would be good to be able to register custom storage and use it with the file/image field.

This PR is doing exactly that

@emmatown emmatown force-pushed the image-files-refactor branch from e2bc89b to d31873d Compare March 5, 2025 00:22
@dcousens dcousens changed the title Refactor image and file fields Rework image and file fields to support different backends Mar 5, 2025
@dcousens dcousens changed the title Rework image and file fields to support different backends Rework image and file fields to support arbitrary upstream storage endpoints (Azure, GCS, S3, et cetera) Mar 5, 2025
@dcousens dcousens changed the title Rework image and file fields to support arbitrary upstream storage endpoints (Azure, GCS, S3, et cetera) Rework image and file fields to support arbitrary storage endpoints (Azure, GCS, S3, et cetera) Mar 5, 2025
@dcousens
Copy link
Member

dcousens commented Mar 5, 2025

This pull request will additionally add an example for doing a few of the common ones (at least S3, maybe Azure and or GCS)

@dcousens dcousens changed the title Rework image and file fields to support arbitrary storage endpoints (Azure, GCS, S3, et cetera) Rework image and file fields to support arbitrary storage endpoints Mar 5, 2025
@emmatown emmatown force-pushed the image-files-refactor branch from a4fca87 to c135ce8 Compare March 5, 2025 01:34
@emmatown emmatown marked this pull request as ready for review March 5, 2025 01:34
@emmatown emmatown requested a review from dcousens March 5, 2025 01:35
@emmatown emmatown force-pushed the image-files-refactor branch from c135ce8 to dd3122e Compare March 5, 2025 01:56
Copy link

socket-security bot commented Mar 5, 2025

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher

View full report↗︎

@gautamsi
Copy link
Member

gautamsi commented Mar 5, 2025

It would be good to be able to register custom storage and use it with the file/image field.

This PR is doing exactly that

I see we still keeping multiple fields in database? (_id, .filename, .extension) etc? As there is no filtering applicable to this field, I am thinking we should convert into JSON field internally. This will help adding more data to the field I can add multiple image size in my custom implementation.

At least add a meta field <field>.meta so that custom implementation can have flexibility.

@dcousens
Copy link
Member

dcousens commented Mar 5, 2025

Interesting idea @gautamsi, but as a breaking change that would be a pretty significant hurdle for many developers wanting to migrate compared to this thus far. Maybe we can posit that as an idea in a different pull request and think about how to help developers migrate?

@gautamsi
Copy link
Member

gautamsi commented Mar 5, 2025

@dcousens I understand that migrating such data structure to json will be cumbersome. Adding _meta field should help with custom implementation

@dcousens
Copy link
Member

dcousens commented Mar 5, 2025

How are you thinking the meta field would work for now?

@gautamsi
Copy link
Member

gautamsi commented Mar 5, 2025

I can save additional information specific to provider or save more information like image metadata (orientation, geocoding etc.) and use that in my frontend/workflow.

@dcousens
Copy link
Member

dcousens commented Mar 6, 2025

OK, so you want a meta JSON sub-field on file and image that you could interact with arbitrarily in resolveInput hooks?

@marekryb
Copy link
Contributor

marekryb commented Mar 6, 2025

These are all great changes! Also the "node_modules black hole" gets smaller by removing the direct dependency on aws-sdk!

Since we're already making breaking changes, could we also address the update problem with these fields? This issue is related to #9419, but the problem is more general.

Currently, the order of operations is:

update → beforeOperation hook (delete old) → transformName → put (upload) → update DB

However, uploading is a fragile operation and can fail, leaving us with nothing.
A simple solution would be to change the order to:

update → transformName → put (upload) → update DB → afterOperation hook (delete old)

With this approach, we would need to enforce the limitation that transformName must produce a unique string. This is already the case unless the user provides a custom implementation.

If this limitation is not practical, the only alternative I see, would be to change the interface. For example, we could introduce a differentiating argument for transformName and/or storage.delete like version, operationType or some unique identifier.

@gautamsi
Copy link
Member

gautamsi commented Mar 6, 2025

OK, so you want a meta JSON sub-field on file and image that you could interact with arbitrarily in resolveInput hooks?

exactly.

@marekryb
Copy link
Contributor

marekryb commented Mar 6, 2025

Thank you @emmatown ❤️ Simple and elegant solution.

@emmatown emmatown force-pushed the image-files-refactor branch 3 times, most recently from 93be677 to cea3fd9 Compare March 12, 2025 06:18
@dcousens
Copy link
Member

dcousens commented Mar 13, 2025

@gautamsi I don't think I have an answer to your meta JSON sub-field issue as part of this pull request

@gautamsi
Copy link
Member

would you accept a pull request?

@emmatown emmatown force-pushed the image-files-refactor branch 2 times, most recently from efadb6a to 8926297 Compare March 18, 2025 03:34

Verified

This commit was signed with the committer’s verified signature.
thomasheartman Thomas Heartman
@emmatown emmatown force-pushed the image-files-refactor branch from 8926297 to 4850043 Compare March 18, 2025 04:28
@emmatown emmatown mentioned this pull request Mar 18, 2025

Verified

This commit was signed with the committer’s verified signature.
thomasheartman Thomas Heartman
emmatown and others added 2 commits March 19, 2025 15:52
@dcousens dcousens enabled auto-merge (squash) March 19, 2025 06:33
@dcousens dcousens merged commit 41ee880 into main Mar 19, 2025
48 checks passed
@dcousens dcousens deleted the image-files-refactor branch March 19, 2025 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

S3 file upload bug when same file name re-uploaded
4 participants