76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
import type { Metadata } from 'next';
|
|
import { notFound } from 'next/navigation';
|
|
import { articleUrl, getPublishedArticle } from '@/lib/articles';
|
|
|
|
const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || process.env.PUBLIC_SITE_URL || 'https://www.aiameiba.com').replace(/\/$/, '');
|
|
|
|
const formatDate = (value: string) => {
|
|
const date = new Date(value);
|
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleDateString('zh-CN');
|
|
};
|
|
|
|
const paragraphs = (body: string) => body.split(/\n\s*\n/).map(item => item.trim()).filter(Boolean);
|
|
|
|
export async function generateMetadata({ params }: { params: { identifier: string } }): Promise<Metadata> {
|
|
const article = await getPublishedArticle(params.identifier);
|
|
if (!article) return { title: '文章不存在' };
|
|
const localUrl = `${siteUrl}${articleUrl(article)}`;
|
|
const canonical = article.canonical_url || localUrl;
|
|
return {
|
|
title: article.title,
|
|
description: article.summary,
|
|
alternates: { canonical },
|
|
openGraph: {
|
|
type: 'article',
|
|
title: article.title,
|
|
description: article.summary,
|
|
url: canonical,
|
|
images: article.cover_image_url ? [{ url: article.cover_image_url }] : undefined,
|
|
publishedTime: article.published_at,
|
|
modifiedTime: article.updated_at,
|
|
authors: [article.author_name],
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function KnowledgeArticlePage({ params }: { params: { identifier: string } }) {
|
|
const article = await getPublishedArticle(params.identifier);
|
|
if (!article) notFound();
|
|
const localUrl = `${siteUrl}${articleUrl(article)}`;
|
|
const canonical = article.canonical_url || localUrl;
|
|
const jsonLd = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Article',
|
|
headline: article.title,
|
|
description: article.summary,
|
|
url: canonical,
|
|
mainEntityOfPage: localUrl,
|
|
author: { '@type': 'Organization', name: article.author_name },
|
|
publisher: { '@type': 'Organization', name: '阿美巴AI', url: siteUrl },
|
|
datePublished: article.published_at,
|
|
dateModified: article.updated_at,
|
|
image: article.cover_image_url || undefined,
|
|
articleSection: article.category,
|
|
};
|
|
|
|
return (
|
|
<div className="container-narrow">
|
|
<nav className="breadcrumb" aria-label="面包屑导航">
|
|
<ol><li><a href="/">首页</a></li><li><a href="/knowledge">知识中心</a></li><li><span aria-current="page">{article.title}</span></li></ol>
|
|
</nav>
|
|
<article className="knowledge-article">
|
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
|
<header className="knowledge-article-header">
|
|
<p className="article-meta"><span>{article.category}</span><span>{article.author_name}</span><time dateTime={article.published_at}>发布于 {formatDate(article.published_at)}</time><time dateTime={article.updated_at}>更新于 {formatDate(article.updated_at)}</time></p>
|
|
<h1>{article.title}</h1>
|
|
{article.summary && <p className="knowledge-article-summary">{article.summary}</p>}
|
|
</header>
|
|
<div className="knowledge-article-body">
|
|
{paragraphs(article.body_plain).map((paragraph, index) => <p key={index}>{paragraph}</p>)}
|
|
</div>
|
|
<footer className="knowledge-article-footer"><a href="/knowledge">← 返回知识中心</a></footer>
|
|
</article>
|
|
</div>
|
|
);
|
|
}
|