ui redesign, markdown fix + metadata and auth header
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
interface PostInfo {
|
||||
slug: string;
|
||||
date: string;
|
||||
summary?: string;
|
||||
excerpt?: string;
|
||||
tags: string[];
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
interface SiteConfig {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
function escapeXml(s: string): string {
|
||||
return s.replace(/[<>&'"]/g, c => ({
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'&': '&',
|
||||
"'": ''',
|
||||
'"': '"',
|
||||
}[c]!));
|
||||
}
|
||||
|
||||
function formatSlug(slug: string): string {
|
||||
return slug
|
||||
.split('-')
|
||||
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ site }) => {
|
||||
const API_URL = process.env.PUBLIC_API_URL || 'http://backend:3000';
|
||||
const origin = site?.toString().replace(/\/$/, '') || '';
|
||||
|
||||
let posts: PostInfo[] = [];
|
||||
let config: SiteConfig = { title: 'Narlblog', subtitle: 'A clean, modern blog' };
|
||||
try {
|
||||
const [pr, cr] = await Promise.all([
|
||||
fetch(`${API_URL}/api/posts`),
|
||||
fetch(`${API_URL}/api/config`),
|
||||
]);
|
||||
if (pr.ok) posts = await pr.json();
|
||||
if (cr.ok) config = await cr.json();
|
||||
} catch (e) {
|
||||
console.error('feed.xml backend fetch failed', e);
|
||||
}
|
||||
|
||||
const items = posts
|
||||
.filter(p => !p.draft)
|
||||
.map(p => {
|
||||
const url = `${origin}/posts/${p.slug}`;
|
||||
const description = p.summary || p.excerpt || '';
|
||||
const pubDate = new Date(p.date).toUTCString();
|
||||
const categories = p.tags.map(t => ` <category>${escapeXml(t)}</category>`).join('\n');
|
||||
return ` <item>
|
||||
<title>${escapeXml(formatSlug(p.slug))}</title>
|
||||
<link>${escapeXml(url)}</link>
|
||||
<guid isPermaLink="true">${escapeXml(url)}</guid>
|
||||
<pubDate>${pubDate}</pubDate>
|
||||
<description>${escapeXml(description)}</description>
|
||||
${categories}
|
||||
</item>`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>${escapeXml(config.title)}</title>
|
||||
<link>${escapeXml(origin)}</link>
|
||||
<description>${escapeXml(config.subtitle)}</description>
|
||||
<language>en</language>
|
||||
<atom:link href="${escapeXml(origin)}/feed.xml" rel="self" type="application/rss+xml" />
|
||||
${items}
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Cache-Control': 'public, max-age=600',
|
||||
},
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user