- the module writes #seo-routes (static paths and route patterns) from the resolved pages; options seo.routeReport, seo.pagination and seo.htmlSitemap reach the public runtime config - seo-route-report: POST /registry/routes once per process with the route-report token, one retry after 60 s on failure, nothing without a token, with the layer off or with routeReport false - seo-drift: one "[vue-seo-layer] drift:" warning per mismatch between the CMS's site answer (URL patterns, pagination, HTML sitemap paths) and the build - GET /api/__seo__/site and useSeoSite() - smoke grammar routereport and log, fixture registry route, drift run
52 lines
2.7 KiB
TypeScript
52 lines
2.7 KiB
TypeScript
// Reports the site's static routes to the CMS's page registry once per
|
|
// server process (D-33): POST <apiBase>/registry/routes with
|
|
// `Authorization: Bearer <route-report token>` and {routes: [path, ...]}.
|
|
// The paths come from the build (#seo-routes): pages without parameters,
|
|
// without the ones whose meta keeps them out of the index (requiresAuth, a
|
|
// noindex seo.robots) and without seo.noindexRoutes. It never delays the
|
|
// start: the report goes out after the plugin returns, a failure (no
|
|
// answer, a 5xx) is tried once more after 60 s, and a 404 (a backend without
|
|
// the registry) or a refused token is logged once and not retried. Nothing
|
|
// is sent with the layer off (seo.cms, public.seo.enabled), with
|
|
// seo.routeReport false, without a token (NUXT_SEO_ROUTE_REPORT_TOKEN, else
|
|
// NUXT_SEO_NOTFOUND_TOKEN) or while prerendering. The token stays in the
|
|
// server-only runtime config and goes to the configured apiBase only.
|
|
|
|
import { staticRoutes } from '#seo-routes'
|
|
import { seoServerConfig } from '../lib/config.ts'
|
|
import { ROUTE_REPORT_RETRY_MS, routeReportBody, routeReportOutcome } from '../lib/routeReport.ts'
|
|
|
|
export default defineNitroPlugin(() => {
|
|
if (import.meta.prerender) return
|
|
const cfg = seoServerConfig(useRuntimeConfig())
|
|
if (!cfg.enabled || !cfg.routeReport || !cfg.routeReportToken) return
|
|
const body = routeReportBody(staticRoutes)
|
|
|
|
const send = async (attempt: number): Promise<void> => {
|
|
let status: number | null = null
|
|
let reason = ''
|
|
try {
|
|
const res = await $fetch.raw('/registry/routes', {
|
|
baseURL: cfg.apiBase,
|
|
method: 'POST',
|
|
body,
|
|
headers: { authorization: `Bearer ${cfg.routeReportToken}` },
|
|
timeout: Math.max(cfg.timeoutMs, 5000),
|
|
ignoreResponseError: true,
|
|
})
|
|
status = res.status
|
|
} catch (err) {
|
|
reason = (err as Error)?.message ?? String(err)
|
|
}
|
|
const outcome = routeReportOutcome(status)
|
|
if (outcome === 'sent') console.info(`[vue-seo-layer] route report: ${body.routes.length} routes sent`)
|
|
else if (outcome === 'absent') console.info('[vue-seo-layer] route report: the CMS has no page registry (404), nothing reported')
|
|
else if (outcome === 'refused') console.warn(`[vue-seo-layer] route report: the CMS refused the token (${status}); set NUXT_SEO_ROUTE_REPORT_TOKEN to golem15.seo.route_report_token`)
|
|
else if (attempt === 1) {
|
|
console.warn(`[vue-seo-layer] route report failed (${status ?? reason}); trying again in ${ROUTE_REPORT_RETRY_MS / 1000} s`)
|
|
setTimeout(() => void send(2), ROUTE_REPORT_RETRY_MS).unref?.()
|
|
} else console.warn(`[vue-seo-layer] route report failed again (${status ?? reason}); not retried before the next start`)
|
|
}
|
|
setTimeout(() => void send(1), 0).unref?.()
|
|
})
|