From 4d989e0a7db111e05fa09b6828ad1b525de509f9 Mon Sep 17 00:00:00 2001 From: radishzzz Date: Tue, 14 Jan 2025 09:05:43 +0000 Subject: [PATCH] feat: add RSS feed generation and content management features --- astro.config.ts | 1 + package.json | 2 ++ src/content/config.ts | 18 +++++++++++++ src/layouts/Layout.astro | 8 ++++++ src/pages/rss.xml.ts | 33 ++++++++++++++++++++++++ src/utils/content.config.ts | 50 +++++++++++++++++++++++++++++++++++++ tsconfig.json | 4 ++- 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/content/config.ts create mode 100644 src/pages/rss.xml.ts create mode 100644 src/utils/content.config.ts diff --git a/astro.config.ts b/astro.config.ts index a877751..01c188b 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -119,6 +119,7 @@ export default defineConfig({ 'className': ['anchor-icon'], 'data-pagefind-ignore': true, }, + // TODO: Switch # to icon children: [ { type: 'text', diff --git a/package.json b/package.json index 2a9919c..be5def1 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "astro-compress": "^2.3.6", "astro-robots-txt": "^1.0.0", "astro-seo": "^0.8.4", + "markdown-it": "^14.1.0", "overlayscrollbars": "^2.10.1", "photoswipe": "^5.4.4", "rehype-autolink-headings": "^7.1.0", @@ -46,6 +47,7 @@ "devDependencies": { "@antfu/eslint-config": "^3.14.0", "@types/hast": "^3.0.4", + "@types/markdown-it": "^14.1.2", "@types/mdast": "^4.0.4", "@types/node": "^22.10.6", "@types/sanitize-html": "^2.13.0", diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..95bddd5 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,18 @@ +import { defineCollection, z } from 'astro:content' + +const postsCollection = defineCollection({ + schema: z.object({ + title: z.string(), + published: z.date(), + updated: z.date().optional(), + draft: z.boolean().optional().default(false), + description: z.string().optional().default(''), + tags: z.array(z.string()).optional().default([]), + lang: z.string().optional().default(''), + slug: z.string().optional().default(''), + pin: z.boolean().optional().default(false), + }), +}) +export const collections = { + posts: postsCollection, +} diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 05587c2..5d44e87 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -6,6 +6,14 @@ Astro Basics + + + diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts new file mode 100644 index 0000000..e62a6e9 --- /dev/null +++ b/src/pages/rss.xml.ts @@ -0,0 +1,33 @@ +import type { APIContext } from 'astro' +import themeConfig from '@/config' +import rss from '@astrojs/rss' +import { getCollection } from 'astro:content' +import MarkdownIt from 'markdown-it' +import sanitizeHtml from 'sanitize-html' +import + +const parser = new MarkdownIt() +const { title, description, url } = themeConfig.site +const { language } = themeConfig.global + +export async function GET(_context: APIContext) { + const posts = await getCollection('blog', ({ data }) => { + return !data.draft // 只包含非草稿文章 + }) + + return rss({ + title, + description, + site: url, + items: posts.map(post => ({ + title: post.data.title, + pubDate: post.data.published, // 使用 published 而不是 pubDate + description: post.data.description, + link: `/posts/${post.slug}/`, + content: sanitizeHtml(parser.render(post.body), { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']), + }), + })), + customData: `${language}`, + }) +} diff --git a/src/utils/content.config.ts b/src/utils/content.config.ts new file mode 100644 index 0000000..3090423 --- /dev/null +++ b/src/utils/content.config.ts @@ -0,0 +1,50 @@ +import type { CollectionEntry } from 'astro:content' +import { getCollection } from 'astro:content' +import MarkdownIt from 'markdown-it' +import sanitizeHtml, { type Tag } from 'sanitize-html' + +export type Post = CollectionEntry<'posts'> + +export async function getPosts(isArchivePage = false) { + const posts = await getCollection('posts', ({ data }) => { + return import.meta.env.DEV || !data.draft + }) + + // 按发布日期降序排序 + return posts.sort((a, b) => b.data.published.valueOf() - a.data.published.valueOf()) +} + +// 获取所有标签及其对应的文章 +export async function getPostsByTags() { + const posts = await getPosts() + const tagMap = new Map() + + posts.forEach((post) => { + post.data.tags?.forEach((tag) => { + if (!tagMap.has(tag)) { + tagMap.set(tag, []) + } + tagMap.get(tag)?.push(post) + }) + }) + + return tagMap +} + +// 获取指定标签的所有文章 +export async function getPostsByTag(tag: string) { + const posts = await getPosts() + return posts.filter(post => post.data.tags?.includes(tag)) +} + +// 获取所有标签列表 +export async function getAllTags() { + const posts = await getPosts() + const tags = new Set() + + posts.forEach((post) => { + post.data.tags?.forEach(tag => tags.add(tag)) + }) + + return Array.from(tags) +} diff --git a/tsconfig.json b/tsconfig.json index f660573..8aff919 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,9 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"] - } + }, + "allowJs": false, + "strictNullChecks": true }, "include": [ ".astro/types.d.ts",