diff --git a/src/routes/rss.ts b/src/routes/rss.ts new file mode 100644 index 0000000..0b76429 --- /dev/null +++ b/src/routes/rss.ts @@ -0,0 +1,124 @@ +import { get_blog } from '@/db/blogs'; +import { DOMAIN } from '@/lib/constants'; +import Hono from '@/lib/hono'; +import type { Blog, RssBlog, Post, RssPost } from '@/lib/types'; + +const rss = new Hono(); + +function addLeadingZero(num: number | string) { + const numString = num.toString(); + while (numString.length < 2) num = "0" + num; + return num; +} + +function buildRFC822Date(dateString: string) { + const dayStrings = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + + const timeStamp = Date.parse(dateString); + const date = new Date(timeStamp); + + const day = dayStrings[date.getDay()]; + const dayNumber = addLeadingZero(date.getDate()); + const month = monthStrings[date.getMonth()]; + const year = date.getFullYear(); + const time = `${addLeadingZero(date.getHours())}:${addLeadingZero(date.getMinutes())}:00`; + const timezone = date.getTimezoneOffset() === 0 ? "GMT" : "BST"; + + //Wed, 02 Oct 2002 13:00:00 GMT + return `${day}, ${dayNumber} ${month} ${year} ${time} ${timezone}`; +} + +interface RssItem { + title: string; + description: string; + pubDate: Date; + guid: string; +} + +class RssFeed { + [x: string]: any; + constructor(data: { title: string, description: string, author?: string, link: string; }) { + Object.assign(this, data); + this.items = [] as RssItem[]; + } + + addItem(item: RssItem) { + this.items.push(item); + } + + getItems(): RssItem[] { + return this.items; + } + + generateRss(): string { + let items = ""; + this.items.forEach((item: RssItem) => { + items = items.concat(` + + ${item.title} + ${this.link + item.guid} + ${this.link + item.guid} + ${item.description} + ${buildRFC822Date(item.pubDate.toISOString())} + `); + }); + return ` + + + + ${this.title} + ${this.link} + ${this.description} + ${items} + + +`; + } +} + +function generate_rss_feed(blog: Blog) { + blog.posts = blog.posts?.filter((p: Post) => p.published_at !== null); + + if (blog.posts!.length > 0) { + blog.posts!.sort((a: Post, b: Post) => { + let a_time = new Date(a.published_at!).getTime(); + let b_time = new Date(b.published_at!).getTime(); + return b_time - a_time + }); + blog.last_updated = buildRFC822Date(blog.posts![0]!.updated_at?.toISOString()!); + } + + const rss_feed = new RssFeed({ + title: blog.title, + description: blog.description, + link: `https://${blog.subdomain}.${DOMAIN}` + }); + + blog.posts?.forEach(post => rss_feed.addItem({ + description: "New post!", + title: post.title, + pubDate: new Date(post.published_at!), + guid: post.uuid! + })) + + return rss_feed.generateRss(); +} + +rss.get(":uuid/rss.xml", async (c) => { + const uuid = c.req.param("uuid"); + let blog; + try { + blog = await get_blog(uuid) as Blog & { posts: Post[], last_updated?: string }; + } catch (e) { + return c.notFound(); + } + + let rss = generate_rss_feed(blog); + + // c.header('Content-Type', 'application/rss+xml'); + c.header('Content-Type', 'application/rss+xml'); + return c.body(rss); +}) + +export default rss; \ No newline at end of file diff --git a/static/img/valid_rss.png b/static/img/valid_rss.png new file mode 100644 index 0000000000000000000000000000000000000000..4073266c7beb836e68639f15a6f3a977bd876f19 GIT binary patch literal 4206 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 F2>{Lv;$i>* literal 0 HcmV?d00001