2025-09-16 17:19:58 +08:00

154 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}
/>
);
}