ui redesign, markdown fix + metadata and auth header

This commit is contained in:
2026-05-09 05:09:07 +02:00
parent 7f8a66f360
commit bc6a34cf1f
42 changed files with 3093 additions and 517 deletions
+87
View File
@@ -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 => ({
'<': '&lt;',
'>': '&gt;',
'&': '&amp;',
"'": '&apos;',
'"': '&quot;',
}[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',
},
});
};