route.ts68 lines · main
1import { CHANGELOG_ENTRIES } from '../entries';
2
3export const dynamic = 'force-static';
4export const revalidate = 3600; // rebuild hourly; entries are added by deploy
5
6const SITE = 'https://docs.briven.tech';
7
8/**
9 * RSS 2.0 feed of the changelog. Linked from the <head> of every docs
10 * page so feed readers auto-discover. Static; revalidates hourly to
11 * pick up newly-merged entries without a redeploy.
12 */
13export function GET(): Response {
14 const items = CHANGELOG_ENTRIES.slice(0, 50)
15 .map((e) => {
16 const tags = e.tags.map((t) => `<category>${escape(t)}</category>`).join('');
17 const url = `${SITE}/changelog`;
18 // Use yyyy-mm-dd at 12:00 UTC for a stable pubDate — date-only entries
19 // don't carry a time-of-day; this keeps the feed monotonically ordered.
20 const pubDate = new Date(`${e.date}T12:00:00Z`).toUTCString();
21 return ` <item>
22 <title>${escape(e.title)}</title>
23 <link>${url}</link>
24 <guid isPermaLink="false">briven-${e.date}-${slug(e.title)}</guid>
25 <pubDate>${pubDate}</pubDate>
26 ${tags}
27 <description>${escape(e.body)}</description>
28 </item>`;
29 })
30 .join('\n');
31
32 const xml = `<?xml version="1.0" encoding="UTF-8"?>
33<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
34 <channel>
35 <title>briven changelog</title>
36 <link>${SITE}/changelog</link>
37 <atom:link href="${SITE}/changelog/feed.xml" rel="self" type="application/rss+xml" />
38 <description>what's new in briven — feature releases, fixes, security updates.</description>
39 <language>en</language>
40${items}
41 </channel>
42</rss>`;
43
44 return new Response(xml, {
45 status: 200,
46 headers: {
47 'content-type': 'application/rss+xml; charset=utf-8',
48 'cache-control': 'public, max-age=3600',
49 },
50 });
51}
52
53function escape(s: string): string {
54 return s
55 .replace(/&/g, '&amp;')
56 .replace(/</g, '&lt;')
57 .replace(/>/g, '&gt;')
58 .replace(/"/g, '&quot;')
59 .replace(/'/g, '&apos;');
60}
61
62function slug(s: string): string {
63 return s
64 .toLowerCase()
65 .replace(/[^a-z0-9]+/g, '-')
66 .replace(/^-+|-+$/g, '')
67 .slice(0, 60);
68}