main
 1import { getCollection } from "astro:content";
 2import type { APIRoute } from "astro";
 3import { Feed } from "feed";
 4import { SITE_CONFIG } from "@/config";
 5import { rehypeCleanProperties } from "@/plugins/rehype-clean-properties";
 6import { rehypeExternalLinks } from "@/plugins/rehype-external-links";
 7import { rehypeExtractImage } from "@/plugins/rehype-extract-image";
 8import { renderTypstToHtml } from "@/utils/render-typst";
 9
10export const GET: APIRoute = async () => {
11  const posts = await getCollection("posts");
12  const siteUrl = SITE_CONFIG.website;
13
14  const feed = new Feed({
15    title: SITE_CONFIG.title,
16    description: SITE_CONFIG.description,
17    id: siteUrl,
18    link: siteUrl,
19    language: "en",
20    favicon: `${siteUrl}/favicon.ico`,
21    copyright: `All rights reserved ${new Date().getFullYear()}`,
22    feedLinks: {
23      rss: `${siteUrl}/feed.atom`,
24    },
25    author: {
26      name: SITE_CONFIG.author,
27    },
28  });
29
30  await Promise.all(
31    posts.map(async (post) => {
32      const url = `${siteUrl}/posts/${post.id}`;
33      if (typeof post.filePath !== "string") return;
34      const renderedContent = await renderTypstToHtml(post.filePath, [
35        rehypeExtractImage,
36        rehypeExternalLinks,
37        rehypeCleanProperties,
38      ]);
39
40      feed.addItem({
41        title: post.data.title,
42        id: url,
43        link: url,
44        description: post.data.desc,
45        content: renderedContent,
46        author: [{ name: post.data.author || SITE_CONFIG.author }],
47        date: post.data.date,
48      });
49    }),
50  );
51
52  return new Response(feed.atom1(), {
53    headers: {
54      "Content-Type": "application/xml",
55    },
56  });
57};