feat(api): POST /api/cache/clear + flush images on asset webhooks
Two changes: - New POST /api/cache/clear (auth: REVALIDATE_TOKEN). Wipes the TTL content cache, the image disk + memory cache, and forwards to the RustyCMS /api/transform/cache/invalidate endpoint when an API key is configured. Single trigger to flush everything. - /api/revalidate now also calls clearImageCache() on asset.* events so a re-upload (focal change, alt edit, replacement) doesn't keep serving the previous transformed image from disk. clearImageCache() in image-cache.server.ts wipes the in-memory map and removes every file under IMAGE_CACHE_DIR.
This commit is contained in:
@@ -218,3 +218,27 @@ export function resolveExtension(params: ImageTransformParams, contentType?: str
|
||||
if (contentType) return extForContentType(contentType);
|
||||
return 'jpg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipe in-memory + on-disk image cache. Returns counts cleared.
|
||||
*/
|
||||
export async function clearImageCache(): Promise<{ memory: number; disk: number }> {
|
||||
const memory = memoryCache.size;
|
||||
memoryCache.clear();
|
||||
let disk = 0;
|
||||
try {
|
||||
const dir = ensureCacheDir();
|
||||
const files = await readdir(dir);
|
||||
for (const f of files) {
|
||||
try {
|
||||
await unlink(join(dir, f));
|
||||
disk++;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return { memory, disk };
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { env as envPublic } from '$env/dynamic/public';
|
||||
import { invalidateAll } from '$lib/cms';
|
||||
import { clearImageCache } from '$lib/image-cache.server';
|
||||
|
||||
/**
|
||||
* Manual full-cache purge:
|
||||
* - SvelteKit-side TTL cache (cms.ts)
|
||||
* - SvelteKit image cache (memory + disk)
|
||||
* - Optionally: forwards POST to RustyCMS /api/transform/cache/invalidate
|
||||
* when an API key is configured (RUSTYCMS_API_KEY env).
|
||||
*
|
||||
* Auth: same `REVALIDATE_TOKEN` as /api/revalidate.
|
||||
*/
|
||||
export const POST: RequestHandler = async ({ request, url }) => {
|
||||
const token = env.REVALIDATE_TOKEN;
|
||||
if (!token) throw error(500, 'REVALIDATE_TOKEN not configured');
|
||||
const provided =
|
||||
request.headers.get('x-revalidate-token') ??
|
||||
request.headers.get('x-webhook-secret') ??
|
||||
url.searchParams.get('token');
|
||||
if (provided !== token) throw error(401, 'invalid token');
|
||||
|
||||
invalidateAll();
|
||||
const img = await clearImageCache();
|
||||
|
||||
let cms: unknown = 'skipped';
|
||||
const apiKey = env.RUSTYCMS_API_KEY;
|
||||
const cmsUrl = (envPublic.PUBLIC_CMS_URL || '').replace(/\/$/, '');
|
||||
if (apiKey && cmsUrl) {
|
||||
try {
|
||||
const res = await fetch(`${cmsUrl}/api/transform/cache/invalidate`, {
|
||||
method: 'POST',
|
||||
headers: { authorization: `Bearer ${apiKey}` },
|
||||
});
|
||||
cms = res.ok ? await res.json() : { error: res.status };
|
||||
} catch (e) {
|
||||
cms = { error: e instanceof Error ? e.message : String(e) };
|
||||
}
|
||||
}
|
||||
|
||||
return json({ ok: true, content_cache: 'cleared', image_cache: img, cms });
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { invalidateAll, invalidateCollection } from '$lib/cms';
|
||||
import { clearImageCache } from '$lib/image-cache.server';
|
||||
|
||||
/**
|
||||
* RustyCMS Webhook Receiver.
|
||||
@@ -50,7 +51,10 @@ export const POST: RequestHandler = async ({ request, url }) => {
|
||||
if (event.startsWith('asset.')) {
|
||||
// Assets referenziert überall → safe bet: purge all collections mit resolve
|
||||
invalidateAll();
|
||||
return json({ ok: true, purged: 'all', reason: event });
|
||||
// Same image bytes (focal change, re-upload, alt edit) → image-cache must
|
||||
// also flush so /cms-images stops serving the previous transform.
|
||||
const img = await clearImageCache();
|
||||
return json({ ok: true, purged: 'all', reason: event, image_cache: img });
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
|
||||
Reference in New Issue
Block a user