RSS & Sitemap

This commit is contained in:
LittleSheep 2024-02-24 18:54:47 +08:00
parent ea50f739b3
commit 86bb1d349a
9 changed files with 99 additions and 20 deletions

View File

@ -11,6 +11,8 @@ export interface RelatedAccount {
}
export const SITE_NAME = "Goatshed";
export const SITE_DESCRIPTION = "山羊寒舍,在这里最终智羊工作室的最新动态。";
export const SITE_URL = "https://smartsheep.studio"
export const RELATED_ACCOUNTS: RelatedAccount[] = [
{ icon: <GitHubIcon />, platform: "GitHub", accountName: "@smartsheep-hq", link: "https://github.com/smartsheep-hq" },

28
app/feed/route.ts Normal file
View File

@ -0,0 +1,28 @@
import RSS from "rss";
import { SITE_DESCRIPTION, SITE_NAME, SITE_URL } from "@/app/consts";
import { getSortedPosts } from "@/content/posts";
export async function GET() {
const feed = new RSS({
title: SITE_NAME,
description: SITE_DESCRIPTION,
site_url: SITE_URL,
feed_url: `${SITE_URL}/feed`,
language: "zh-CN"
});
getSortedPosts().forEach((item) => {
feed.item({
url: `${SITE_URL}/p/${item.id}`,
title: item.title,
description: item.description ?? "No description yet.",
date: item.date,
});
});
return new Response(feed.xml(), {
headers: {
"content-type": "application/xml"
}
});
}

View File

@ -3,7 +3,7 @@ import { ReactNode } from "react";
import { ThemeProvider } from "@mui/material/styles";
import { AppRouterCacheProvider } from "@mui/material-nextjs/v13-appRouter";
import { CssBaseline } from "@mui/material";
import { SITE_NAME } from "@/app/consts";
import { SITE_DESCRIPTION, SITE_NAME } from "@/app/consts";
import { theme } from "@/app/theme";
import "@fontsource/roboto/300.css";
@ -16,15 +16,18 @@ import "./globals.css";
import AppShell from "@/components/AppShell";
export const metadata: Metadata = {
title: SITE_NAME,
description: "山羊寒舍,在这里最终智羊工作室的最新动态。"
title: {
default: SITE_NAME,
template: `${SITE_NAME} | %s`
},
description: SITE_DESCRIPTION,
};
export default function RootLayout({ children }: Readonly<{
children: ReactNode;
}>) {
return (
<html lang="en">
<html lang="zh-CN">
<body>
<AppRouterCacheProvider>
<CssBaseline />

View File

@ -30,7 +30,7 @@ export default function PostList() {
</Typography>
</CardContent>
<CardActions sx={{ paddingX: 4, paddingBottom: 2 }}>
<Link href={`/posts/${post.id}`} passHref>
<Link href={`/p/${post.id}`} passHref>
<Button>Read more</Button>
</Link>
</CardActions>

29
app/sitemap.ts Normal file
View File

@ -0,0 +1,29 @@
import { MetadataRoute } from "next";
import { getSortedPosts, Post } from "@/content/posts";
import { SITE_URL } from "@/app/consts";
export default function sitemap(): MetadataRoute.Sitemap {
const posts = getSortedPosts();
return [
{
url: `${SITE_URL}/`,
lastModified: new Date(),
changeFrequency: "weekly",
priority: 1
},
{
url: `${SITE_URL}/posts`,
lastModified: new Date(),
changeFrequency: "daily",
priority: 0.8
},
...posts.map((item: Post) => ({
url: `${SITE_URL}/posts/${item.id}`,
lastModified: item.date,
changeFrequency: "daily" as any,
priority: 0.75
}))
];
}

BIN
bun.lockb

Binary file not shown.

View File

@ -18,18 +18,22 @@ import { theme } from "@/app/theme";
import { ReactNode } from "react";
import HomeIcon from "@mui/icons-material/Home";
import ArticleIcon from "@mui/icons-material/Article";
import FeedIcon from "@mui/icons-material/RssFeed";
import Link from "next/link";
export interface NavigationItem {
icon: ReactNode;
title: string;
link: string;
icon?: ReactNode;
title?: string;
link?: string;
divider?: boolean;
}
export const DRAWER_WIDTH = 320;
export const NAVIGATION_ITEMS: NavigationItem[] = [
{ icon: <HomeIcon />, title: "首页", link: "/" },
{ icon: <ArticleIcon />, title: "新闻", link: "/posts" },
{ divider: true },
{ icon: <FeedIcon />, title: "订阅源", link: "/feed" },
];
export const AppNavigationHeader = styled("div")(({ theme }) => ({
@ -57,14 +61,16 @@ export function AppNavigation({ showClose, onClose }: {
</AppNavigationHeader>
<Divider />
<List>
{NAVIGATION_ITEMS.map((item, idx) => (
<Link key={idx} href={item.link} passHref>
<ListItemButton>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} />
</ListItemButton>
</Link>
))}
{NAVIGATION_ITEMS.map((item, idx) => {
return item.divider ? <Divider /> : (
<Link key={idx} href={item.link ?? "/"} passHref>
<ListItemButton>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} />
</ListItemButton>
</Link>
);
})}
</List>
</>
);

View File

@ -5,9 +5,18 @@ const nextConfig = {
remotePatterns: [
{
protocol: "https",
hostname: "**",
},
],
hostname: "**"
}
]
},
async rewrites() {
return [
{ source: "/rss", destination: "/feed" },
{ source: "/rss.xml", destination: "/feed.xml" },
{ source: "/feed.xml", destination: "/feed" },
{ source: "/p/:id", destination: "/posts/:id" }
];
}
};

View File

@ -24,13 +24,15 @@
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",
"react-markdown": "^9.0.1"
"react-markdown": "^9.0.1",
"rss": "^1.2.2"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/rss": "^0.0.32",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.0",