import type { APIRoute } from "astro"; import { getEmDashCollection, getSiteSettings } from "emdash"; import { resolveBlogSiteIdentity } from "../utils/site-identity"; export const GET: APIRoute = async ({ site, url }) => { const siteUrl = site?.toString() || url.origin; const { siteTitle, siteTagline } = resolveBlogSiteIdentity(await getSiteSettings()); const { entries: posts } = await getEmDashCollection("posts", { orderBy: { published_at: "desc" }, limit: 20, }); const items = posts .map((post) => { if (!post.data.publishedAt) return null; const pubDate = post.data.publishedAt.toUTCString(); const postUrl = `${siteUrl}/posts/${post.id}`; const title = escapeXml(post.data.title || "Untitled"); const description = escapeXml(post.data.excerpt || ""); return ` ${title} ${postUrl} ${postUrl} ${pubDate} ${description} `; }) .filter(Boolean) .join("\n"); const rss = ` ${escapeXml(siteTitle)} ${escapeXml(siteTagline)} ${siteUrl} en-us ${new Date().toUTCString()} ${items} `; return new Response(rss, { headers: { "Content-Type": "application/rss+xml; charset=utf-8", "Cache-Control": "public, max-age=3600", }, }); }; const XML_ESCAPE_PATTERNS = [ [/&/g, "&"], [//g, ">"], [/"/g, """], [/'/g, "'"], ] as const; function escapeXml(str: string): string { let result = str; for (const [pattern, replacement] of XML_ESCAPE_PATTERNS) { result = result.replace(pattern, replacement); } return result; }