fix(plugin-cloud-storage): prevent draft file reupload from unpublishing the document - #17030
Merged
Merged
Conversation
…ing the document When an upload collection has drafts enabled, replacing the file and clicking "Save draft" on a published document used to flip the main document's _status from published to draft (effectively unpublishing it) and delete the published file from storage. This only happened with cloud storage adapters and only when the file itself was reuploaded. The cloud storage afterChange hook persists adapter-returned upload metadata via a nested payload.update. Adapters like S3 return the full data object, so the nested update wrote the draft document (including _status: draft) onto the published main row because it did not preserve the draft state. The same hook also deleted the previous file, which is still referenced by the published document. Pass the originating draft state to the nested update so metadata is persisted to the draft version instead of the published main document, and skip deleting the previous file when saving a draft over a published document.
Contributor
📦 esbuild Bundle Analysis for payloadThis analysis was generated by esbuild-bundle-analyzer. 🤖
Largest pathsThese visualization shows top 20 largest paths in the bundle.Meta file: packages/next/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js
Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js
DetailsNext to the size is how much the size has increased or decreased compared with the base branch of this PR.
|
GermanJablo
enabled auto-merge (squash)
June 17, 2026 15:49
DanRibbens
approved these changes
Jun 17, 2026
GermanJablo
added a commit
that referenced
this pull request
Jun 17, 2026
…ing the document (3.x) (#17034) ## What Backport of #17030 to the `3.x` branch. Fixes #17016. On an upload collection with drafts enabled, replacing the file and clicking **Save draft** on a published document used to: - flip the main document's `_status` from `published` to `draft` (effectively unpublishing it), and - delete the published file from storage. This only reproduced with **cloud storage adapters** (S3, Azure, GCS, R2, Vercel Blob, Uploadthing) and only when the file itself was reuploaded. ## Root cause The cloud storage `afterChange` hook persists adapter-returned upload metadata through a nested `payload.update`. Adapters such as S3 return the full `data` object from `handleUpload`, so `uploadMetadata` was the entire draft document (including `_status: 'draft'`). Because the nested update did not preserve the draft state, the draft document was written onto the **published main row**, unpublishing it. The same hook then deleted the previous file, which is still referenced by the published document. ## Fix In `packages/plugin-cloud-storage/src/hooks/afterChange.ts`: - Pass the originating draft state (`draft: isDraftSave`) to the nested `payload.update` so metadata is persisted to the draft version instead of the published main document. - Skip deleting the previous file when saving a draft over a published document (`isDraftOverPublished`), since the published file is still in use. Non-draft updates and collections without drafts are unaffected (`draft: false` matches the prior behavior). The hook in 3.x is structurally identical to `main`, so the change is the same as #17030. ## Tests Adds a `draft-with-upload-cloud-storage` collection to the `versions` suite backed by a mock cloud storage adapter that mirrors the real S3 adapter (its `handleUpload` returns the full `data`). New integration tests cover: - Saving a draft with a new file does not unpublish the main document nor delete its file. - Publishing the draft afterwards correctly promotes the draft file. - A normal (non-draft) update still persists adapter metadata to the main document. ## Notes - `payload-types.ts` was hand-updated for the new collection (the 3.x `versions` types have no lexical-node union, unlike `main`). It is type-only and erased at runtime. - This was prepared in an isolated worktree without the full monorepo install, so the integration suite was not run locally; CI runs `test:int versions`. Static checks done: identical hook diff to #17030, Prettier clean, and verified the 3.x `cloudStoragePlugin`/`GeneratedAdapter` APIs match the test wiring. ## Test plan - `pnpm run test:int versions` Co-authored-by: German Jablonski <GermanJablo@users.noreply.github.com>
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.
What
Fixes #17016.
On an upload collection with drafts enabled, replacing the file and clicking Save draft on a published document used to:
_statusfrompublishedtodraft(effectively unpublishing it), andThis only reproduced with cloud storage adapters (S3, Azure, GCS, R2, Vercel Blob, Uploadthing) and only when the file itself was reuploaded, which is why the earlier fix (#16853, tested with local storage) did not resolve it.
Root cause
The cloud storage
afterChangehook persists adapter-returned upload metadata through a nestedpayload.update. Adapters such as S3 return the fulldataobject fromhandleUpload, souploadMetadatawas the entire draft document (including_status: 'draft'). Because the nested update did not preserve the draft state, the draft document was written onto the published main row, unpublishing it.The same hook then deleted the previous file, which is still referenced by the published document.
Fix
In
packages/plugin-cloud-storage/src/hooks/afterChange.ts:draft: isDraftSave) to the nestedpayload.updateso metadata is persisted to the draft version instead of the published main document.isDraftOverPublished), mirroring the existing coreisDraftOverPublishedguard, since the published file is still in use.Non-draft updates and collections without drafts are unaffected (
draft: falseis equivalent to the prior behavior).Tests
Added a
draft-with-upload-cloud-storagecollection to theversionssuite backed by a mock cloud storage adapter that mirrors the real S3 adapter (itshandleUploadreturns the fulldata). New integration tests cover:Test plan
pnpm run test:int versions(105 passed)