implement content-hash based image cache invalidation

Add content-based hashing to recipe images for proper cache invalidation
while maintaining graceful degradation through dual file storage.

Changes:
- Add imageHash utility with SHA-256 content hashing (8-char)
- Update Recipe model to store hashed filenames in images[0].mediapath
- Modify image upload endpoint to save both hashed and unhashed versions
- Update frontend components to use images[0].mediapath with fallback
- Add migration endpoint to hash existing images (production-only)
- Update image delete/rename endpoints to handle both file versions

Images are now stored as:
  - recipe.a1b2c3d4.webp (hashed, cached forever)
  - recipe.webp (unhashed, graceful degradation fallback)

Database stores hashed filename for cache busting, while unhashed
version remains on disk for backward compatibility and manual uploads.
This commit is contained in:
2026-01-02 12:06:53 +01:00
parent 6bf3518db7
commit ccf3fd7ea2
12 changed files with 603 additions and 38 deletions

View File

@@ -11,7 +11,7 @@
export let data: PageData;
let preamble = data.recipe.preamble
let addendum = data.recipe.addendum
let image_preview_url="https://bocken.org/static/rezepte/thumb/" + data.recipe.short_name + ".webp?v=" + data.recipe.dateModified;
let image_preview_url="https://bocken.org/static/rezepte/thumb/" + (data.recipe.images?.[0]?.mediapath || `${data.recipe.short_name}.webp`);
let note = data.recipe.note
// Translation workflow state