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

@@ -9,7 +9,7 @@ const RecipeSchema = new mongoose.Schema(
dateCreated: {type: Date, default: Date.now},
dateModified: {type: Date, default: Date.now},
images: [ {
mediapath: {type: String, required: true},
mediapath: {type: String, required: true}, // filename with hash for cache busting: e.g., "maccaroni.a1b2c3d4.webp"
alt: String,
caption: String,
}],