diff --git a/bun.lockb b/bun.lockb
index 6656860..118717e 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 093f7c3..7d03859 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"axios": "^1.7.9",
"axios-case-converter": "^1.1.1",
"cookies-next": "^5.0.2",
+ "feed": "^4.2.2",
"next": "15.1.3",
"next-nprogress-bar": "^2.4.3",
"react": "^19.0.0",
diff --git a/src/pages/posts/feed.ts b/src/pages/posts/feed.ts
new file mode 100644
index 0000000..f4ca772
--- /dev/null
+++ b/src/pages/posts/feed.ts
@@ -0,0 +1,72 @@
+import { sni } from '@/services/network'
+import { SnPost } from '@/services/post'
+import { GetServerSideProps } from 'next'
+import { Feed } from 'feed'
+
+function generateFeed(posts: SnPost[]): string {
+ const feed = new Feed({
+ title: 'Solar Network Posts',
+ description: 'All posts on the Solar Network platform, and now you can view them via the RSS feed!',
+ id: 'https://solsynth.dev/posts',
+ link: 'https://solsynth.dev/posts',
+ favicon: 'https://solsynth.dev/favicon.png',
+ copyright: `All rights reserved ${new Date().getFullYear()} © Solsynth LLC & Post Publishers`,
+ updated: new Date(posts[0].createdAt),
+ generator: 'Capital',
+ feedLinks: {
+ json: 'https://example.com/json',
+ atom: 'https://example.com/atom',
+ },
+ })
+
+ for (const p of posts) {
+ feed.addItem({
+ id: p.id.toString(),
+ title: p.body.title ?? `Post #${p.id}`,
+ description: p.body.description,
+ link:
+ p.alias && p.aliasPrefix
+ ? `https://solsynth.dev/posts/${p.aliasPrefix}/${p.alias}`
+ : `https://solsynth.dev/posts/${p.id}`,
+ content: p.body.content,
+ date: new Date(p.publishedAt ?? p.createdAt),
+ published: new Date(p.publishedAt ?? p.createdAt),
+ copyright: `All right reserved ${new Date().getFullYear()} © @${p.publisher.name}`,
+ author: [
+ {
+ name: `@${p.publisher.name}`,
+ },
+ ],
+ })
+ }
+
+ return feed.rss2()
+}
+
+export const getServerSideProps: GetServerSideProps = async ({ res, query }) => {
+ let page: number = parseInt(query.page as string)
+ if (isNaN(page)) page = 1
+
+ const countPerPage = 20
+
+ const { data: resp } = await sni.get<{ data: SnPost[] }>('/cgi/co/posts', {
+ params: {
+ take: countPerPage,
+ offset: (page - 1) * countPerPage,
+ },
+ })
+
+ const sitemap = generateFeed(resp.data)
+
+ res.setHeader('Content-Type', 'text/xml')
+ res.write(sitemap)
+ res.end()
+
+ return {
+ props: {},
+ }
+}
+
+export default function PostSiteMap() {
+ // getServerSideProps will do the heavy lifting
+}
diff --git a/src/pages/posts/index.tsx b/src/pages/posts/index.tsx
index ade03b3..32f3295 100644
--- a/src/pages/posts/index.tsx
+++ b/src/pages/posts/index.tsx
@@ -2,9 +2,20 @@ import { AttachmentItem } from '@/components/attachments/AttachmentItem'
import { SnAttachment, listAttachment } from '@/services/attachment'
import { getAttachmentUrl, sni } from '@/services/network'
import { SnPost } from '@/services/post'
-import { Avatar, Box, Container, Divider, Grid2 as Grid, Pagination, Paper, Typography } from '@mui/material'
+import {
+ Avatar,
+ Box,
+ Button,
+ Container,
+ Divider,
+ Grid2 as Grid,
+ Link,
+ Pagination,
+ Paper,
+ Typography,
+} from '@mui/material'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
-import Link from 'next/link'
+import NextLink from 'next/link'
import { useRouter } from 'next/router'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
@@ -86,7 +97,7 @@ export default function PostList({ posts, page, pages }: InferGetServerSideProps
-
+
{(p.body.title || p.body.content) && (
@@ -108,7 +119,7 @@ export default function PostList({ posts, page, pages }: InferGetServerSideProps
{p.body.content && }
-
+
{p.attachments && (
))}
- router.push('/posts?page=' + page)}
- />
+
+ router.push('/posts?page=' + page)} />
+
+ RSS Feed
+
+
)
}