Files
vue-journal-layer/nuxt.config.ts
Jakub Zych a03e076c62 feat: token-protected site cache purge route
- 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
2026-10-10 18:19:58 +02:00

67 lines
2.6 KiB
TypeScript

// vue-journal-layer: reusable Journal views as a Nuxt layer. An app consumes
// it with `extends: ['vue-journal-layer']`.
//
// Every path below is absolute and built from this file's directory: a
// relative or ~ path in a layer config resolves against the consuming
// project, not the layer.
import { fileURLToPath } from 'node:url'
const currentDir = fileURLToPath(new URL('.', import.meta.url))
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: false },
// The consuming app may have no app/pages directory; the Journal pages are
// registered by the journal module, so the router must be on regardless.
pages: true,
// Declared once here, so the app never re-lists them (two copies of
// @nuxtjs/i18n split locale state).
modules: ['@nuxt/fonts', '@nuxtjs/i18n', '@nuxtjs/seo', `${currentDir}lib/journal-module.ts`],
// Tokens first, then the neutral styles that use them. The app's own css
// array loads after these, so its token overrides win.
css: [`${currentDir}app/assets/css/journal-tokens.css`, `${currentDir}app/assets/css/journal-base.css`],
runtimeConfig: {
journal: {
// Server-only base for SSR fetches (NUXT_JOURNAL_API_BASE). It never
// comes from request input.
apiBase: 'http://127.0.0.1:8096/_journal/api/v1',
// Server-only bearer token of the site cache purge route
// (NUXT_JOURNAL_CACHE_PURGE_TOKEN). Shorter than 32 characters, the
// route answers 404.
cachePurgeToken: '',
},
public: {
journal: {
// Same-origin base for browser fetches (NUXT_PUBLIC_JOURNAL_API_BASE).
apiBase: '/_journal/api/v1',
},
},
},
// The Journal's internal routes are never route-cached. Nitro wraps every
// handler matching an app rule such as '/**': { swr } in its cached
// handler, which keys by path, does not skip POST and hides request
// headers, so the purge route would answer from the cache without running.
routeRules: {
'/api/__journal__/**': { cache: false },
},
// No OG image rendering at runtime.
ogImage: { enabled: false },
// Paginated listings keep ?page=N in their canonical URL. nuxt-seo-utils
// already whitelists page by default; set explicitly to lock the behaviour.
seo: { canonicalQueryWhitelist: ['page'] },
// Journal posts and categories come from the API through the layer's
// sitemap source; locs are built from journal.routes. One flat sitemap.
sitemap: { sources: ['/api/__sitemap__/journal'], autoI18n: false },
// i18n: the layer declares no locales. The journal module registers the
// layer's pl/en strings only for locales the app enables.
})