// Sitemap source for Journal content (sitemap.sources in the layer config). // It pages GET /posts at per_page=30 until meta.last_page, fetches the // category tree with include_empty=1, and builds every loc from // journal.routes. The API base is server runtime config, never request input. import { createJournalClient, type JournalFetcher } from '../../../app/utils/client.ts' import { buildJournalSitemapEntries } from '../../../app/utils/sitemap.ts' import type { JournalPost, JournalRoutes } from '../../../app/utils/types.ts' /** Hard stop for a runaway last_page (30 000 posts). */ const MAX_PAGES = 1000 export default defineSitemapEventHandler(async (event) => { const config = useRuntimeConfig(event) const base = (config.journal as { apiBase: string }).apiBase const routes = (config.public.journal as { routes: JournalRoutes }).routes // Recorded by server/plugins/journal-client-address.ts before any route // cache hides the request headers. const forwarded = (event.context.journalForwardedFor as string | undefined) || [getRequestHeader(event, 'x-forwarded-for'), getRequestIP(event)] .filter((v): v is string => typeof v === 'string' && v !== '') .join(', ') const headers: Record = forwarded ? { 'x-forwarded-for': forwarded } : {} const fetcher: JournalFetcher = (path, query) => $fetch(path, { baseURL: base, query, headers }) const api = createJournalClient(fetcher) const posts: JournalPost[] = [] if (routes.post !== false) { for (let page = 1; page <= MAX_PAGES; page++) { const res = await api.listPosts({ page, perPage: 30 }) posts.push(...res.data) if (page >= res.meta.last_page) break } } const categories = routes.category !== false ? await api.listCategories() : [] return buildJournalSitemapEntries(posts, categories, routes) })