Nitro's cached handler renders from a header-less copy of the request, so under swr route rules useJournalApi, /rss.xml and the sitemap source forwarded only the Nuxt peer and every cache miss shared one Journal API rate-limit bucket (deferred in Phase 16). A Nitro plugin records x-forwarded-for plus the peer in event.context.journalForwardedFor on the request hook; the three callers forward it. Same key as the host app's workaround, which keeps working. The acme SSR smoke's x-forwarded-for check on the now route-cached playground is the test.
31 lines
1.7 KiB
TypeScript
31 lines
1.7 KiB
TypeScript
// The Journal API client for pages and components. On the server it calls
|
|
// the internal base (runtimeConfig.journal.apiBase) and forwards the visitor's
|
|
// x-forwarded-for, plus the address of the peer that reached Nuxt, so the Go
|
|
// rate limiter keeps one bucket per visitor instead of one for the Nuxt
|
|
// server, also on route-cached renders. In the browser it calls the same-origin public base. The base is
|
|
// always runtime config, never request input.
|
|
|
|
import { createJournalClient, type JournalClient, type JournalFetcher } from '../utils/client.ts'
|
|
|
|
export function useJournalApi(): JournalClient {
|
|
const config = useRuntimeConfig()
|
|
|
|
if (import.meta.server) {
|
|
const base = (config.journal as { apiBase: string }).apiBase
|
|
// Under route caching the request copy has no headers; the address the
|
|
// layer's server/plugins/journal-client-address.ts recorded comes first.
|
|
const event = useRequestEvent()
|
|
const recorded = event?.context.journalForwardedFor as string | undefined
|
|
const incoming = useRequestHeaders(['x-forwarded-for'])['x-forwarded-for']
|
|
const peer = event?.node?.req?.socket?.remoteAddress
|
|
const forwarded = recorded || [incoming, peer].filter((v): v is string => typeof v === 'string' && v !== '').join(', ')
|
|
const headers: Record<string, string> = forwarded ? { 'x-forwarded-for': forwarded } : {}
|
|
const fetcher: JournalFetcher = (path, query) => $fetch(path, { baseURL: base, query, headers })
|
|
return createJournalClient(fetcher)
|
|
}
|
|
|
|
const base = (config.public.journal as { apiBase: string }).apiBase
|
|
const fetcher: JournalFetcher = (path, query) => $fetch(path, { baseURL: base, query })
|
|
return createJournalClient(fetcher)
|
|
}
|