import rss from '@astrojs/rss'; import type { APIRoute } from 'astro'; import { escapeUTF8 } from 'entities'; import { getPostUrl, getSortedPosts } from '../runtime/lib/content'; import { siteLocale, siteTitle } from '../runtime/lib/site'; const RSS_EXT_NS = 'https://komorebi.keqing.moe/rss-ext'; function formatDateForFeed(date: Date) { return date.toLocaleDateString(siteLocale); } function formatReadingTime(milliseconds: number) { const totalSeconds = Math.ceil(milliseconds / 1000); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; const parts: string[] = []; if (hours > 0) parts.push(`${hours} 小时`); if (minutes > 0) parts.push(`${minutes} 分`); if (seconds > 0 || totalSeconds === 0) parts.push(`${seconds} 秒`); return parts.join(' '); } export const GET: APIRoute = async (context) => { let site = context.site; if (!site) { site = new URL(context.url.origin); } const blog = await getSortedPosts(); return rss({ title: siteTitle, description: `${siteTitle} RSS`, site, xmlns: { k: RSS_EXT_NS, }, items: blog.map((post) => { const pubDateLabel = formatDateForFeed(post.data.pubDate); const updatedAt = post.data.updatedDate; const words = Math.round(post.readingStats.words); const readingTimeLabel = formatReadingTime(post.readingStats.time); const customDataParts = [ `${escapeUTF8(pubDateLabel)}`, ]; if (updatedAt) { customDataParts.push( `${escapeUTF8(formatDateForFeed(updatedAt))}`, ); } customDataParts.push( `${words}`, `${escapeUTF8(readingTimeLabel)}`, ); return { title: post.data.title, pubDate: post.data.pubDate, description: post.data.description, link: getPostUrl(post.id), customData: customDataParts.join(''), }; }), stylesheet: '/rss/styles.xsl', }); };