// Sitemap — hand-rolled static endpoint (no @astrojs/sitemap dependency; our URL // set is fully known at build time). /admin is intentionally excluded. import { getCollection } from 'astro:content'; import { PAGE_FIRST, PAGE_REST } from '../lib/news.js'; // Chunk sizes are imported from lib/news.js — the same values the // /news/[...page] route slices with, so the page count here cannot drift. const newsPageCount = (n) => (n <= PAGE_FIRST ? 1 : 1 + Math.ceil((n - PAGE_FIRST) / PAGE_REST)); export async function GET(context) { const site = context.site; // always set in astro.config.mjs const news = (await getCollection('news', ({ data }) => !data.draft)) .sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()); const pages = await getCollection('pages'); // Static pages come from the actual route files, so a new src/pages/*.astro // can never be forgotten here. 404 is excluded; /admin (subdirectory) is // intentionally unlisted. Go-live note: /login and /dashboard are mock // pages — filter them out before robots.txt stops disallowing everything. const staticPages = Object.keys(import.meta.glob('./*.astro')) .map((p) => p.slice(1, -'.astro'.length)) .filter((p) => p !== '/404') .map((p) => (p === '/index' ? '/' : p)) .sort(); const urls = [ ...staticPages, '/news', ...Array.from({ length: newsPageCount(news.length) - 1 }, (_, i) => `/news/${i + 2}`), ...pages.map((p) => `/pages/${p.id}`), ].map((path) => ` ${new URL(path, site).href}`); const articles = news.map( (p) => ` ${new URL(`/news/${p.id}`, site).href}${p.data.date.toISOString().slice(0, 10)}`, ); const xml = ` ${[...urls, ...articles].join('\n')} `; return new Response(xml, { headers: { 'Content-Type': 'application/xml; charset=utf-8' } }); }