Skip to content

Commit 3546bcc

Browse files
committed
Refactor blog and projects pages; enhance SEO metadata and structured data
- Updated blog post page to dynamically fetch post data and generate SEO metadata. - Added structured data (JSON-LD) for articles to improve search engine visibility. - Created a new ProjectsContent component to encapsulate project details and modal functionality. - Simplified projects page by removing inline project details and utilizing the new ProjectsContent component. - Enhanced sitemap generation to include dynamic blog post URLs and updated last modified dates.
1 parent 41e78a3 commit 3546bcc

4 files changed

Lines changed: 605 additions & 468 deletions

File tree

frontend/app/blog/[slug]/page.tsx

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import { Metadata } from "next";
22
import BlogPost from "@/components/BlogPost";
33

4-
// 动态生成 SEO 信息
5-
export async function generateMetadata({
6-
params,
7-
}: {
8-
params: Promise<{ slug: string }>;
9-
}): Promise<Metadata> {
10-
const { slug } = await params;
4+
interface Post {
5+
id: string;
6+
title: string;
7+
slug: string;
8+
content: string;
9+
excerpt: string | null;
10+
featured_image: string | null;
11+
published: boolean;
12+
author_id: string;
13+
created_at: string;
14+
updated_at: string;
15+
published_at: string;
16+
labels: string[];
17+
}
1118

19+
async function getPost(slug: string): Promise<Post | null> {
1220
try {
13-
const apiUrl = process.env.INTERNAL_API_BASE_URL ||
21+
const apiUrl =
22+
process.env.INTERNAL_API_BASE_URL ||
1423
process.env.NEXT_PUBLIC_API_BASE_URL ||
1524
"https://api.exquisitecore.xyz/api";
1625

@@ -19,24 +28,49 @@ export async function generateMetadata({
1928
});
2029

2130
if (response.ok) {
22-
const post = await response.json();
23-
return {
24-
title: post.title,
25-
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
26-
keywords: `${post.title},${post.labels?.join(",") || ""},ExquisiteCore,技术博客`,
27-
openGraph: {
28-
title: post.title,
29-
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
30-
type: "article",
31-
publishedTime: post.published_at,
32-
modifiedTime: post.updated_at,
33-
tags: post.labels || [],
34-
images: post.featured_image ? [post.featured_image] : [],
35-
},
36-
};
31+
return await response.json();
3732
}
3833
} catch (error) {
39-
console.error("获取文章元数据失败:", error);
34+
console.error("获取文章失败:", error);
35+
}
36+
return null;
37+
}
38+
39+
// 动态生成 SEO 信息
40+
export async function generateMetadata({
41+
params,
42+
}: {
43+
params: Promise<{ slug: string }>;
44+
}): Promise<Metadata> {
45+
const { slug } = await params;
46+
const post = await getPost(slug);
47+
48+
if (post) {
49+
return {
50+
title: post.title,
51+
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
52+
keywords: `${post.title},${post.labels?.join(",") || ""},ExquisiteCore,技术博客`,
53+
openGraph: {
54+
title: post.title,
55+
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
56+
type: "article",
57+
publishedTime: post.published_at,
58+
modifiedTime: post.updated_at,
59+
authors: ["ExquisiteCore"],
60+
tags: post.labels || [],
61+
images: post.featured_image ? [post.featured_image] : [],
62+
url: `https://blog.exquisitecore.xyz/blog/${post.id}`,
63+
},
64+
twitter: {
65+
card: "summary_large_image",
66+
title: post.title,
67+
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
68+
images: post.featured_image ? [post.featured_image] : [],
69+
},
70+
alternates: {
71+
canonical: `https://blog.exquisitecore.xyz/blog/${post.id}`,
72+
},
73+
};
4074
}
4175

4276
return {
@@ -45,12 +79,58 @@ export async function generateMetadata({
4579
};
4680
}
4781

82+
// 生成 Article JSON-LD 结构化数据
83+
function generateArticleJsonLd(post: Post) {
84+
return {
85+
"@context": "https://schema.org",
86+
"@type": "Article",
87+
headline: post.title,
88+
description: post.excerpt || `阅读ExquisiteCore的文章:${post.title}`,
89+
image: post.featured_image || "https://blog.exquisitecore.xyz/logo.svg",
90+
datePublished: post.published_at,
91+
dateModified: post.updated_at,
92+
author: {
93+
"@type": "Person",
94+
name: "ExquisiteCore",
95+
url: "https://blog.exquisitecore.xyz/about",
96+
},
97+
publisher: {
98+
"@type": "Organization",
99+
name: "ExquisiteCore Blog",
100+
logo: {
101+
"@type": "ImageObject",
102+
url: "https://blog.exquisitecore.xyz/logo.svg",
103+
},
104+
},
105+
mainEntityOfPage: {
106+
"@type": "WebPage",
107+
"@id": `https://blog.exquisitecore.xyz/blog/${post.id}`,
108+
},
109+
keywords: post.labels?.join(", ") || "",
110+
articleSection: "技术博客",
111+
inLanguage: "zh-CN",
112+
};
113+
}
114+
48115
export default async function BlogPostPage({
49116
params,
50117
}: {
51118
params: Promise<{ slug: string }>;
52119
}) {
53120
const { slug } = await params;
121+
const post = await getPost(slug);
54122

55-
return <BlogPost slug={slug} />;
123+
return (
124+
<>
125+
{post && (
126+
<script
127+
type="application/ld+json"
128+
dangerouslySetInnerHTML={{
129+
__html: JSON.stringify(generateArticleJsonLd(post)),
130+
}}
131+
/>
132+
)}
133+
<BlogPost slug={slug} />
134+
</>
135+
);
56136
}

0 commit comments

Comments
 (0)