summaryrefslogtreecommitdiff
path: root/src/app/(main)
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/(main)')
-rw-r--r--src/app/(main)/(home)/_components/posts.tsx58
-rw-r--r--src/app/(main)/(home)/actions.ts15
-rw-r--r--src/app/(main)/(home)/page.tsx9
-rw-r--r--src/app/(main)/(home)/posts/[slug]/page.tsx185
-rw-r--r--src/app/(main)/(home)/posts/page.tsx84
-rw-r--r--src/app/(main)/(home)/tags/[...slug]/page.tsx80
-rw-r--r--src/app/(main)/(home)/tags/page.tsx13
-rw-r--r--src/app/(main)/api/search/route.ts27
-rw-r--r--src/app/(main)/og/[...slug]/route.tsx48
-rw-r--r--src/app/(main)/rss.xml/route.ts36
10 files changed, 165 insertions, 390 deletions
diff --git a/src/app/(main)/(home)/_components/posts.tsx b/src/app/(main)/(home)/_components/posts.tsx
index 00ada0c..0eacce1 100644
--- a/src/app/(main)/(home)/_components/posts.tsx
+++ b/src/app/(main)/(home)/_components/posts.tsx
@@ -2,73 +2,25 @@ import { Icons } from '@/components/icons/icons';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { buttonVariants } from '@/components/ui/button';
-import type { Page } from '@/lib/source';
import type { BlogPost } from '@/lib/payload-posts';
import Link from 'next/link';
-// 统一的文章数据格式
-interface UnifiedPost {
- title: string;
- description: string;
- image?: string | null;
- url: string;
- date: string;
- author: string;
- tags?: string[];
-}
-
-// 将 MDX Page 转换为统一格式
-function transformMdxPost(post: Page): UnifiedPost {
- return {
- title: post.data.title,
- description: post.data.description ?? '',
- image: post.data.image,
- url: post.url,
- date: new Date(post.data.date).toDateString(),
- author: post.data.author,
- tags: post.data.tags,
- };
-}
-
-// 将 Payload BlogPost 转换为统一格式
-function transformPayloadPost(post: BlogPost): UnifiedPost {
- return {
- title: post.title,
- description: post.description,
- image: post.image,
- url: post.url,
- date: post.date.toDateString(),
- author: post.author,
- tags: post.tags,
- };
-}
-
interface PostsProps {
- mdxPosts?: Page[];
- payloadPosts?: BlogPost[];
+ posts: BlogPost[];
}
-export default function Posts({ mdxPosts = [], payloadPosts = [] }: PostsProps) {
- // 转换并合并所有文章
- const allPosts: UnifiedPost[] = [
- ...mdxPosts.map(transformMdxPost),
- ...payloadPosts.map(transformPayloadPost),
- ];
-
- // 按日期排序
- allPosts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
-
+export default function Posts({ posts }: PostsProps) {
return (
<Section>
<div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'>
- {allPosts.map((post) => (
+ {posts.map((post) => (
<PostCard
title={post.title}
description={post.description}
image={post.image}
url={post.url}
- date={post.date}
- key={post.url}
+ date={post.date.toDateString()}
+ key={post.id}
author={post.author}
tags={post.tags}
/>
diff --git a/src/app/(main)/(home)/actions.ts b/src/app/(main)/(home)/actions.ts
index fdb16ca..5b0c456 100644
--- a/src/app/(main)/(home)/actions.ts
+++ b/src/app/(main)/(home)/actions.ts
@@ -1,8 +1,7 @@
'use server';
-import { getContact, sendWelcomeEmail, updateContact } from '@/lib/resend';
+import { getContact, updateContact } from '@/lib/resend';
import { ActionError, actionClient } from '@/lib/safe-action';
-import { getSortedByDatePosts } from '@/lib/source';
import { NewsletterSchema } from '@/lib/validators';
import { getSession } from '@/server/auth';
import { Resend } from 'resend';
@@ -59,12 +58,12 @@ export const subscribeUser = actionClient
);
}
- const posts = getSortedByDatePosts();
- await sendWelcomeEmail({
- posts,
- to: email,
- firstName: firstName || 'there',
- });
+ // const posts = getSortedByDatePosts();
+ // await sendWelcomeEmail({
+ // posts,
+ // to: email,
+ // firstName: firstName || 'there',
+ // });
return {
success: true,
diff --git a/src/app/(main)/(home)/page.tsx b/src/app/(main)/(home)/page.tsx
index a94193c..0718da9 100644
--- a/src/app/(main)/(home)/page.tsx
+++ b/src/app/(main)/(home)/page.tsx
@@ -3,16 +3,11 @@ import Posts from '@/app/(main)/(home)/_components/posts';
import { Icons } from '@/components/icons/icons';
import { Section } from '@/components/section';
import Separator from '@/components/separator';
-import { getSortedByDatePosts } from '@/lib/source';
import { getPublishedPosts } from '@/lib/payload-posts';
import { CTA } from './_components/call-to-action';
export default async function Home() {
- // 获取 MDX 文章(保留原有功能)
- // const mdxPosts = getSortedByDatePosts().slice(0, 3);
-
- // 获取 Payload CMS 文章
- const { posts: payloadPosts } = await getPublishedPosts({ limit: 3 });
+ const { posts } = await getPublishedPosts({ limit: 3 });
return (
<>
@@ -26,7 +21,7 @@ export default async function Home() {
</h2>
</Section>
<Separator />
- <Posts mdxPosts={[]} payloadPosts={payloadPosts} />
+ <Posts posts={posts} />
<Separator />
<CTA />
</>
diff --git a/src/app/(main)/(home)/posts/[slug]/page.tsx b/src/app/(main)/(home)/posts/[slug]/page.tsx
index fa096b6..5960f06 100644
--- a/src/app/(main)/(home)/posts/[slug]/page.tsx
+++ b/src/app/(main)/(home)/posts/[slug]/page.tsx
@@ -7,55 +7,18 @@ import { RichText } from '@/components/rich-text';
import { Section } from '@/components/section';
import { TagCard } from '@/components/tags/tag-card';
import { createMetadata } from '@/lib/metadata';
-import { metadataImage } from '@/lib/metadata-image';
import {
getPostBySlug,
getAllPostSlugs,
type BlogPost,
} from '@/lib/payload-posts';
-import { type Page as MDXPage, getPost, getPosts } from '@/lib/source';
import { cn } from '@/lib/utils';
-import { File, Files, Folder } from 'fumadocs-ui/components/files';
-import { InlineTOC } from 'fumadocs-ui/components/inline-toc';
-import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
-import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import Balancer from 'react-wrap-balancer';
import { description as homeDescription } from '@/app/(main)/layout.config';
-// MDX 文章 Header
-function MdxHeader(props: { page: MDXPage; tags?: string[] }) {
- const { page, tags } = props;
-
- return (
- <Section className="p-4 lg:p-6">
- <div
- className={cn(
- 'flex flex-col items-start justify-center gap-4 py-8 md:gap-6',
- 'sm:items-center sm:rounded-lg sm:border sm:bg-muted/70 sm:px-8 sm:py-20 sm:shadow-xs sm:dark:bg-muted'
- )}
- >
- <div className="flex flex-col gap-2 sm:text-center md:gap-4">
- <h1 className="max-w-4xl font-bold text-3xl leading-tight tracking-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight">
- <Balancer>{page.data.title}</Balancer>
- </h1>
- <p className="mx-auto max-w-4xl">
- <Balancer>{page.data.description}</Balancer>
- </p>
- </div>
- <div className="flex flex-wrap gap-2">
- {tags?.map((tag) => (
- <TagCard name={tag} key={tag} className=" border border-border " />
- ))}
- </div>
- </div>
- </Section>
- );
-}
-
-// Payload 文章 Header
-function PayloadHeader(props: { post: BlogPost }) {
+function PostHeader(props: { post: BlogPost }) {
const { post } = props;
return (
@@ -84,70 +47,10 @@ function PayloadHeader(props: { post: BlogPost }) {
);
}
-// MDX 文章内容
-function MdxContent({ page }: { page: MDXPage }) {
- const { body: Mdx, toc, tags, lastModified } = page.data;
- const lastUpdate = lastModified ? new Date(lastModified) : undefined;
-
- return (
- <>
- <MdxHeader page={page} tags={tags} />
-
- <Section className="h-full" sectionClassName="flex flex-1">
- <article className="flex min-h-full flex-col lg:flex-row">
- <div className="flex flex-1 flex-col gap-4">
- <InlineTOC
- items={toc}
- className="rounded-none border-0 border-border/70 border-b border-dashed dark:border-border"
- />
- <div className="prose min-w-0 flex-1 px-4">
- <Mdx
- components={{
- ...defaultMdxComponents,
- File,
- Files,
- Folder,
- Tabs,
- Tab,
- }}
- />
- </div>
- <PostComments
- slug={page.slugs[0] ?? ''}
- className="[&_form>div]:!rounded-none rounded-none border-0 border-border/70 border-t border-dashed dark:border-border"
- />
- </div>
- <div className="flex flex-col gap-4 p-4 text-sm lg:sticky lg:top-[4rem] lg:h-[calc(100vh-4rem)] lg:w-[250px] lg:self-start lg:overflow-y-auto lg:border-border/70 lg:border-l lg:border-dashed lg:dark:border-border">
- <div>
- <p className="mb-1 text-fd-muted-foreground">Written by</p>
- <p className="font-medium">{page.data.author}</p>
- </div>
- <div>
- <p className="mb-1 text-fd-muted-foreground text-sm">Created At</p>
- <p className="font-medium">
- {new Date(page.data.date ?? page.file.name).toDateString()}
- </p>
- </div>
- {lastUpdate && (
- <div>
- <p className="mb-1 text-fd-muted-foreground text-sm">Updated At</p>
- <p className="font-medium">{lastUpdate.toDateString()}</p>
- </div>
- )}
- <Share url={page.url} />
- </div>
- </article>
- </Section>
- <PostJsonLd page={page} />
- </>
- );
-}
-
-// Payload 文章内容
-function PayloadContent({ post }: { post: BlogPost }) {
+function PostContent({ post }: { post: BlogPost }) {
return (
<>
- <PayloadHeader post={post} />
+ <PostHeader post={post} />
<Section className="h-full" sectionClassName="flex flex-1">
<article className="flex min-h-full flex-col lg:flex-row">
@@ -179,6 +82,7 @@ function PayloadContent({ post }: { post: BlogPost }) {
</div>
</article>
</Section>
+ <PostJsonLd post={post} />
</>
);
}
@@ -187,80 +91,39 @@ export default async function Page(props: {
params: Promise<{ slug: string }>;
}) {
const params = await props.params;
+ const post = await getPostBySlug(params.slug);
- // 先尝试获取 MDX 文章
- const mdxPage = getPost([params.slug]);
-
- if (mdxPage) {
- return <MdxContent page={mdxPage} />;
- }
-
- // 再尝试获取 Payload 文章
- const payloadPost = await getPostBySlug(params.slug);
-
- if (payloadPost) {
- return <PayloadContent post={payloadPost} />;
+ if (!post) {
+ notFound();
}
- // 都找不到则 404
- notFound();
+ return <PostContent post={post} />;
}
export async function generateMetadata(props: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const params = await props.params;
+ const post = await getPostBySlug(params.slug);
- // 先尝试 MDX 文章
- const mdxPage = getPost([params.slug]);
-
- if (mdxPage) {
- const title = mdxPage.data.title;
- const description = mdxPage.data.description ?? homeDescription;
-
- return createMetadata(
- metadataImage.withImage(mdxPage.slugs, {
- title,
- description,
- openGraph: {
- url: `/posts/${mdxPage.slugs.join('/')}`,
- },
- alternates: {
- canonical: mdxPage.url,
- },
- })
- );
+ if (!post) {
+ return {};
}
- // 再尝试 Payload 文章
- const payloadPost = await getPostBySlug(params.slug);
-
- if (payloadPost) {
- return createMetadata({
- title: payloadPost.title,
- description: payloadPost.description || homeDescription,
- openGraph: {
- url: `/posts/${payloadPost.slug}`,
- },
- alternates: {
- canonical: `/posts/${payloadPost.slug}`,
- },
- });
- }
-
- return {};
+ return createMetadata({
+ title: post.title,
+ description: post.description || homeDescription,
+ openGraph: {
+ url: `/posts/${post.slug}`,
+ images: post.image ? [{ url: post.image }] : undefined,
+ },
+ alternates: {
+ canonical: `/posts/${post.slug}`,
+ },
+ });
}
export async function generateStaticParams(): Promise<{ slug: string }[]> {
- // MDX 文章的 slugs
- const mdxSlugs = getPosts()
- .map((page) => page.slugs[0])
- .filter((slug): slug is string => !!slug)
- .map((slug) => ({ slug }));
-
- // Payload 文章的 slugs
- const payloadSlugs = await getAllPostSlugs();
- const payloadParams = payloadSlugs.map((slug) => ({ slug }));
-
- return [...mdxSlugs, ...payloadParams];
+ const slugs = await getAllPostSlugs();
+ return slugs.map((slug) => ({ slug }));
}
diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx
index 00e51c4..40ebcda 100644
--- a/src/app/(main)/(home)/posts/page.tsx
+++ b/src/app/(main)/(home)/posts/page.tsx
@@ -3,70 +3,10 @@ import { NumberedPagination } from '@/components/numbered-pagination';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { createMetadata } from '@/lib/metadata';
-import { getPublishedPosts, type BlogPost } from '@/lib/payload-posts';
-import { getSortedByDatePosts, type Page } from '@/lib/source';
+import { getPublishedPosts } from '@/lib/payload-posts';
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound, redirect } from 'next/navigation';
-// 统一的文章类型
-interface UnifiedPost {
- id: string;
- title: string;
- description: string;
- image?: string;
- url: string;
- date: Date;
- author: string;
- tags?: string[];
- source: 'mdx' | 'payload';
-}
-
-// 将 MDX 文章转换为统一格式
-function transformMdxPost(post: Page): UnifiedPost {
- return {
- id: `mdx-${post.url}`,
- title: post.data.title,
- description: post.data.description ?? '',
- image: post.data.image,
- url: post.url,
- date: new Date(post.data.date),
- author: post.data.author,
- tags: post.data.tags,
- source: 'mdx',
- };
-}
-
-// 将 Payload 文章转换为统一格式
-function transformPayloadPost(post: BlogPost): UnifiedPost {
- return {
- id: `payload-${post.id}`,
- title: post.title,
- description: post.description,
- image: post.image,
- url: post.url,
- date: post.date,
- author: post.author,
- tags: post.tags,
- source: 'payload',
- };
-}
-
-// 获取所有文章(合并 MDX 和 Payload)
-async function getAllPosts(): Promise<UnifiedPost[]> {
- // 获取 MDX 文章
- const mdxPosts = getSortedByDatePosts().map(transformMdxPost);
-
- // 获取 Payload 文章(获取全部,用于分页计算)
- const { posts: payloadPosts } = await getPublishedPosts({ limit: 1000 });
- const transformedPayloadPosts = payloadPosts.map(transformPayloadPost);
-
- // 合并并按日期排序
- const allPosts = [...mdxPosts, ...transformedPayloadPosts];
- allPosts.sort((a, b) => b.date.getTime() - a.date.getTime());
-
- return allPosts;
-}
-
const CurrentPostsCount = ({
startIndex,
endIndex,
@@ -115,11 +55,6 @@ export default async function Page(props: {
}) {
const searchParams = await props.searchParams;
- // 获取所有文章
- const allPosts = await getAllPosts();
- const totalPosts = allPosts.length;
- const pageCount = Math.ceil(totalPosts / postsPerPage);
-
const pageIndex = searchParams.page
? Number.parseInt(
Array.isArray(searchParams.page)
@@ -129,21 +64,26 @@ export default async function Page(props: {
) - 1
: 0;
- if (pageIndex < 0 || (pageCount > 0 && pageIndex >= pageCount)) notFound();
+ // 获取文章(带分页)
+ const { posts, totalDocs, totalPages } = await getPublishedPosts({
+ limit: postsPerPage,
+ page: pageIndex + 1,
+ });
+
+ if (pageIndex < 0 || (totalPages > 0 && pageIndex >= totalPages)) notFound();
const startIndex = pageIndex * postsPerPage;
- const endIndex = startIndex + postsPerPage;
- const posts = allPosts.slice(startIndex, endIndex);
+ const endIndex = startIndex + posts.length;
return (
<>
<Section className='p-4 lg:p-6'>
<h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'>
- All {totalPosts} Posts{' '}
+ All {totalDocs} Posts{' '}
<CurrentPostsCount
startIndex={startIndex}
endIndex={endIndex}
- totalPosts={totalPosts}
+ totalPosts={totalDocs}
/>
</h1>
</Section>
@@ -166,7 +106,7 @@ export default async function Page(props: {
})}
</div>
</Section>
- {pageCount > 1 && <Pagination pageIndex={pageIndex} pageCount={pageCount} />}
+ {totalPages > 1 && <Pagination pageIndex={pageIndex} pageCount={totalPages} />}
</>
);
}
diff --git a/src/app/(main)/(home)/tags/[...slug]/page.tsx b/src/app/(main)/(home)/tags/[...slug]/page.tsx
index c1ce96b..71615cb 100644
--- a/src/app/(main)/(home)/tags/[...slug]/page.tsx
+++ b/src/app/(main)/(home)/tags/[...slug]/page.tsx
@@ -5,28 +5,21 @@ import { NumberedPagination } from '@/components/numbered-pagination';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { createMetadata } from '@/lib/metadata';
-import { getPostsByTag, getTags } from '@/lib/source';
+import { getPostsByTag, getAllTags } from '@/lib/payload-posts';
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound, redirect } from 'next/navigation';
-export const dynamicParams = false;
-
-const totalPosts = (title: string) => getPostsByTag(title).length;
-const pageCount = (title: string) =>
- Math.ceil(totalPosts(title) / postsPerPage);
-
const CurrentPostsCount = ({
startIndex,
endIndex,
- tag,
+ totalPosts,
}: {
startIndex: number;
endIndex: number;
- tag: string;
+ totalPosts: number;
}) => {
- const total = totalPosts(tag);
const start = startIndex + 1;
- const end = endIndex < total ? endIndex : total;
+ const end = endIndex < totalPosts ? endIndex : totalPosts;
if (start === end) return <span>({start})</span>;
return (
<span>
@@ -39,10 +32,12 @@ const Header = ({
tag,
startIndex,
endIndex,
+ totalPosts,
}: {
tag: string;
startIndex: number;
endIndex: number;
+ totalPosts: number;
}) => (
<Section className='p-4 lg:p-6'>
<div className='flex items-center gap-2'>
@@ -55,14 +50,14 @@ const Header = ({
<CurrentPostsCount
startIndex={startIndex}
endIndex={endIndex}
- tag={tag}
+ totalPosts={totalPosts}
/>
</h1>
</div>
</Section>
);
-const Pagination = ({ pageIndex, tag }: { pageIndex: number; tag: string }) => {
+const Pagination = ({ pageIndex, tag, totalPages }: { pageIndex: number; tag: string; totalPages: number }) => {
const handlePageChange = async (page: number) => {
'use server';
redirect(`/tags/${tag}?page=${page}`);
@@ -72,7 +67,7 @@ const Pagination = ({ pageIndex, tag }: { pageIndex: number; tag: string }) => {
<Section className='bg-dashed'>
<NumberedPagination
currentPage={pageIndex + 1}
- totalPages={pageCount(tag)}
+ totalPages={totalPages}
paginationItemsToDisplay={5}
onPageChange={handlePageChange}
/>
@@ -91,54 +86,56 @@ export default async function Page(props: {
if (!tag) return notFound();
const pageIndex = searchParams.page
- ? Number.parseInt(searchParams.page[0] ?? '', 10) - 1
+ ? Number.parseInt(
+ Array.isArray(searchParams.page)
+ ? searchParams.page[0] ?? ''
+ : searchParams.page,
+ 10
+ ) - 1
: 0;
- if (pageIndex < 0 || pageIndex >= pageCount(tag)) notFound();
+ const { posts, totalDocs, totalPages } = await getPostsByTag(tag, {
+ limit: postsPerPage,
+ page: pageIndex + 1,
+ });
+
+ if (pageIndex < 0 || (totalPages > 0 && pageIndex >= totalPages)) notFound();
const startIndex = pageIndex * postsPerPage;
- const endIndex = startIndex + postsPerPage;
- const posts = getPostsByTag(tag).slice(startIndex, endIndex);
+ const endIndex = startIndex + posts.length;
return (
<>
- <Header tag={tag} startIndex={startIndex} endIndex={endIndex} />
+ <Header tag={tag} startIndex={startIndex} endIndex={endIndex} totalPosts={totalDocs} />
<Section className='h-full' sectionClassName='flex flex-1'>
<div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'>
{posts.map((post) => {
- const date = new Date(post.data.date).toDateString();
+ const date = post.date.toDateString();
return (
<PostCard
- title={post.data.title}
- description={post.data.description ?? ''}
- image={post.data.image}
+ title={post.title}
+ description={post.description}
+ image={post.image}
url={post.url}
date={date}
- key={post.url}
- author={post.data.author}
- tags={post.data.tags}
+ key={post.id}
+ author={post.author}
+ tags={post.tags}
/>
);
})}
</div>
</Section>
- {pageCount(tag) > 1 && <Pagination pageIndex={pageIndex} tag={tag} />}
+ {totalPages > 1 && <Pagination pageIndex={pageIndex} tag={tag} totalPages={totalPages} />}
<TagJsonLd tag={tag} />
</>
);
}
-export const generateStaticParams = () => {
- const tags = getTags();
- return [
- ...tags.map((tag) => ({ slug: [tag] })),
- ...tags.flatMap((tag) =>
- Array.from({ length: pageCount(tag) }, (_, index) => ({
- slug: [tag, (index + 1).toString()],
- })),
- ),
- ];
-};
+export async function generateStaticParams() {
+ const tags = await getAllTags();
+ return tags.map((item) => ({ slug: [item.tag] }));
+}
type Props = {
params: Promise<{ slug: string[] }>;
@@ -154,7 +151,12 @@ export async function generateMetadata(
const tag = params.slug[0];
const pageIndex = searchParams.page
- ? Number.parseInt(searchParams.page.toString(), 10)
+ ? Number.parseInt(
+ Array.isArray(searchParams.page)
+ ? searchParams.page[0] ?? ''
+ : searchParams.page,
+ 10
+ )
: 1;
const isFirstPage = pageIndex === 1 || !searchParams.page;
diff --git a/src/app/(main)/(home)/tags/page.tsx b/src/app/(main)/(home)/tags/page.tsx
index 9138fde..6db13fc 100644
--- a/src/app/(main)/(home)/tags/page.tsx
+++ b/src/app/(main)/(home)/tags/page.tsx
@@ -2,12 +2,12 @@ import { title as homeTitle } from '@/app/(main)/layout.config';
import { Section } from '@/components/section';
import { TagCard } from '@/components/tags/tag-card';
import { createMetadata } from '@/lib/metadata';
-import { getTags } from '@/lib/source';
+import { getAllTags } from '@/lib/payload-posts';
import { cn } from '@/lib/utils';
import type { Metadata } from 'next';
-export default function Page() {
- const tags = getTags();
+export default async function Page() {
+ const tags = await getAllTags();
return (
<>
@@ -18,11 +18,12 @@ export default function Page() {
</Section>
<Section className='h-full' sectionClassName='flex flex-1'>
<div className='grid grid-cols-1 divide-y divide-dashed divide-border/70 sm:grid-cols-2 lg:grid-cols-4 dark:divide-border'>
- {tags.map((tag, index) => (
+ {tags.map((item, index) => (
<TagCard
- key={tag}
+ key={item.tag}
displayCount={true}
- name={tag}
+ name={item.tag}
+ count={item.count}
className={cn(
'items-center justify-start gap-2 rounded-none border-r-0 bg-card/50 p-6 last:border-border/70 last:border-b last:border-dashed hover:bg-card/80 last:dark:border-border',
tags.at(index - 1) && 'border-l',
diff --git a/src/app/(main)/api/search/route.ts b/src/app/(main)/api/search/route.ts
index 3c99f5c..cc2a1c8 100644
--- a/src/app/(main)/api/search/route.ts
+++ b/src/app/(main)/api/search/route.ts
@@ -1,11 +1,20 @@
-import { getPosts } from '@/lib/source';
+import { getPublishedPosts } from '@/lib/payload-posts';
import { createSearchAPI } from 'fumadocs-core/search/server';
-export const { GET } = createSearchAPI('advanced', {
- indexes: getPosts().map((page) => ({
- title: page.data.title,
- structuredData: page.data.structuredData,
- id: page.url,
- url: page.url,
- })),
-});
+// 动态生成搜索索引
+export async function GET(request: Request) {
+ const { posts } = await getPublishedPosts({ limit: 1000 });
+
+ const indexes = posts.map((post) => ({
+ title: post.title,
+ description: post.description,
+ id: post.url,
+ url: post.url,
+ }));
+
+ const searchAPI = createSearchAPI('advanced', {
+ indexes,
+ });
+
+ return searchAPI.GET(request);
+}
diff --git a/src/app/(main)/og/[...slug]/route.tsx b/src/app/(main)/og/[...slug]/route.tsx
index d713923..77ae7f8 100644
--- a/src/app/(main)/og/[...slug]/route.tsx
+++ b/src/app/(main)/og/[...slug]/route.tsx
@@ -1,6 +1,7 @@
import { generateOGImage } from '@/app/(main)/og/[...slug]/og';
-import { metadataImage } from '@/lib/metadata-image';
+import { getPostBySlug, getAllPostSlugs } from '@/lib/payload-posts';
import type { ImageResponse } from 'next/og';
+import { notFound } from 'next/navigation';
async function loadAssets(): Promise<
{ name: string; data: Buffer; weight: 400 | 600; style: 'normal' }[]
@@ -39,20 +40,33 @@ async function loadAssets(): Promise<
];
}
-export const GET = metadataImage.createAPI(
- async (page): Promise<ImageResponse> => {
- const [fonts] = await Promise.all([loadAssets()]);
-
- return generateOGImage({
- title: page.data.title,
- description: page.data.description,
- fonts,
- });
- },
-);
-
-export function generateStaticParams(): {
- slug: string[];
-}[] {
- return metadataImage.generateParams();
+export async function GET(
+ request: Request,
+ { params }: { params: Promise<{ slug: string[] }> }
+): Promise<ImageResponse> {
+ const { slug } = await params;
+ const postSlug = slug[0];
+
+ if (!postSlug) {
+ notFound();
+ }
+
+ const post = await getPostBySlug(postSlug);
+
+ if (!post) {
+ notFound();
+ }
+
+ const fonts = await loadAssets();
+
+ return generateOGImage({
+ title: post.title,
+ description: post.description,
+ fonts,
+ });
+}
+
+export async function generateStaticParams(): Promise<{ slug: string[] }[]> {
+ const slugs = await getAllPostSlugs();
+ return slugs.map((slug) => ({ slug: [slug, 'image.png'] }));
}
diff --git a/src/app/(main)/rss.xml/route.ts b/src/app/(main)/rss.xml/route.ts
index ee06a40..3507948 100644
--- a/src/app/(main)/rss.xml/route.ts
+++ b/src/app/(main)/rss.xml/route.ts
@@ -1,10 +1,10 @@
import { description, title } from '@/app/(main)/layout.config';
import { owner } from '@/app/(main)/layout.config';
import { baseUrl } from '@/lib/constants';
-import { getPosts } from '@/lib/source';
+import { getPublishedPosts } from '@/lib/payload-posts';
import { Feed } from 'feed';
-export const dynamic = 'force-static';
+export const dynamic = 'force-dynamic';
const escapeForXML = (str: string) => {
return str
@@ -15,8 +15,8 @@ const escapeForXML = (str: string) => {
.replace(/'/g, '&apos;');
};
-export const GET = () => {
- const feed = createFeed();
+export const GET = async () => {
+ const feed = await createFeed();
return new Response(feed.atom1(), {
headers: {
@@ -25,7 +25,7 @@ export const GET = () => {
});
};
-function createFeed(): Feed {
+async function createFeed(): Promise<Feed> {
const feed = new Feed({
title,
description,
@@ -39,24 +39,24 @@ function createFeed(): Feed {
updated: new Date(),
});
- const posts = getPosts();
+ const { posts } = await getPublishedPosts({ limit: 1000 });
+
for (const post of posts) {
feed.addItem({
- title: post.data.title,
- description: post.data.description,
+ title: post.title,
+ description: post.description,
link: new URL(post.url, baseUrl).href,
- image: {
- title: post.data.title,
- type: 'image/png',
- url: escapeForXML(
- new URL(`/og/${post.slugs.join('/')}/image.png`, baseUrl.href).href,
- ),
- },
- date: post.data.date,
+ image: post.image
+ ? {
+ title: post.title,
+ type: 'image/png',
+ url: escapeForXML(new URL(post.image, baseUrl.href).href),
+ }
+ : undefined,
+ date: post.date,
author: [
{
- name: post.data.author,
- // link: new URL('/about', baseUrl).href,
+ name: post.author,
},
],
});