fix: keep the site cache purge token out of the page payload (NUXT_CACHE_PURGE_TOKEN)
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.
This commit is contained in:
30
server/api/__site__/cache/purge.post.ts
vendored
30
server/api/__site__/cache/purge.post.ts
vendored
@@ -1,34 +1,38 @@
|
||||
// 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.siteCache.purgeToken>` and the body
|
||||
// {"kind":"<kind>","slug":"<slug>"}; the answer is {"purged":<n>}. The kind
|
||||
// and slug only reach the log line.
|
||||
// `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.siteCache.allowProxied (NUXT_SITE_CACHE_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.
|
||||
// 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 } from '../../../lib/siteCachePurge.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 config = useRuntimeConfig(event)
|
||||
const siteCache = config.siteCache as { purgeToken?: unknown; allowProxied?: unknown } | undefined
|
||||
const { token, allowProxied } = resolveSitePurgeConfig(useRuntimeConfig(event))
|
||||
const verdict = authorizeSitePurge({
|
||||
token: siteCache?.purgeToken,
|
||||
token,
|
||||
authorization: getRequestHeader(event, 'authorization'),
|
||||
proxied: PROXY_HEADERS.some((name) => getRequestHeader(event, name) !== undefined),
|
||||
allowProxied: siteCache?.allowProxied === true || siteCache?.allowProxied === 'true',
|
||||
allowProxied,
|
||||
})
|
||||
if (verdict === 'disabled' || verdict === 'proxied') {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
|
||||
|
||||
Reference in New Issue
Block a user