// vue-seo-layer: the frontend half of the SummerCMS SEO plugin // (golem15.seo). For every page it asks GET /_seo/api/v1/resolve?url=…, // enforces the redirect and 410 decisions, and sets the head (title, // description, canonical, robots, Open Graph, X/Twitter, verification, // hreflang, and one JSON-LD @graph) from the answer with priority // over the page's own tags. It also reports 404s to the plugin's log and // serves the site's root files (verification files and, with a backend that // answers /_seo/api/v1/site, robots.txt, the sitemaps, llms.txt and the // IndexNow key). An app consumes it with `extends: ['vue-seo-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. The layer's only module is its own local one // (lib/seo-module.ts); it pulls in no package. import { fileURLToPath } from 'node:url' const currentDir = fileURLToPath(new URL('.', import.meta.url)) export default defineNuxtConfig({ compatibilityDate: '2025-07-15', devtools: { enabled: false }, modules: [`${currentDir}lib/seo-module.ts`], // Tokens first, then the base styles that use them (only .seo-* classes). // The app's own css array loads after these, so its overrides win. css: [`${currentDir}app/assets/css/seo-tokens.css`, `${currentDir}app/assets/css/seo-base.css`], runtimeConfig: { seo: { // Server-only base for SSR calls (NUXT_SEO_API_BASE). It never comes // from request input. apiBase: 'http://127.0.0.1:8096/_seo/api/v1', // Server-only origin of the Go binary for the root files // (NUXT_SEO_BACKEND_BASE). Empty: apiBase without /_seo/api/v1, so // http://127.0.0.1:8096 by default. backendBase: '', // Server-only bearer token of POST /notfound (NUXT_SEO_NOTFOUND_TOKEN), // the backend's golem15.seo.notfound_token. Empty: 404s are not // reported. Not under NUXT_SITE_: nuxt-site-config copies every // NUXT_SITE_* variable into the site config, which is published in // every page's payload. notfoundToken: '', // Server-only bearer token of the route report // (NUXT_SEO_ROUTE_REPORT_TOKEN). Empty: notfoundToken is used. routeReportToken: '', // How long a resolve call may take before the page renders without it // (NUXT_SEO_TIMEOUT_MS). timeoutMs: 1500, // Seconds a resolve answer stays in the layer's cache // (NUXT_SEO_RESOLVE_CACHE_TTL); the site cache purge clears it. 0 // turns the cache off. resolveCacheTtl: 60, }, // The shared site cache purge route POST /api/__site__/cache/purge // (moved here from the journal, menu and FAQ layers, same contract). cachePurge: { // Server-only bearer token (NUXT_CACHE_PURGE_TOKEN), the backend's // site_cache.purge_token. Shorter than 32 characters, the route // answers 404. Not under NUXT_SITE_: nuxt-site-config copies every // NUXT_SITE_* variable into the site config, which is published in // every page's payload. token: '', // NUXT_CACHE_PURGE_ALLOW_PROXIED: set it to true when the backend // reaches the site through a TLS-terminating proxy (an https // site_cache.purge_url on another host), which adds X-Forwarded-For, // X-Real-IP or Forwarded. The route then accepts proxied requests and // the bearer token is the only gate; unset, it answers 404 to any // proxied request. allowProxied: false, }, // Deprecated (one release): the earlier names of cachePurge.token // (NUXT_SITE_CACHE_PURGE_TOKEN) and cachePurge.allowProxied // (NUXT_SITE_CACHE_ALLOW_PROXIED), read when the new ones are unset. The // layer's server/plugins/site-cache-purge.ts strips them from the site // config and logs a deprecation warning. siteCache: { purgeToken: '', allowProxied: false, }, public: { seo: { // Same-origin base for browser navigations (NUXT_PUBLIC_SEO_API_BASE). // The site's proxy routes GET /resolve to the backend; set it // to /api/__seo__ to resolve through the Nuxt server instead. apiBase: '/_seo/api/v1', // NUXT_PUBLIC_SEO_ENABLED=false turns the layer off: no resolve // calls, no head, no redirects, no root files, no 404 reports. The // module option seo.cms (NUXT_PUBLIC_SEO_CMS) is an alias. enabled: true, }, }, }, // The layer's routes and the shared site 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/__seo__/**': { cache: false }, '/api/__site__/**': { cache: false }, }, })