34 lines
1.6 KiB
TypeScript
34 lines
1.6 KiB
TypeScript
// The resolve call and the shared answer of the current page. On the server
|
|
// it calls the internal base (runtimeConfig.seo.apiBase) and forwards the
|
|
// visitor's x-forwarded-for plus the Nuxt peer, so the backend's per-IP
|
|
// throttle (seo-resolve, 600 a minute) keeps one bucket per visitor, also on
|
|
// route-cached renders. In the browser it calls the same-origin public base.
|
|
// Any failure (timeout, 4xx, 5xx, an unexpected body) answers null: the page
|
|
// then renders with its own head.
|
|
|
|
import { seoAnswer } from '../utils/seo.ts'
|
|
import type { SeoResolveAnswer } from '../utils/types.ts'
|
|
|
|
/** The resolve answer of the page being rendered, null when there is none. */
|
|
export function useSeoAnswer() {
|
|
return useState<SeoResolveAnswer | null>('seo-layer:answer', () => null)
|
|
}
|
|
|
|
export async function resolveSeo(url: string): Promise<SeoResolveAnswer | null> {
|
|
const config = useRuntimeConfig()
|
|
const timeout = Number((config.seo as { timeoutMs?: number } | undefined)?.timeoutMs) || 1500
|
|
try {
|
|
if (import.meta.server) {
|
|
const base = (config.seo as { apiBase: string }).apiBase
|
|
const event = useRequestEvent()
|
|
const recorded = event?.context.seoForwardedFor as string | undefined
|
|
const headers: Record<string, string> = recorded ? { 'x-forwarded-for': recorded } : {}
|
|
return seoAnswer(await $fetch('/resolve', { baseURL: base, query: { url }, headers, timeout }))
|
|
}
|
|
const base = (config.public.seo as { apiBase: string }).apiBase
|
|
return seoAnswer(await $fetch('/resolve', { baseURL: base, query: { url }, timeout }))
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|