- POST /api/__journal__/cache/purge removes every Nitro route-cache entry - 404 without a 32-character token or behind a proxy, 401 on a wrong bearer token (constant-time over sha256 digests) - routeRules keep /api/__journal__/** out of the route cache - runtimeConfig.journal.cachePurgeToken (NUXT_JOURNAL_CACHE_PURGE_TOKEN) - fixture API reload(); testing/purge-check.mjs proves a cached page shows a change after a purge; the playground caches with swr 300
37 lines
1.7 KiB
TypeScript
37 lines
1.7 KiB
TypeScript
// POST /api/__journal__/cache/purge: removes every Nitro route-cache entry
|
|
// (pages, _payload.json and feeds), so a Journal save in the admin shows on
|
|
// the next page view. The backend calls it on loopback after the save
|
|
// commits, with `Authorization: Bearer <runtimeConfig.journal.cachePurgeToken>`
|
|
// and the body {"kind":"post"|"category","slug":"<slug>"}; the answer is
|
|
// {"purged":<n>}.
|
|
//
|
|
// 404 without a token of at least 32 characters, and for any request that
|
|
// came through a reverse proxy (X-Forwarded-For, X-Real-IP or Forwarded);
|
|
// 401 for a missing or wrong bearer token. The layer's routeRules keep this
|
|
// route out of the route cache.
|
|
|
|
import { authorizePurge, parsePurgeEvent, purgeRouteCache } from '../../../../app/utils/siteCache.ts'
|
|
|
|
const PROXY_HEADERS = ['x-forwarded-for', 'x-real-ip', 'forwarded']
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
setResponseHeader(event, 'cache-control', 'no-store')
|
|
const config = useRuntimeConfig(event)
|
|
const verdict = authorizePurge({
|
|
token: (config.journal as { cachePurgeToken?: unknown }).cachePurgeToken,
|
|
authorization: getRequestHeader(event, 'authorization'),
|
|
proxied: PROXY_HEADERS.some((name) => getRequestHeader(event, name) !== undefined),
|
|
})
|
|
if (verdict === 'disabled' || verdict === 'proxied') {
|
|
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
|
|
}
|
|
if (verdict === 'unauthorized') {
|
|
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
|
|
}
|
|
|
|
const { kind, slug } = parsePurgeEvent(await readBody(event).catch(() => null))
|
|
const purged = await purgeRouteCache(useStorage('cache'))
|
|
console.info(`[journal] cache purge: removed ${purged} route cache entries (${kind} ${slug})`)
|
|
return { purged }
|
|
})
|