Files
vue-menu-layer/app/composables/useMenuApi.ts
Jakub Zych d980f4cf2f feat: MenuNav layer for the SummerCMS menu API
- typed client and useMenu/useMenuApi (SSR, menu:<code>:<locale> key,
  server and public bases, x-forwarded-for forwarded also on cached renders)
- MenuNav and recursive MenuNavItem: WAI disclosure navigation, URL
  allowlist with a text fallback, rel on _blank, active link and trail,
  mobile toggle, Esc / focus-out / outside pointer / route change closing
- menu tokens and base CSS, README
- node tests (link rules, client, server-rendered NavItem) and a playground
  SSR smoke against a fixture API
2026-10-12 18:19:53 +02:00

29 lines
1.4 KiB
TypeScript

// The menu API client. On the server it calls the internal base
// (runtimeConfig.menu.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, 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 { createMenuClient, type MenuClient, type MenuFetcher } from '../utils/client.ts'
export function useMenuApi(): MenuClient {
const config = useRuntimeConfig()
if (import.meta.server) {
const base = (config.menu as { apiBase: string }).apiBase
const event = useRequestEvent()
const recorded = event?.context.menuForwardedFor 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: MenuFetcher = (path, query) => $fetch(path, { baseURL: base, query, headers })
return createMenuClient(fetcher)
}
const base = (config.public.menu as { apiBase: string }).apiBase
const fetcher: MenuFetcher = (path, query) => $fetch(path, { baseURL: base, query })
return createMenuClient(fetcher)
}