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};