From a5e4155a752fa090c7bc3751a803b4359453e56c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:30:41 +0000 Subject: fix: frontend-backend connection bugs Agent-Logs-Url: https://github.com/bertyuan/next-blog/sessions/f86da32b-3af7-4393-8077-ce3435137221 Co-authored-by: bertyuan <189593334+bertyuan@users.noreply.github.com> --- src/app/(main)/(home)/actions.ts | 5 ++-- src/app/(main)/(home)/posts/[slug]/page.client.tsx | 5 ++-- src/app/(main)/(home)/posts/page.tsx | 19 ++++++++++---- src/app/(main)/(home)/tags/[...slug]/page.tsx | 29 ++++++++++++++++------ src/lib/resend.ts | 20 +++++++++------ 5 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/app/(main)/(home)/actions.ts b/src/app/(main)/(home)/actions.ts index 5b0c456..c4f586f 100644 --- a/src/app/(main)/(home)/actions.ts +++ b/src/app/(main)/(home)/actions.ts @@ -1,13 +1,14 @@ 'use server'; +import { env } from '@/env'; import { getContact, updateContact } from '@/lib/resend'; import { ActionError, actionClient } from '@/lib/safe-action'; import { NewsletterSchema } from '@/lib/validators'; import { getSession } from '@/server/auth'; import { Resend } from 'resend'; -const resend = new Resend(process.env.RESEND_API_KEY as string); -const audienceId = process.env.RESEND_AUDIENCE_ID as string; +const resend = new Resend(env.RESEND_API_KEY); +const audienceId = env.RESEND_AUDIENCE_ID; const splitName = (name = '') => { const [firstName, ...lastName] = name.split(' ').filter(Boolean); diff --git a/src/app/(main)/(home)/posts/[slug]/page.client.tsx b/src/app/(main)/(home)/posts/[slug]/page.client.tsx index 7a97f56..13dbfeb 100644 --- a/src/app/(main)/(home)/posts/[slug]/page.client.tsx +++ b/src/app/(main)/(home)/posts/[slug]/page.client.tsx @@ -7,7 +7,7 @@ import { Icons } from '@/components/icons/icons'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { Comments } from '@fuma-comment/react'; -import { redirect } from 'next/navigation'; +import { useRouter } from 'next/navigation'; import { useRef } from 'react'; import { toast } from 'sonner'; import { useCopyToClipboard } from 'usehooks-ts'; @@ -42,6 +42,7 @@ export function PostComments({ slug, className, }: { slug: string; className?: string }) { + const router = useRouter(); return ( { - redirect('/login'); + router.push('/login'); }, }} /> diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx index 40ebcda..daced35 100644 --- a/src/app/(main)/(home)/posts/page.tsx +++ b/src/app/(main)/(home)/posts/page.tsx @@ -58,12 +58,14 @@ export default async function Page(props: { const pageIndex = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) - ? searchParams.page[0] ?? '' + ? (searchParams.page[0] ?? '') : searchParams.page, - 10 + 10, ) - 1 : 0; + if (Number.isNaN(pageIndex)) notFound(); + // 获取文章(带分页) const { posts, totalDocs, totalPages } = await getPublishedPosts({ limit: postsPerPage, @@ -106,7 +108,9 @@ export default async function Page(props: { })} - {totalPages > 1 && } + {totalPages > 1 && ( + + )} ); } @@ -118,12 +122,17 @@ type Props = { export async function generateMetadata( props: Props, - parent: ResolvingMetadata + parent: ResolvingMetadata, ): Promise { const searchParams = await props.searchParams; const pageIndex = searchParams.page - ? Number.parseInt(searchParams.page as string, 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/[...slug]/page.tsx b/src/app/(main)/(home)/tags/[...slug]/page.tsx index 71615cb..45fa52f 100644 --- a/src/app/(main)/(home)/tags/[...slug]/page.tsx +++ b/src/app/(main)/(home)/tags/[...slug]/page.tsx @@ -5,7 +5,7 @@ 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, getAllTags } from '@/lib/payload-posts'; +import { getAllTags, getPostsByTag } from '@/lib/payload-posts'; import type { Metadata, ResolvingMetadata } from 'next'; import { notFound, redirect } from 'next/navigation'; @@ -57,7 +57,11 @@ const Header = ({ ); -const Pagination = ({ pageIndex, tag, totalPages }: { pageIndex: number; tag: string; totalPages: number }) => { +const Pagination = ({ + pageIndex, + tag, + totalPages, +}: { pageIndex: number; tag: string; totalPages: number }) => { const handlePageChange = async (page: number) => { 'use server'; redirect(`/tags/${tag}?page=${page}`); @@ -88,12 +92,14 @@ export default async function Page(props: { const pageIndex = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) - ? searchParams.page[0] ?? '' + ? (searchParams.page[0] ?? '') : searchParams.page, - 10 + 10, ) - 1 : 0; + if (Number.isNaN(pageIndex)) notFound(); + const { posts, totalDocs, totalPages } = await getPostsByTag(tag, { limit: postsPerPage, page: pageIndex + 1, @@ -106,7 +112,12 @@ export default async function Page(props: { return ( <> -
+
{posts.map((post) => { @@ -126,7 +137,9 @@ export default async function Page(props: { })}
- {totalPages > 1 && } + {totalPages > 1 && ( + + )} ); @@ -153,9 +166,9 @@ export async function generateMetadata( const pageIndex = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) - ? searchParams.page[0] ?? '' + ? (searchParams.page[0] ?? '') : searchParams.page, - 10 + 10, ) : 1; diff --git a/src/lib/resend.ts b/src/lib/resend.ts index 5ab7032..fb39186 100644 --- a/src/lib/resend.ts +++ b/src/lib/resend.ts @@ -1,9 +1,10 @@ +import { env } from '@/env'; import { baseUrl } from '@/lib/constants'; import { Resend, type UpdateContactOptions } from 'resend'; import NewsletterWelcomeEmail from '../../emails/newsletter-welcome'; -import type { getPosts } from './source'; +import type { BlogPost } from './payload-posts'; -const resend = new Resend(process.env.RESEND_API_KEY as string); +const resend = new Resend(env.RESEND_API_KEY); export async function updateContact({ email, @@ -53,18 +54,21 @@ export async function sendWelcomeEmail({ firstName, to, }: { - posts: ReturnType; + posts: BlogPost[]; firstName: string; to: string; }) { - const EMAIL_FROM = process.env.EMAIL_FROM as string; - if (!EMAIL_FROM) throw new Error('Missing EMAIL_FROM environment variable'); + const EMAIL_FROM = env.EMAIL_FROM; if (!firstName || !to) throw new Error('Missing required email fields'); const formattedPosts = posts.map((post) => ({ - ...post.data, - image: `${baseUrl}${post.data.image}`, - url: `${baseUrl}${post.url}`, + title: post.title, + description: post.description, + date: post.date, + tags: post.tags, + image: post.image ? new URL(post.image, baseUrl).href : undefined, + author: post.author, + url: new URL(post.url, baseUrl).href, })); const { data: res, error } = await resend.emails.send({ -- cgit v1.2.3 From c8b9d38d55fdb48423e4fe663f9ccfb756ab603f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:31:54 +0000 Subject: fix: check NaN before page subtraction for clarity Agent-Logs-Url: https://github.com/bertyuan/next-blog/sessions/f86da32b-3af7-4393-8077-ce3435137221 Co-authored-by: bertyuan <189593334+bertyuan@users.noreply.github.com> --- src/app/(main)/(home)/posts/page.tsx | 10 ++++++---- src/app/(main)/(home)/tags/[...slug]/page.tsx | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx index daced35..376f5c1 100644 --- a/src/app/(main)/(home)/posts/page.tsx +++ b/src/app/(main)/(home)/posts/page.tsx @@ -55,16 +55,18 @@ export default async function Page(props: { }) { const searchParams = await props.searchParams; - const pageIndex = searchParams.page + const rawPage = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) ? (searchParams.page[0] ?? '') : searchParams.page, 10, - ) - 1 - : 0; + ) + : 1; + + if (Number.isNaN(rawPage)) notFound(); - if (Number.isNaN(pageIndex)) notFound(); + const pageIndex = rawPage - 1; // 获取文章(带分页) const { posts, totalDocs, totalPages } = await getPublishedPosts({ diff --git a/src/app/(main)/(home)/tags/[...slug]/page.tsx b/src/app/(main)/(home)/tags/[...slug]/page.tsx index 45fa52f..30c6464 100644 --- a/src/app/(main)/(home)/tags/[...slug]/page.tsx +++ b/src/app/(main)/(home)/tags/[...slug]/page.tsx @@ -89,16 +89,18 @@ export default async function Page(props: { const tag = params.slug[0]; if (!tag) return notFound(); - const pageIndex = searchParams.page + const rawPage = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) ? (searchParams.page[0] ?? '') : searchParams.page, 10, - ) - 1 - : 0; + ) + : 1; + + if (Number.isNaN(rawPage)) notFound(); - if (Number.isNaN(pageIndex)) notFound(); + const pageIndex = rawPage - 1; const { posts, totalDocs, totalPages } = await getPostsByTag(tag, { limit: postsPerPage, -- cgit v1.2.3 From 2e7d98420900e1d6749729ea2a94c334e7d65754 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 07:12:51 +0000 Subject: fix: guard generateMetadata against NaN and sub-1 page numbers Agent-Logs-Url: https://github.com/bertyuan/next-blog/sessions/a183ee36-4a5d-49b3-a43d-dba5985217dd Co-authored-by: bertyuan <189593334+bertyuan@users.noreply.github.com> --- src/app/(main)/(home)/posts/page.tsx | 12 +++++++----- src/app/(main)/(home)/tags/[...slug]/page.tsx | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx index 376f5c1..45016a4 100644 --- a/src/app/(main)/(home)/posts/page.tsx +++ b/src/app/(main)/(home)/posts/page.tsx @@ -128,7 +128,7 @@ export async function generateMetadata( ): Promise { const searchParams = await props.searchParams; - const pageIndex = searchParams.page + const rawPage = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) ? (searchParams.page[0] ?? '') @@ -137,13 +137,15 @@ export async function generateMetadata( ) : 1; - const isFirstPage = pageIndex === 1 || !searchParams.page; - const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${pageIndex}`; - const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${pageIndex}`; + if (Number.isNaN(rawPage) || rawPage < 1) notFound(); + + const isFirstPage = rawPage === 1 || !searchParams.page; + const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${rawPage}`; + const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${rawPage}`; return createMetadata({ title: pageTitle, - description: `Posts${!isFirstPage ? ` - Page ${pageIndex}` : ''}`, + description: `Posts${!isFirstPage ? ` - Page ${rawPage}` : ''}`, openGraph: { url: canonicalUrl, }, diff --git a/src/app/(main)/(home)/tags/[...slug]/page.tsx b/src/app/(main)/(home)/tags/[...slug]/page.tsx index 30c6464..b6bdafa 100644 --- a/src/app/(main)/(home)/tags/[...slug]/page.tsx +++ b/src/app/(main)/(home)/tags/[...slug]/page.tsx @@ -165,7 +165,7 @@ export async function generateMetadata( const searchParams = await props.searchParams; const tag = params.slug[0]; - const pageIndex = searchParams.page + const rawPage = searchParams.page ? Number.parseInt( Array.isArray(searchParams.page) ? (searchParams.page[0] ?? '') @@ -174,18 +174,20 @@ export async function generateMetadata( ) : 1; - const isFirstPage = pageIndex === 1 || !searchParams.page; + if (Number.isNaN(rawPage) || rawPage < 1) notFound(); + + const isFirstPage = rawPage === 1 || !searchParams.page; const pageTitle = isFirstPage ? `${tag} Posts` - : `${tag} Posts - Page ${pageIndex}`; + : `${tag} Posts - Page ${rawPage}`; const canonicalUrl = isFirstPage ? `/tags/${tag}` - : `/tags/${tag}?page=${pageIndex}`; + : `/tags/${tag}?page=${rawPage}`; return createMetadata({ title: pageTitle, description: `Posts tagged with ${tag}${ - !isFirstPage ? ` - Page ${pageIndex}` : '' + !isFirstPage ? ` - Page ${rawPage}` : '' }`, openGraph: { url: canonicalUrl, -- cgit v1.2.3