60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import { Metadata } from 'next'
|
|
import { notFound } from 'next/navigation'
|
|
import { blogPosts } from '@/lib/blogPosts'
|
|
import { BlogPostTemplate } from '@/components/blog/BlogPostTemplate'
|
|
import { getHreflangAlternates } from '@/lib/localeRoutes'
|
|
|
|
interface Props {
|
|
params: Promise<{ slug: string }>
|
|
}
|
|
|
|
export async function generateStaticParams() {
|
|
return Object.keys(blogPosts).map((slug) => ({ slug }))
|
|
}
|
|
|
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
const { slug } = await params
|
|
const post = blogPosts[slug]
|
|
if (!post) return {}
|
|
|
|
const languages = getHreflangAlternates(post.canonical)
|
|
|
|
return {
|
|
title: post.metaTitle,
|
|
description: post.metaDescription,
|
|
// Selbstreferenzierender Canonical. Wichtig für die Syndication: Wenn der
|
|
// Beitrag auf Medium, DEV.to oder LinkedIn zweitveröffentlicht wird, muss
|
|
// dort ein rel=canonical auf genau diese URL zeigen.
|
|
alternates: {
|
|
canonical: post.canonical,
|
|
...(languages && { languages }),
|
|
},
|
|
openGraph: {
|
|
type: 'article',
|
|
title: post.metaTitle,
|
|
description: post.metaDescription,
|
|
url: post.canonical,
|
|
publishedTime: post.publishedAtIso,
|
|
modifiedTime: post.updatedAtIso || post.publishedAtIso,
|
|
authors: [post.author.name],
|
|
images: [{ url: post.heroImage, width: 1200, height: 630, alt: post.heroImageAlt }],
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: post.metaTitle,
|
|
description: post.metaDescription,
|
|
images: [post.heroImage],
|
|
},
|
|
}
|
|
}
|
|
|
|
export default async function BlogPostPage({ params }: Props) {
|
|
const { slug } = await params
|
|
const post = blogPosts[slug]
|
|
if (!post) {
|
|
notFound()
|
|
}
|
|
|
|
return <BlogPostTemplate post={post} />
|
|
}
|