import { loadRenderers } from "astro:container";
import { render } from "astro:content";
import { getContainerRenderer as getMDXRenderer } from "@astrojs/mdx";
import rss, { type RSSFeedItem } from "@astrojs/rss";
import { getSortedPosts } from "@utils/content-utils";
import { formatDateI18nWithTime } from "@utils/date-utils";
import { url } from "@utils/url-utils";
import type { APIContext } from "astro";
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import sanitizeHtml from "sanitize-html";
import { siteConfig } from "@/config";
import pkg from "../../package.json";
function stripInvalidXmlChars(str: string): string {
return str.replace(
// biome-ignore lint/suspicious/noControlCharactersInRegex: https://www.w3.org/TR/xml/#charsets
/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]/g,
"",
);
}
export async function GET(context: APIContext) {
const blog = await getSortedPosts();
const renderers = await loadRenderers([getMDXRenderer()]);
const container = await AstroContainer.create({ renderers });
const feedItems: RSSFeedItem[] = [];
for (const post of blog) {
const { Content } = await render(post);
const rawContent = await container.renderToString(Content);
const cleanedContent = stripInvalidXmlChars(rawContent);
feedItems.push({
title: post.data.title,
pubDate: post.data.published,
description: post.data.description || "",
link: url(`/posts/${post.id}/`),
content: sanitizeHtml(cleanedContent, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
}),
});
}
return rss({
title: siteConfig.title,
description: siteConfig.subtitle || "No description",
site: context.site ?? "https://fuwari.vercel.app",
customData: `Firefly
${pkg.version}
https://github.com/CuteLeaf/Firefly
${formatDateI18nWithTime(new Date())}`,
items: feedItems,
});
}