154 lines
5.2 KiB
TypeScript
154 lines
5.2 KiB
TypeScript
import type { Metadata } from 'next';
|
||
import { getSEOConfig } from '../../../../lib/seo-config';
|
||
import { Article } from '@/lib/types';
|
||
import NewsArticleServerComponent from '@/app/components/news/NewsArticleServerComponent';
|
||
import { notFound } from 'next/navigation';
|
||
|
||
// 服务端获取文章数据
|
||
async function getArticleData(slug: string, locale: string) {
|
||
try {
|
||
// 检查是否为静态导出模式
|
||
const isStaticMode = process.env.BUILD_MODE === 'static';
|
||
|
||
if (isStaticMode) {
|
||
// 静态模式下直接从文件系统读取
|
||
const { getStaticArticleBySlug, getStaticArticles } = await import('../../../../lib/static-data');
|
||
const article = getStaticArticleBySlug(slug, locale as 'zh-CN' | 'zh-TW' | 'en');
|
||
|
||
if (!article) {
|
||
return null;
|
||
}
|
||
|
||
// 获取相关文章
|
||
const allArticles = getStaticArticles(locale as 'zh-CN' | 'zh-TW' | 'en');
|
||
const relatedArticles = allArticles
|
||
.filter(a => a.slug !== slug && a.metadata.category === article.metadata.category)
|
||
.slice(0, 3);
|
||
|
||
return { article, relatedArticles };
|
||
} else {
|
||
// 开发模式下使用 API
|
||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
|
||
const response = await fetch(`${baseUrl}/api/articles/${slug}?locale=${locale}`, {
|
||
// 使用 ISR 缓存策略,1小时后重新验证
|
||
next: { revalidate: 3600 }
|
||
});
|
||
|
||
if (!response.ok) {
|
||
if (response.status === 404) {
|
||
return null;
|
||
}
|
||
throw new Error('Failed to fetch article');
|
||
}
|
||
|
||
const data = await response.json();
|
||
return data;
|
||
}
|
||
} catch (error) {
|
||
console.error('Error fetching article:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 生成静态参数
|
||
export async function generateStaticParams() {
|
||
const { getStaticArticles } = await import('../../../../lib/static-data');
|
||
const locales = ['zh-CN', 'zh-TW', 'en'];
|
||
const params = [];
|
||
|
||
for (const locale of locales) {
|
||
const articles = getStaticArticles(locale as 'zh-CN' | 'zh-TW' | 'en');
|
||
for (const article of articles) {
|
||
// 为每个locale生成对应的路由参数
|
||
if (locale === 'zh-CN') {
|
||
// 为简体中文生成两个路由:zh 和 zh-CN
|
||
params.push({
|
||
locale: 'zh',
|
||
id: article.slug,
|
||
});
|
||
params.push({
|
||
locale: 'zh-CN',
|
||
id: article.slug,
|
||
});
|
||
} else {
|
||
params.push({
|
||
locale: locale,
|
||
id: article.slug,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
return params;
|
||
}
|
||
|
||
// 生成动态元数据
|
||
export async function generateMetadata({
|
||
params: { locale, id },
|
||
}: {
|
||
params: { locale: string; id: string };
|
||
}): Promise<Metadata> {
|
||
const mappedLocale = locale === 'zh' ? 'zh-CN' : locale === 'zh-CN' ? 'zh-CN' : locale === 'zh-TW' ? 'zh-TW' : 'en';
|
||
const data = await getArticleData(id, mappedLocale);
|
||
|
||
if (!data?.article) {
|
||
return {
|
||
title: '文章未找到 - AwsLinker',
|
||
description: '您访问的文章不存在或已被移除。',
|
||
};
|
||
}
|
||
|
||
const article: Article = data.article;
|
||
const language = mappedLocale;
|
||
|
||
return {
|
||
title: `${article.metadata.title} - AwsLinker`,
|
||
description: article.metadata.description,
|
||
keywords: article.metadata.tags,
|
||
openGraph: {
|
||
title: article.metadata.title,
|
||
description: article.metadata.description,
|
||
type: 'article',
|
||
images: article.metadata.image ? [{ url: article.metadata.image }] : undefined,
|
||
publishedTime: article.metadata.date,
|
||
authors: article.metadata.author ? [article.metadata.author] : undefined,
|
||
},
|
||
twitter: {
|
||
card: 'summary_large_image',
|
||
title: article.metadata.title,
|
||
description: article.metadata.description,
|
||
images: article.metadata.image ? [article.metadata.image] : undefined,
|
||
},
|
||
alternates: {
|
||
canonical: locale === 'zh' ? `/news/${id}` : `/${locale}/news/${id}`,
|
||
languages: {
|
||
'zh-CN': `/news/${id}`,
|
||
'zh-TW': `/zh-TW/news/${id}`,
|
||
en: `/en/news/${id}`,
|
||
},
|
||
},
|
||
};
|
||
}
|
||
|
||
// 服务器组件 - 预渲染数据
|
||
export default async function NewsArticlePage({
|
||
params: { locale, id },
|
||
}: {
|
||
params: { locale: string; id: string };
|
||
}) {
|
||
const mappedLocale = locale === 'zh' ? 'zh-CN' : locale === 'zh-CN' ? 'zh-CN' : locale === 'zh-TW' ? 'zh-TW' : 'en';
|
||
const data = await getArticleData(id, mappedLocale);
|
||
|
||
if (!data?.article) {
|
||
notFound();
|
||
}
|
||
|
||
return (
|
||
<NewsArticleServerComponent
|
||
article={data.article}
|
||
relatedArticles={data.relatedArticles || []}
|
||
locale={mappedLocale}
|
||
/>
|
||
);
|
||
}
|