feat: use checksummed filenames for recipe images and clean up old files
All checks were successful
CI / update (push) Successful in 1m13s
All checks were successful
CI / update (push) Successful in 1m13s
Updated recipe image handling to use checksummed filenames for proper cache busting. When uploading a new image during recipe edit, old image files (both hashed and unhashed versions) are now properly deleted from all directories (full, thumb, placeholder). Changes: - CardAdd.svelte: Use checksummed filename from upload response - Edit page server: Add deleteRecipeImage() helper to remove old images - Edit page server: Delete old images when new image is uploaded
This commit is contained in:
@@ -113,7 +113,7 @@ export async function show_local_image(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
uploaded_image_filename = result.unhashedFilename;
|
uploaded_image_filename = result.filename;
|
||||||
upload_error = "";
|
upload_error = "";
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Recipe } from '$models/Recipe';
|
|||||||
import { dbConnect } from '$utils/db';
|
import { dbConnect } from '$utils/db';
|
||||||
import { invalidateRecipeCaches } from '$lib/server/cache';
|
import { invalidateRecipeCaches } from '$lib/server/cache';
|
||||||
import { IMAGE_DIR } from '$env/static/private';
|
import { IMAGE_DIR } from '$env/static/private';
|
||||||
import { rename, access } from 'fs/promises';
|
import { rename, access, unlink } from 'fs/promises';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { constants } from 'fs';
|
import { constants } from 'fs';
|
||||||
import {
|
import {
|
||||||
@@ -14,6 +14,43 @@ import {
|
|||||||
detectChangedFields
|
detectChangedFields
|
||||||
} from '$utils/recipeFormHelpers';
|
} from '$utils/recipeFormHelpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete recipe image files from all directories (full, thumb, placeholder)
|
||||||
|
* Handles both hashed and unhashed versions for backward compatibility
|
||||||
|
*/
|
||||||
|
async function deleteRecipeImage(filename: string): Promise<void> {
|
||||||
|
if (!filename) return;
|
||||||
|
|
||||||
|
const imageDirectories = ['full', 'thumb', 'placeholder'];
|
||||||
|
|
||||||
|
// Extract basename to handle both hashed and unhashed versions
|
||||||
|
const basename = filename
|
||||||
|
.replace(/\.[a-f0-9]{8}\.webp$/, '') // Remove hash if present
|
||||||
|
.replace(/\.webp$/, ''); // Remove extension
|
||||||
|
|
||||||
|
const unhashedFilename = basename + '.webp';
|
||||||
|
|
||||||
|
for (const dir of imageDirectories) {
|
||||||
|
// Delete hashed version (if it's the hashed filename that was passed in)
|
||||||
|
if (filename !== unhashedFilename) {
|
||||||
|
try {
|
||||||
|
await unlink(join(IMAGE_DIR, 'rezepte', dir, filename));
|
||||||
|
console.log(`Deleted hashed image: ${dir}/${filename}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Could not delete hashed image ${dir}/${filename}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete unhashed version
|
||||||
|
try {
|
||||||
|
await unlink(join(IMAGE_DIR, 'rezepte', dir, unhashedFilename));
|
||||||
|
console.log(`Deleted unhashed image: ${dir}/${unhashedFilename}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Could not delete unhashed image ${dir}/${unhashedFilename}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ fetch, params, locals}) => {
|
export const load: PageServerLoad = async ({ fetch, params, locals}) => {
|
||||||
// Edit is German-only - redirect to German version
|
// Edit is German-only - redirect to German version
|
||||||
if (params.recipeLang === 'recipes') {
|
if (params.recipeLang === 'recipes') {
|
||||||
@@ -79,7 +116,11 @@ export const actions = {
|
|||||||
const existingImagePath = formData.get('existing_image_path')?.toString();
|
const existingImagePath = formData.get('existing_image_path')?.toString();
|
||||||
|
|
||||||
if (uploadedImage) {
|
if (uploadedImage) {
|
||||||
// New image uploaded - use it
|
// New image uploaded - delete old image files and use new one
|
||||||
|
if (existingImagePath && existingImagePath !== uploadedImage) {
|
||||||
|
await deleteRecipeImage(existingImagePath);
|
||||||
|
}
|
||||||
|
|
||||||
recipeData.images = [{
|
recipeData.images = [{
|
||||||
mediapath: uploadedImage,
|
mediapath: uploadedImage,
|
||||||
alt: existingImagePath ? (recipeData.images?.[0]?.alt || '') : '',
|
alt: existingImagePath ? (recipeData.images?.[0]?.alt || '') : '',
|
||||||
|
|||||||
Reference in New Issue
Block a user