You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Media replacement with revisions, decode validation and atomic publication
#3740
I'd like to know whether you'd accept a reworked version of this in core. If so, I'd contribute it as a series of small PRs. My use case is plugins that rewrite image bytes, such as optimizers, where a bad output has to be detected and reversible.
What happens today
PUT /_emdash/api/media/:id/replace (packages/core/src/astro/routes/api/media/[id]/replace.ts):
Overwrites in place. It writes the new bytes to the existing storage key, so the previous bytes are gone and the change can't be undone.
Updates the record separately. It updates size, dimensions and hash in a second step. A failure between the two leaves the stored bytes and the record out of sync, and nothing detects or resumes it.
Trusts client metadata. It takes width, height and the MIME type from the client.
Doesn't decode. The new bytes are never decoded, so beyond the size limit there's no check that they're a valid image within pixel, animation or format limits.
Proposed direction
An opt-in mode, off by default, in four pieces that can be reviewed separately:
Revisions and retained originals. Add a migration with revision records. The original bytes are kept in a private location the public file route won't serve.
Validating replacement bytes.
Check the header for format, animation, pixel count and dimensions.
Limit the total pixels being decoded at once.
Fully decode in a child process that's killed after a time limit.
If no decoder is available, replacement stays disabled.
Atomic publication.
Write each new version as a separate object.
Switch to it in one transaction, together with metadata derived from the bytes and a record of the replacement.
A retried replacement resumes rather than duplicating work.
One write path, revision-aware reads.
Every reader of media bytes resolves the stable key to the active revision.
The replace route goes through the new service.
The old direct-overwrite path is refused for media under this mode.
URLs and storage keys stay the same for consumers.
Prototype
I have this working as patches against current main (913cb1bb). It isn't public yet. Tests run on Node and SQLite with local storage through the real route handlers. They cover hostile and corrupted image inputs, and a simulated crash after each publication step. For upstream I'd rewrite it to project conventions rather than submit it as is.
Limits:
Only Node, SQLite and local storage are exercised. D1 and other storage adapters are refused, not supported.
There's no plugin-facing replace API, orphan cleanup or restore yet.
Cache freshness after a replacement isn't bounded yet, and the image transform pipeline hasn't been tested against a replaced image.
The decoder is Sharp, as an optional peer dependency.
Questions
Does this belong in core, or would you prefer hook points so it can live in a package?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
I'd like to know whether you'd accept a reworked version of this in core. If so, I'd contribute it as a series of small PRs. My use case is plugins that rewrite image bytes, such as optimizers, where a bad output has to be detected and reversible.
What happens today
PUT /_emdash/api/media/:id/replace(packages/core/src/astro/routes/api/media/[id]/replace.ts):width,heightand the MIME type from the client.Proposed direction
An opt-in mode, off by default, in four pieces that can be reviewed separately:
URLs and storage keys stay the same for consumers.
Prototype
I have this working as patches against current
main(913cb1bb). It isn't public yet. Tests run on Node and SQLite with local storage through the real route handlers. They cover hostile and corrupted image inputs, and a simulated crash after each publication step. For upstream I'd rewrite it to project conventions rather than submit it as is.Limits:
Questions
All reactions