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