blog.hangerthem.com/app/feed.xml/route.ts
HangerThem 4301e50ab3 fix: tag/category routes, recently updated list, feed and no-js flash
- Slugify tag/category static params so pre-rendered pages match links;
  disable dynamic params and return a real 404 via notFound()
- Drop bogus generateStaticParams from the tags/categories index pages
- Add a styled not-found page
- Sort 'Recently updated' by last modification before slicing
- Atom feed: correct rel=self URL, use updated dates, valid content type
- Swap no-js -> js class in an inline head script before first paint
- Markdown links: only route internal paths through next/link and
  tolerate missing hrefs
2026-10-19 14:15:57 +02:00

68 lines
2 KiB
TypeScript

import { metadata } from '@/app/metadata'
import { getAllPostsMeta } from '@/lib/posts'
import type { PostMeta } from '@/types/Post.type'
export const dynamic = 'force-static'
const SITE_URL = process.env.SITE_URL
const lastModified = (p: PostMeta) => new Date(p.updated ?? p.date)
function escapeXml(str: string) {
return str
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;')
}
export async function GET(req: Request) {
const posts = getAllPostsMeta()
const updated =
posts.length > 0
? new Date(Math.max(...posts.map((p) => lastModified(p).getTime()))).toISOString()
: new Date().toISOString()
const entries = posts
.map((p) => {
const url = `${SITE_URL}/posts/${p.slug}`
const date = new Date(p.date).toISOString()
const modified = lastModified(p).toISOString()
return `
<entry>
<title>${escapeXml(p.title)}</title>
<link href="${url}" rel="alternate" type="text/html" />
<id>${url}</id>
<published>${date}</published>
<updated>${modified}</updated>
<content type="text/html" src="${url}" />
<summary>${escapeXml(p.description)}</summary>
</entry>`
})
.join('')
const feed = `<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>${SITE_URL}/</id>
<title>${escapeXml(metadata.title!.toString())}</title>
<updated>${updated}</updated>
<author>
<name>${escapeXml(metadata.creator!.toString())}</name>
</author>
<link href="${SITE_URL}/feed.xml" rel="self" type="application/atom+xml" />
<link href="${SITE_URL}" rel="alternate" type="text/html" />
<rights> © ${new Date().getFullYear()} Frank Borisjuk </rights>
${entries}
</feed>`
const accept = req.headers.get('accept') || ''
const contentType = accept.includes('application/atom+xml')
? 'application/atom+xml; charset=utf-8'
: 'application/xml; charset=utf-8'
return new Response(feed, {
headers: { 'Content-Type': contentType },
})
}