feat: add metadata generation for post detail page and improve description formatting

This commit is contained in:
암냥 2026-04-19 20:27:00 +09:00
commit be855563bb
No known key found for this signature in database
2 changed files with 82 additions and 0 deletions

View file

@ -1,6 +1,7 @@
import Link from "next/link";
import { cookies, headers } from "next/headers";
import { notFound } from "next/navigation";
import type { Metadata } from "next";
import Header from "@/components/header";
import DetailRawPanel from "@/components/detail-raw-panel";
@ -20,6 +21,20 @@ type Me = {
role: "admin" | "writer" | "reader";
};
function getSourceLabel(type: SourceType) {
return type === "twitter" ? "X" : type === "pixiv" ? "Pixiv" : "-";
}
function createDetailDescription(post: PostDetailResponse) {
const author = post.author?.trim() || "unknown";
const source = getSourceLabel(post.type);
const tags = (post.tags ?? []).filter((tag) => tag.trim().length > 0).slice(0, 5);
const tagText = tags.length > 0 ? ` | Tags: ${tags.join(", ")}` : "";
return `${author} | ${source} post${tagText}`;
}
async function getApiBaseUrl() {
const configuredBaseUrl = process.env.API_BASE_URL;
if (configuredBaseUrl) {
@ -78,6 +93,71 @@ async function fetchViewerRole(apiBaseUrl: string) {
}
}
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params;
const fallbackTitle = `Detail ${id} | Akiyama Mizuki`;
try {
const apiBaseUrl = await getApiBaseUrl();
const post = await fetchPostDetail(apiBaseUrl, id);
const detailUrl = `${apiBaseUrl}/detail/${id}`;
if (!post) {
return {
title: fallbackTitle,
description: "Post detail",
alternates: {
canonical: detailUrl,
},
robots: {
index: false,
follow: false,
},
};
}
const source = getSourceLabel(post.type);
const author = post.author?.trim() || "unknown";
const title = `${author} | ${source}`;
const description = createDetailDescription(post);
const ogImages = post.mediaUrl
? [
{
url: post.mediaUrl,
alt: `${author} ${source}`,
},
]
: undefined;
return {
title,
description,
alternates: {
canonical: detailUrl,
},
openGraph: {
type: "article",
title,
description,
url: detailUrl,
siteName: "Akiyama Mizuki",
images: ogImages,
},
twitter: {
card: post.mediaUrl ? "summary_large_image" : "summary",
title,
description,
images: post.mediaUrl ? [post.mediaUrl] : undefined,
},
};
} catch {
return {
title: fallbackTitle,
description: "Post detail",
};
}
}
export default async function DetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const apiBaseUrl = await getApiBaseUrl();