import type { Metadata } from "next"; import { cookies } from "next/headers"; import { notFound } from "next/navigation"; import { Megaphone } from "lucide-react"; import { fetchSitePageBySlug, fetchOverview } from "@/lib/api"; import { authCookieHeader } from "@/lib/cookies"; import { getMeCached, getPublicSettingsCached } from "@/lib/serverData"; import { isAdminOrAbove } from "@/lib/roles"; import { extractToc, estimateReadingMinutes, markdownHeadingToc } from "@/lib/toc"; import MarkdownBody from "@/components/MarkdownBody"; import PostToc from "@/components/PostToc"; import { SiteDocShell, SiteDocIdentity, SiteDocPageNav, SiteDocAnnoNav, SiteDocMasthead, } from "@/components/site-doc"; import { requestCanonical } from "@/lib/canonicalMeta"; import { webPageJsonLd } from "@/lib/jsonLd"; import JsonLd from "@/components/JsonLd"; interface PageProps { params: Promise<{ slug: string }>; } export async function generateMetadata({ params }: PageProps): Promise { const { slug } = await params; const data = await fetchSitePageBySlug(slug).catch(() => null); if (!data) return { title: "页面不存在" }; const p = data.page; return { title: p.title, description: p.excerpt || p.title, openGraph: { title: p.title, description: p.excerpt || p.title, type: "article", publishedTime: p.created_at, }, ...(await requestCanonical(`/p/${p.slug}`)), }; } export default async function SitePagePublic({ params }: PageProps) { const { slug } = await params; const cookie = authCookieHeader(await cookies()); const [data, overview, settings, me] = await Promise.all([ fetchSitePageBySlug(slug).catch(() => null), fetchOverview().catch(() => null), getPublicSettingsCached(), getMeCached(cookie || undefined), ]); if (!data) notFound(); const p = data.page; const sidebarPages = overview?.sidebar_pages ?? []; const announcements = overview?.announcements ?? []; const canManagePages = !!me.user && isAdminOrAbove(me.user.role); const siteLead = settings.site_description?.trim() || undefined; const toc = extractToc(p.content); const headingToc = markdownHeadingToc(toc); const readingMinutes = estimateReadingMinutes(p.content, p.title.length); let headingIndex = 0; const headingRenderer = (Tag: "h2" | "h3") => function Heading({ children }: { children?: React.ReactNode }) { const item = headingToc[headingIndex++]; return ( {children} ); }; const jsonLd = webPageJsonLd({ name: p.title, description: p.excerpt || undefined, path: `/p/${p.slug}`, datePublished: p.created_at, dateModified: p.updated_at, }); const left = ( <> ); const right = toc.length > 0 ? ( ) : ( ); return ( <> {toc.length > 0 ? : null} {toc.length === 0 && announcements.length > 0 ? ( ) : null} } >
0 ? { h2: headingRenderer("h2"), h3: headingRenderer("h3") } : undefined } />
由站点管理团队维护
); }