- POST /api/__site__/cache/purge, its library and the site config plugin moved from vue-journal-layer with the same contract (token rules, 404 for proxied requests unless allowProxied, deprecated siteCache names); the route also clears the resolve cache - server/plugins/seo-cache-purge.ts clears the SEO caches after any 200 purge, so an app whose journal, menu or FAQ layer copy of the route wins keeps fresh SEO answers until those copies are removed - cachePurge and siteCache runtime keys; /api/__site__/** never cached - testing/purge-check.mjs (CMS answer change, plus a redirect added for a route-cached page applying at once) and purge-token-check.mjs
56 lines
2.7 KiB
TypeScript
56 lines
2.7 KiB
TypeScript
// POST /api/__site__/cache/purge: removes every Nitro route-cache entry
|
|
// (pages, _payload.json and feeds), so an admin save shows on the next page
|
|
// view. The backend calls it on loopback after the save commits, with
|
|
// `Authorization: Bearer <runtimeConfig.cachePurge.token>`
|
|
// (NUXT_CACHE_PURGE_TOKEN) and the body {"kind":"<kind>","slug":"<slug>"};
|
|
// the answer is {"purged":<n>}. The kind and slug only reach the log line.
|
|
//
|
|
// 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)
|
|
// unless runtimeConfig.cachePurge.allowProxied
|
|
// (NUXT_CACHE_PURGE_ALLOW_PROXIED) is true or "true": a backend that reaches
|
|
// the site through a TLS-terminating proxy opts in, and the bearer token is
|
|
// then the only gate. 401 for a missing or wrong bearer token. The
|
|
// '/api/__site__/**' route rule keeps this route out of the route cache.
|
|
//
|
|
// Deprecated for one release: siteCache.purgeToken
|
|
// (NUXT_SITE_CACHE_PURGE_TOKEN) and siteCache.allowProxied
|
|
// (NUXT_SITE_CACHE_ALLOW_PROXIED), read when the new names are unset (see
|
|
// resolveSitePurgeConfig).
|
|
//
|
|
// The purge also empties the SEO layer's caches (the resolve answers and
|
|
// the site answer), so a purged page renders with a fresh CMS answer.
|
|
//
|
|
// Moved here from vue-journal-layer, vue-menu-layer and vue-faq-layer, which
|
|
// ship an older copy of this file until they extend this layer; Nitro keeps
|
|
// one handler per route and method, so server/plugins/seo-cache-purge.ts
|
|
// also clears the SEO caches when one of those copies answers.
|
|
|
|
import { clearSeoCaches } from '../../../lib/resolve.ts'
|
|
import { authorizeSitePurge, parseSitePurgeEvent, purgeSiteRouteCache, resolveSitePurgeConfig } from '../../../lib/siteCachePurge.ts'
|
|
|
|
const PROXY_HEADERS = ['x-forwarded-for', 'x-real-ip', 'forwarded']
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
setResponseHeader(event, 'cache-control', 'no-store')
|
|
const { token, allowProxied } = resolveSitePurgeConfig(useRuntimeConfig(event))
|
|
const verdict = authorizeSitePurge({
|
|
token,
|
|
authorization: getRequestHeader(event, 'authorization'),
|
|
proxied: PROXY_HEADERS.some((name) => getRequestHeader(event, name) !== undefined),
|
|
allowProxied,
|
|
})
|
|
if (verdict === 'disabled' || verdict === 'proxied') {
|
|
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
|
|
}
|
|
if (verdict === 'unauthorized') {
|
|
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
|
|
}
|
|
|
|
const { kind, slug } = parseSitePurgeEvent(await readBody(event).catch(() => null))
|
|
const purged = await purgeSiteRouteCache(useStorage('cache'))
|
|
clearSeoCaches()
|
|
console.info(`[site-cache] purge: removed ${purged} route cache entries (${kind} ${slug})`)
|
|
return { purged }
|
|
})
|