nuxt-site-config copies every NUXT_SITE_* variable into the public site config, so NUXT_SITE_CACHE_PURGE_TOKEN reached every page's payload as cachePurgeToken (and NUXT_SITE_CACHE_ALLOW_PROXIED as cacheAllowProxied). - The purge settings move to runtimeConfig cachePurge.token (NUXT_CACHE_PURGE_TOKEN) and cachePurge.allowProxied (NUXT_CACHE_PURGE_ALLOW_PROXIED), outside that prefix. - The old siteCache.* names keep working for one release: a non-empty new token wins, either allowProxied flag turns it on, and one deprecation warning per process names the variables (never a value). - The shared Nitro plugin server/plugins/site-cache-purge.ts strips cachePurgeToken and cacheAllowProxied from nuxt-site-config's stack on site-config:init, so a site on the old names leaks nothing either; it does nothing without nuxt-site-config. - The shared lib, route and plugin and tests/sitePurge.test.ts are byte-identical in vue-journal-layer, vue-menu-layer and vue-faq-layer, with three sha256 pins. - README: new names, the deprecation, the stripping and the advice to rotate a token ever set under the old name.
49 lines
2.3 KiB
TypeScript
49 lines
2.3 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).
|
|
//
|
|
// Shared: several layers ship this file byte for byte, and Nitro keeps one
|
|
// handler per route and method.
|
|
|
|
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'))
|
|
console.info(`[site-cache] purge: removed ${purged} route cache entries (${kind} ${slug})`)
|
|
return { purged }
|
|
})
|