refactor(cloudinary)!: derive publicId from filename, align with storage-s3#149
Draft
jhb-dev wants to merge 1 commit into
Draft
refactor(cloudinary)!: derive publicId from filename, align with storage-s3#149jhb-dev wants to merge 1 commit into
jhb-dev wants to merge 1 commit into
Conversation
jhb-dev
commented
May 18, 2026
| cloudinaryPublicId: `${cloudinaryPublicId}.webp`, | ||
| cloudName, | ||
| mimeType: 'video/', // Keep as video for correct resource type | ||
| mimeType: 'video/', |
Contributor
Author
There was a problem hiding this comment.
restore the comment
| ...incomingConfig, | ||
| collections: (incomingConfig.collections || []).map((collection) => { | ||
| if (!collectionsWithAdapter[collection.slug]) { | ||
| const collOptions = options.collections[collection.slug] |
Contributor
Author
There was a problem hiding this comment.
rename to collectionOptions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Alternative to #148 that removes the workaround entirely by aligning this plugin with the stateless model used by
@payloadcms/storage-s3. The plugin no longer depends on the cloudinary response coming back throughadapter.handleUpload, so the upstream 3.82+ regression that skips that call for client uploads becomes inert.Why this works
generatePublicId(prefix, filename)is the value the plugin tells Cloudinary to use (both server-side viacloudinary.uploader.upload_streamand client-side via the signed upload). Cloudinary honors an explicitpublic_idverbatim — no suffixing — so the resultingcloudinaryPublicIdis fully determined by:…every piece of which is either adapter config (closure) or already on the document (
data.filename). The cloudinary response is a mirror of what we passed in, not a source of truth — exactly like how the S3 adapter knows the bucket key without asking S3 (@payloadcms/storage-s3/dist/adapter.js).What changed
cloudinaryPublicIdfield gets abeforeChangehook that derives the value fromdata.filename+ closure-capturedcollectionPrefix+folderSrc. Falls back tooriginalDoc.filenameon updates without a new file. This runs whether or nothandleUploadwas called, so client uploads on Payload 3.82+ persist correctly.generateURLderivescloudinaryPublicIdinline instead of readingdata.cloudinaryPublicId. It now takescloudName,collectionPrefix, andfolderSrcinstead of the full options object.staticHandlerreconstructs the Cloudinary CDN URL fromdoc.cloudinaryPublicId+mimeType(looked up viapayload.findwhen not indoc). It no longer readsdoc.url, which removes a footgun: withdisablePayloadAccessControl: false,doc.urlwas/api/{collection}/file/{filename}and the old handler would recursively fetch itself.getAdminThumbnailderivescloudinaryPublicIdfromdoc.filename+ collection prefix instead of readingdoc.cloudinaryPublicId— same logic asgenerateURL, same factory signature.handleUploadstill performs the actual upload but no longer mutatesdata— the field hook owns persistence.useFilename: falseremoved (breaking). In that mode Cloudinary mints a randompublic_id, and the only place it exists is the upload response — non-derivable. Dropping it is the price of a stateless plugin.adapter.clientUploadsis now forwarded fromoptions.clientUploadsinstead of being hardcoded, fixing a pre-existing oversight.What this means for the upstream bug
The upstream filter
!file.clientUploadContextin@payloadcms/plugin-cloud-storage/hooks/afterChange.js:35still skipshandleUploadfor client uploads. That's still arguably an API contract violation (HandleUpload's type declaresclientUploadContextas a parameter). But for this plugin it's now a non-issue — there's nothing left for the adapter to do that depends on that callback firing. Filing the upstream bug remains worthwhile for other adapters in the wild that still need it.Relationship to #148
#148 is a fast, low-risk fix that restores persistence by adding a collection-level
beforeChangehook readingreq.file.clientUploadContext. This PR is the cleaner alternative — pick one. If we ship this, #148 should be closed without merging.Test plan
pnpm test— 5 new tests covering:videos/)clientUploadContextinvolvement (proves the new path doesn't depend on it)originalDoc.filenameon update without a new filepnpm typecheckcleanpnpm lintcleanpnpm buildproduces a cleandist/with no*.test.*filescloudinary/dev: upload an image (usesdisablePayloadAccessControl: true) and a video (nodisablePayloadAccessControl) via the admin's client upload path, confirm both render in the admin and the docs carry the expectedcloudinaryPublicId.cloudinaryPublicIdvalues stored (the persistence used to come from Cloudinary's response, which equals what the new derivation produces foruseFilename: true). No data migration needed except for users who were onuseFilename: false— they'll need to rename objects in Cloudinary.Breaking changes
useFilenameoption removed. Documented in CHANGELOG with migration guidance.adapter.handleUploadno longer writescloudinaryPublicIdorurlonto the document. Direct consumers of the adapter (rare) should be aware.