import rss, { type RSSFeedItem } from '@astrojs/rss' import type { APIRoute } from 'astro' import { getBlog, listBlogs, type ListBlogItemType, } from '../lib/apis/legacy/blog' import { ensureShikiEngine } from '../lib/markdown' // import { toMarkdocTree } from '../lib/markdoc' import Markdoc from '@markdoc/markdoc' import KeyV from 'keyv' import { createHash } from 'node:crypto' export const prerender = false const renderCache = new KeyV({ namespace: 'markdoc-rss', }) const _render = async (content: string) => { const key = 'html:' + createHash('sha256').update(content).digest('hex') const cached = await renderCache.get(key) if (cached) { return cached } const blogTree = Markdoc.transform(Markdoc.parse(content)) const html = Markdoc.renderers.html(blogTree) await renderCache.set(key, html, 1000 * 60 * 60 * 24) return html } const _getBlog = async (blogId: number) => { const key = `raw:blog-${blogId}` const cached = await renderCache.get(key) if (cached) { return cached } const content = (await getBlog(blogId))?.content if (content) { await renderCache.set(key, content, 1000 * 60 * 60 * 12) } return content } export const GET = (async (context) => { let blogs: ListBlogItemType[] = [] let pid = 0 let newBlogs = [] while (pid == 0 || newBlogs.length > 0) { newBlogs = await listBlogs({ page: ++pid, limit: 100, }) blogs = [...blogs, ...newBlogs] } const site = context.site || 'https://passthem.top' await ensureShikiEngine() return rss({ title: '小帕的小窝', description: '小帕和他朋友们的博客', site, items: await Promise.all( blogs.map(async (blog) => { const blogContent = (await _getBlog(blog.id)) || '博客内容暂不可用' const rssItem: RSSFeedItem = { title: blog.title, description: `一篇由 ${blog.author.nickname} 写的博客`, link: `${site}/blogs/${blog.id}`, pubDate: new Date(blog.created_at), content: await _render(blogContent), author: blog.author.nickname, enclosure: blog.featured_image ? { url: blog.featured_image.image_url, length: 0, type: 'image/jpeg', } : undefined, } return rssItem }), ), customData: `zh-hans`, }) }) satisfies APIRoute