diff --git a/web/app/rss.xml/route.ts b/web/app/rss.xml/route.ts
index eb8f174..5476940 100644
--- a/web/app/rss.xml/route.ts
+++ b/web/app/rss.xml/route.ts
@@ -10,39 +10,42 @@ interface ChangelogEntry {
title: string
}
+function escapeXml(text: string): string {
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+}
+
function formatContentForRSS(content: string): string {
return (
content
.replace(/https:\/\/macrimi\.github\.io\/ProxMenux/g, "https://proxmenux.com")
.replace(/`([^`]+)`/g, "$1")
- .replace(/!\[([^\]]*)\]$$([^)]+)$$/g, (match, alt, url) => {
- // Convert relative URLs to absolute
+ .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, url) => {
let absoluteUrl = url
if (url.startsWith("/")) {
absoluteUrl = `https://proxmenux.com${url}`
} else if (!url.startsWith("http://") && !url.startsWith("https://")) {
- // Relative path, make it absolute
absoluteUrl = `https://proxmenux.com/${url}`
}
- return ``
+ return `
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}
`)
- .replace(/\n/g, "
")
- // Clean up extra spaces
+ .replace(/^---$/gm, '
')
+ .replace(/\n/g, "
")
.replace(/\s+/g, " ")
.trim()
)
@@ -59,20 +62,15 @@ async function parseChangelog(): Promise {
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)
@@ -81,7 +79,6 @@ async function parseChangelog(): Promise {
}
}
- // Start new entry
if (versionMatch) {
const version = versionMatch[1]
const date = versionMatch[2]
@@ -103,14 +100,12 @@ async function parseChangelog(): Promise {
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)
@@ -119,7 +114,7 @@ async function parseChangelog(): Promise {
}
}
- return entries.slice(0, 20) // Latest 20 entries
+ return entries.slice(0, 20)
} catch (error) {
console.error("Error parsing changelog:", error)
return []
@@ -146,8 +141,9 @@ export async function GET() {
.map(
(entry) => `
-
-
${entry.title}
-
+ ${escapeXml(entry.title)}
+ ${escapeXml(entry.content.replace(/<[^>]*>/g, '').substring(0, 200))}...
+
${entry.url}
${entry.url}
${new Date(entry.date).toUTCString()}
@@ -164,4 +160,4 @@ export async function GET() {
"Cache-Control": "public, max-age=3600, s-maxage=3600",
},
})
-}
+}
\ No newline at end of file