Api cms delete and migrate media storage - #1909
Closed
johan-bell wants to merge 8 commits into
Closed
Conversation
- Refactored MediaEditor component to remove legacy media upload functionality. - Added useMediaEncoder composable to manage encoding sessions with Luminary Media Convert. - Implemented health checks and session management for the encoder. - Created utility functions for handling encoder session keys and events. - Updated API to fetch encoder configuration for media buckets. - Enhanced media DTO to include HLS URL and key for encoded media. - Added tests for media encoder utilities and session management.
processMediaDto no longer processes uploads, and MediaDto has no fileCollections or uploadData, so two validateChangeRequest tests were asserting against a shape the DTO rejects. Replaced with the current contract: a collection validates with an hlsUrl (plus optional hlsKey), and a key with no URL fails on hlsUrl. Also drop a now-unused MediaType import that failed lint. fix(app): skip player setup when there is no <video> element videojs() throws on an element it does not recognise, and inside the mounted hook that surfaces as an unhandled rejection rather than something the caller can catch. The ref is empty whenever the element was not rendered, which is why mounting the player for content whose video comes from parentMedia.hlsUrl failed the test run while every assertion still passed.
Deleting a post left its whole HLS collection in the bucket permanently: hundreds of objects, the playlists, the chunk chains, the sprites and the sidecars. Images have always been cleaned up by deleteImage three lines away; media was the only asset type that leaked. Opt-in, carried on the document. A delete *is* a change request — the whole document arrives with deleteReq set — so `media.deleteFiles` travels with the thing it applies to and cannot be separated from it in flight. Write-only and never persisted, the same shape as `media.hlsKey`. The location comes from the *saved* document and the intent from the incoming one, so an hlsUrl edited in the same breath as the delete cannot redirect the deletion somewhere else. Resolving the prefix is where the safety lives, and the guard falls out of the arithmetic rather than being bolted on: the only way to get an object key from a public URL is to strip the bucket's own public base, so a URL that does not start with that base cannot be resolved at all. That is the "never delete a prefix we did not create" rule, and unlike a marker object it also protects every collection already in a bucket. On top of it: the key must end in /master.m3u8, must not be the bucket root, must contain no traversal, and its last folder must be a session id — because hlsUrl is an editable field, and without that last check someone could paste a URL naming a shared folder and turn "delete everything under it" into a tick box. A refusal is a warning, not an error, and so is a failure part-way: the caller is deleting a document, and refusing to do that because a bucket was unreachable would be worse than leaving objects behind. Every key is logged before removal, so a deletion that turns out to be wrong can be reconstructed rather than guessed at. Adds S3Service.listObjectsUnder: the existing listObjects returns the whole bucket as a stream, which cannot answer "what belongs to this collection". The resolver's tests need no database — deliberately, since the guards are the part that must never regress and CouchDB is not always up.
A checkbox in the delete confirmation, shown only when the document actually has a collection, with the URL underneath so it is clear what goes. A checkbox rather than a toggle: a toggle reads as a setting applied the moment it moves, and this is a choice being confirmed. Unticked by default, and reset every time the dialog opens — an irreversible option that remembers a previous "yes" is one somebody eventually triggers without meaning to. Matches the existing "Duplicate image" checkbox in the duplicate dialog two blocks below. The answer rides on the document as `media.deleteFiles`, which the API reads and never stores, because a delete already sends the whole document with `deleteReq` set. Nothing to clean up afterwards either way.
The Vite SSG work (#1686) rewrote SingleContent.vue and reworked ContentTile.vue around it. Both conflicts were in the import blocks alone — the functional hunks merged on their own — so main's versions stand and the videoSource changes are re-applied on top: hasVideoSource/videoSourceFor pick the encoder's parentMedia.hlsUrl over a typed-in `video` URL, in the tile's media icons and progress bar and in the article page's VideoPlayer guard. Main's one-line readingTime import is kept.
Changing `mediaBucketId` used to move nothing: `processMedia` only stored the key, so the document ended up naming one bucket while `hlsUrl` still pointed at another. That is worse than untidy — `resolveCollectionPrefix` derives the storage prefix by stripping the bucket's own public base off the URL, so once the two disagree the collection can no longer be resolved, and deleting the document with "delete the files" ticked refuses and leaves the objects behind for good. Images have migrated between buckets since they were added; media never did. The order is the design: copy the whole collection, check each object's size against the source, only then rewrite `hlsUrl`, and only then delete the originals. A collection is not a set of independent files — a master playlist without its segments is a broken video — so a per-file "upload then delete" like `migrateImagesBetweenBuckets` is not enough here, where a partial result costs a video rather than one thumbnail. Any failure leaves the source whole and untouched and reverts `mediaBucketId`, so the document keeps pointing at files that exist. Copies stream. `uploadFile` takes a Buffer, which is right for a thumbnail and wrong for a byte-range chunk chain capped at 500 MB by default, so `putStream` was added alongside it and takes the size from the source's own `statObject`. Refusals are inherited rather than restated: the same session-id proof that guards deletion decides whether a collection may be moved. Changing the URL and the bucket in one save is read as repointing the document by hand and moves nothing, which keeps a deliberate edit from being overwritten.
Collaborator
Author
|
Closing as already landed. Its media-migration work is on Superseded by #1910. |
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.
No description provided.