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:
@@ -35,10 +35,14 @@
|
||||
|
||||
const isEnglish = $derived(data.lang === 'en');
|
||||
|
||||
// Use German short_name for images (they're the same for both languages)
|
||||
const imageShortName = $derived(data.germanShortName || data.short_name);
|
||||
const hero_img_src = $derived("https://bocken.org/static/rezepte/full/" + imageShortName + ".webp?v=" + data.dateModified);
|
||||
const placeholder_src = $derived("https://bocken.org/static/rezepte/placeholder/" + imageShortName + ".webp?v=" + data.dateModified);
|
||||
// Use mediapath from images array (includes hash for cache busting)
|
||||
// Fallback to short_name.webp for backward compatibility
|
||||
const img_filename = $derived(
|
||||
data.images?.[0]?.mediapath ||
|
||||
`${data.germanShortName || data.short_name}.webp`
|
||||
);
|
||||
const hero_img_src = $derived("https://bocken.org/static/rezepte/full/" + img_filename);
|
||||
const placeholder_src = $derived("https://bocken.org/static/rezepte/placeholder/" + img_filename);
|
||||
|
||||
const months = $derived(isEnglish
|
||||
? ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||
@@ -296,8 +300,8 @@ h4{
|
||||
<svelte:head>
|
||||
<title>{stripHtmlTags(data.name)} - {labels.title}</title>
|
||||
<meta name="description" content="{stripHtmlTags(data.description)}" />
|
||||
<meta property="og:image" content="https://bocken.org/static/rezepte/thumb/{imageShortName}.webp" />
|
||||
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/{imageShortName}.webp" />
|
||||
<meta property="og:image" content="https://bocken.org/static/rezepte/thumb/{img_filename}" />
|
||||
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/{img_filename}" />
|
||||
<meta property="og:image:type" content="image/webp" />
|
||||
<meta property="og:image:alt" content="{stripHtmlTags(data.name)}" />
|
||||
{@html `<script type="application/ld+json">${JSON.stringify(data.recipeJsonLd)}</script>`}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user