summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBertrand Yuan <189593334+bertyuan@users.noreply.github.com>2026-04-27 15:51:27 +0800
committerGitHub <noreply@github.com>2026-04-27 15:51:27 +0800
commit658798b3a2378bb6df16cfbb16d707c6fb719e1e (patch)
tree63fc32f5f7fda4bbf718f490e3b1640311dbf994
parent8b9c0139a93c8b9d41068e5271d0fc6917d34fab (diff)
parent2e7d98420900e1d6749729ea2a94c334e7d65754 (diff)
Merge pull request #19 from bertyuan/copilot/fix-front-backend-connection
fix: patch rough frontend-backend connection issues
-rw-r--r--src/app/(main)/(home)/actions.ts5
-rw-r--r--src/app/(main)/(home)/posts/[slug]/page.client.tsx5
-rw-r--r--src/app/(main)/(home)/posts/page.tsx39
-rw-r--r--src/app/(main)/(home)/tags/[...slug]/page.tsx49
-rw-r--r--src/lib/resend.ts20
5 files changed, 77 insertions, 41 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 (
<Comments
page={slug}
@@ -49,7 +50,7 @@ export function PostComments({
auth={{
type: 'api',
signIn: () => {
- 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..45016a4 100644
--- a/src/app/(main)/(home)/posts/page.tsx
+++ b/src/app/(main)/(home)/posts/page.tsx
@@ -55,14 +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[0] ?? '')
: searchParams.page,
- 10
- ) - 1
- : 0;
+ 10,
+ )
+ : 1;
+
+ if (Number.isNaN(rawPage)) notFound();
+
+ const pageIndex = rawPage - 1;
// 获取文章(带分页)
const { posts, totalDocs, totalPages } = await getPublishedPosts({
@@ -106,7 +110,9 @@ export default async function Page(props: {
})}
</div>
</Section>
- {totalPages > 1 && <Pagination pageIndex={pageIndex} pageCount={totalPages} />}
+ {totalPages > 1 && (
+ <Pagination pageIndex={pageIndex} pageCount={totalPages} />
+ )}
</>
);
}
@@ -118,21 +124,28 @@ type Props = {
export async function generateMetadata(
props: Props,
- parent: ResolvingMetadata
+ parent: ResolvingMetadata,
): Promise<Metadata> {
const searchParams = await props.searchParams;
- const pageIndex = searchParams.page
- ? Number.parseInt(searchParams.page as string, 10)
+ const rawPage = searchParams.page
+ ? Number.parseInt(
+ Array.isArray(searchParams.page)
+ ? (searchParams.page[0] ?? '')
+ : searchParams.page,
+ 10,
+ )
: 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 71615cb..b6bdafa 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 = ({
</Section>
);
-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}`);
@@ -85,14 +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[0] ?? '')
: searchParams.page,
- 10
- ) - 1
- : 0;
+ 10,
+ )
+ : 1;
+
+ if (Number.isNaN(rawPage)) notFound();
+
+ const pageIndex = rawPage - 1;
const { posts, totalDocs, totalPages } = await getPostsByTag(tag, {
limit: postsPerPage,
@@ -106,7 +114,12 @@ export default async function Page(props: {
return (
<>
- <Header tag={tag} startIndex={startIndex} endIndex={endIndex} totalPosts={totalDocs} />
+ <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) => {
@@ -126,7 +139,9 @@ export default async function Page(props: {
})}
</div>
</Section>
- {totalPages > 1 && <Pagination pageIndex={pageIndex} tag={tag} totalPages={totalPages} />}
+ {totalPages > 1 && (
+ <Pagination pageIndex={pageIndex} tag={tag} totalPages={totalPages} />
+ )}
<TagJsonLd tag={tag} />
</>
);
@@ -150,27 +165,29 @@ 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] ?? ''
+ ? (searchParams.page[0] ?? '')
: searchParams.page,
- 10
+ 10,
)
: 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,
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<typeof getPosts>;
+ 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({