Files
vue-journal-layer/server/api/__sitemap__/journal.ts
Jakub Zych 783426c5d8 feat: Journal author and tag pages, SEO, sitemap source and RSS route
- author pages resolve slug or login and canonicalise to the slug
- tag pages, per-page SEO meta and canonicals that keep ?page=N
- sitemap source pages the API and builds locs from journal.routes
- /rss.xml proxies the API feed with x-forwarded-for
- neutral README; version 0.1.0
2026-10-09 18:06:20 +02:00

37 lines
1.6 KiB
TypeScript

// 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
const forwarded = [getRequestHeader(event, 'x-forwarded-for'), getRequestIP(event)]
.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 })
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)
})