mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 14:37:26 +00:00
The dynamic /blog/rss.xml returned 500 in CI (no Supabase env) and the E2E still asserted the old static feed's hand-written items. - /blog, /blog/[slug], and /blog/rss.xml now degrade gracefully (empty feed/ list, HTTP 200) when Supabase is unavailable, instead of throwing. - E2E: assert the always-present channel <title>LogicSRC Blog</title> and a looser xml content-type, dropping the removed static post titles. Verified with `next dev` and no Supabase env (CI conditions): rss/blog/sitemap all return 200. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
import { publicClient } from "@/lib/supabase";
|
|
|
|
export const runtime = "nodejs";
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type PostRow = {
|
|
slug: string;
|
|
title: string;
|
|
excerpt: string | null;
|
|
published_at: string;
|
|
};
|
|
|
|
function baseUrl(): string {
|
|
return (process.env.PUBLIC_URL ?? "https://logicsrc.com").replace(/\/$/, "");
|
|
}
|
|
|
|
function escapeXml(value: string): string {
|
|
return value
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
// GET /blog/rss.xml — RSS 2.0 feed generated from published blog_posts.
|
|
// Degrades to an empty (but valid) feed when Supabase is unavailable.
|
|
export async function GET(): Promise<Response> {
|
|
const base = baseUrl();
|
|
let posts: PostRow[] = [];
|
|
try {
|
|
const supabase = publicClient();
|
|
const { data } = await supabase
|
|
.from("blog_posts")
|
|
.select("slug, title, excerpt, published_at")
|
|
.eq("status", "published")
|
|
.order("published_at", { ascending: false })
|
|
.limit(50);
|
|
posts = (data ?? []) as PostRow[];
|
|
} catch {
|
|
posts = [];
|
|
}
|
|
|
|
const items = posts
|
|
.map((post) => {
|
|
const link = `${base}/blog/${post.slug}`;
|
|
return ` <item>
|
|
<title>${escapeXml(post.title)}</title>
|
|
<link>${escapeXml(link)}</link>
|
|
<guid>${escapeXml(link)}</guid>
|
|
<description>${escapeXml(post.excerpt ?? "")}</description>
|
|
<pubDate>${new Date(post.published_at).toUTCString()}</pubDate>
|
|
</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>LogicSRC Blog</title>
|
|
<link>${base}/blog</link>
|
|
<description>Project notes for LogicSRC OpenSpec standards, AgentSwarm, AgentByte, SDKs, MCP, and reference implementations.</description>
|
|
<language>en-us</language>
|
|
<atom:link href="${base}/blog/rss.xml" rel="self" type="application/rss+xml" />
|
|
${items}
|
|
</channel>
|
|
</rss>
|
|
`;
|
|
|
|
return new Response(xml, {
|
|
headers: {
|
|
"content-type": "application/rss+xml; charset=utf-8",
|
|
"cache-control": "public, max-age=300, s-maxage=300",
|
|
},
|
|
});
|
|
}
|