diff --git a/app/layout.tsx b/app/layout.tsx index 49a2624..27d5d7f 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -25,6 +25,11 @@ export const metadata: Metadata = { metadataBase: new URL('https://blog.chorus.services'), alternates: { canonical: 'https://blog.chorus.services', + types: { + 'application/rss+xml': [ + { url: '/rss.xml', title: 'CHORUS PING! RSS Feed' } + ] + } }, openGraph: { type: 'website', diff --git a/app/rss.xml/route.ts b/app/rss.xml/route.ts new file mode 100644 index 0000000..3eb6867 --- /dev/null +++ b/app/rss.xml/route.ts @@ -0,0 +1,101 @@ +import { getSortedPostsData } from '@/lib/blog' +import { NextResponse } from 'next/server' + +// RSS feed configuration +const SITE_URL = 'https://blog.chorus.services' +const SITE_TITLE = 'CHORUS Services Blog' +const SITE_DESCRIPTION = 'Contextual AI orchestration, on-premises infrastructure, and the future of intelligent systems.' +const SITE_LANGUAGE = 'en-us' +const AUTHOR_EMAIL = 'noreply@chorus.services' +const AUTHOR_NAME = 'CHORUS Services' + +function escapeXml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +function formatRssDate(dateString: string): string { + const date = new Date(dateString) + return date.toUTCString() +} + +export async function GET() { + try { + // Get all published posts (includes scheduled posts that should be live) + const posts = getSortedPostsData() + + // Generate RSS XML + const rssXml = ` + + + ${escapeXml(SITE_TITLE)} + ${SITE_URL} + ${escapeXml(SITE_DESCRIPTION)} + ${SITE_LANGUAGE} + ${AUTHOR_EMAIL} (${AUTHOR_NAME}) + ${AUTHOR_EMAIL} (${AUTHOR_NAME}) + ${formatRssDate(new Date().toISOString())} + + CHORUS Blog RSS Generator + 60 +${posts.map(post => ` + ${escapeXml(post.title)} + ${SITE_URL}/posts/${post.slug} + ${escapeXml(post.description)} + ${AUTHOR_EMAIL} (${post.author.name}) + ${post.tags.map(tag => escapeXml(tag)).join(', ')} + ${SITE_URL}/posts/${post.slug} + ${formatRssDate(post.date)} + Author: ${escapeXml(post.author.name)}${post.author.role ? `, ${escapeXml(post.author.role)}` : ''}

+

Reading Time: ${post.readingTime} min

+

Tags: ${post.tags.map(tag => escapeXml(tag)).join(', ')}

+
+
${post.content}
+
+

Read full post on CHORUS Blog

+ ]]>
+
`).join('\n')} +
+
` + + return new NextResponse(rssXml, { + status: 200, + headers: { + 'Content-Type': 'application/rss+xml; charset=utf-8', + 'Cache-Control': 'public, max-age=3600, s-maxage=3600', // Cache for 1 hour + }, + }) + } catch (error) { + console.error('Error generating RSS feed:', error) + + // Return a minimal RSS feed with error info + const errorRss = ` + + + ${escapeXml(SITE_TITLE)} - Error + ${SITE_URL} + Error generating RSS feed + ${SITE_LANGUAGE} + ${formatRssDate(new Date().toISOString())} + + RSS Feed Error + ${SITE_URL} + There was an error generating the RSS feed. Please try again later. + ${formatRssDate(new Date().toISOString())} + + +` + + return new NextResponse(errorRss, { + status: 500, + headers: { + 'Content-Type': 'application/rss+xml; charset=utf-8', + }, + }) + } +} \ No newline at end of file