import { NextResponse } from "next/server" import fs from "fs" import path from "path" interface ChangelogEntry { version: string date: string content: string url: string title: string } function formatContentForRSS(content: string): string { return ( content // Convert ### headers to

tags .replace(/^### (.+)$/gm, "

$1

") // Convert ** bold ** to tags .replace(/\*\*(.*?)\*\*/g, "$1") // Convert code blocks to
 tags
      .replace(/```[\s\S]*?```/g, (match) => {
        const code = match.replace(/```/g, "").trim()
        return `
${code}
` }) // Convert - bullet points to
  • tags .replace(/^- (.+)$/gm, "
  • $1
  • ") // Wrap consecutive
  • tags in
      .replace(/(
    • .*?<\/li>\s*)+/g, (match) => `
        ${match}
      `) // Convert double newlines to

      for paragraphs .replace(/\n\n/g, "

      ") // Clean up extra spaces .replace(/\s+/g, " ") .trim() ) } async function parseChangelog(): Promise { try { const changelogPath = path.join(process.cwd(), "..", "CHANGELOG.md") if (!fs.existsSync(changelogPath)) { return [] } const fileContents = fs.readFileSync(changelogPath, "utf8") const entries: ChangelogEntry[] = [] // Split by ## headers (both versions and dates) const lines = fileContents.split("\n") let currentEntry: Partial | null = null let contentLines: string[] = [] for (const line of lines) { // Check for version header: ## [1.1.1] - 2025-03-21 const versionMatch = line.match(/^##\s+\[([^\]]+)\]\s*-\s*(\d{4}-\d{2}-\d{2})/) // Check for date-only header: ## 2025-05-13 const dateMatch = line.match(/^##\s+(\d{4}-\d{2}-\d{2})$/) if (versionMatch || dateMatch) { // Save previous entry if exists if (currentEntry && contentLines.length > 0) { const rawContent = contentLines.join("\n").trim() currentEntry.content = formatContentForRSS(rawContent) if (currentEntry.version && currentEntry.date && currentEntry.title) { entries.push(currentEntry as ChangelogEntry) } } // Start new entry if (versionMatch) { const version = versionMatch[1] const date = versionMatch[2] currentEntry = { version, date, url: `https://proxmenux.com/changelog#${version}`, title: `ProxMenux ${version}`, } } else if (dateMatch) { const date = dateMatch[1] currentEntry = { version: date, date, url: `https://proxmenux.com/changelog#${date}`, title: `ProxMenux Update ${date}`, } } contentLines = [] } else if (currentEntry && line.trim()) { // Add content lines (skip empty lines at the beginning) if (contentLines.length > 0 || line.trim() !== "") { contentLines.push(line) } } } // Don't forget the last entry if (currentEntry && contentLines.length > 0) { const rawContent = contentLines.join("\n").trim() currentEntry.content = formatContentForRSS(rawContent) if (currentEntry.version && currentEntry.date && currentEntry.title) { entries.push(currentEntry as ChangelogEntry) } } return entries.slice(0, 20) // Latest 20 entries } catch (error) { console.error("Error parsing changelog:", error) return [] } } export async function GET() { const entries = await parseChangelog() const siteUrl = "https://proxmenux.com" const rssXml = ` ProxMenux Changelog Latest updates and changes in ProxMenux - An Interactive Menu for Proxmox VE Management ${siteUrl}/changelog en-US ${new Date().toUTCString()} ProxMenux RSS Generator 60 ${entries .map( (entry) => ` ${entry.title} ${entry.url} ${entry.url} ${new Date(entry.date).toUTCString()} Changelog `, ) .join("")} ` return new NextResponse(rssXml, { headers: { "Content-Type": "application/rss+xml; charset=utf-8", "Cache-Control": "public, max-age=3600, s-maxage=3600", }, }) }