Files
solo-company-feed/app/p/[slug]/page.tsx
爱喝水的木子 02a06ba888 change
2026-03-13 17:13:04 +08:00

68 lines
2.1 KiB
TypeScript

import { getDb } from "@/lib/mongo";
import { MarkdownViewer } from "@/components/MarkdownViewer";
import { notFound } from "next/navigation";
import { normalizeImageUrl } from "@/lib/normalize";
import { Post } from "@/types/post";
import { SharePanel } from "@/components/SharePanel";
import { getSiteUrl } from "@/lib/site";
export const dynamic = "force-dynamic";
type Props = {
params: { slug: string };
};
async function fetchPost(slug: string): Promise<Post | null> {
const db = await getDb();
const doc = await db.collection("posts").findOne({ slug });
if (!doc) return null;
// fire-and-forget view increment
db.collection("posts").updateOne({ slug }, { $inc: { views: 1 } }).catch(() => {});
return {
_id: doc._id?.toString(),
title: doc.title ?? "",
slug: doc.slug ?? slug,
markdown: doc.markdown ?? "",
cover: doc.cover,
tags: doc.tags ?? [],
author: doc.author ?? "admin",
createdAt: doc.createdAt ?? new Date().toISOString(),
updatedAt: doc.updatedAt ?? doc.createdAt ?? new Date().toISOString(),
views: doc.views ?? 0
};
}
export default async function PostPage({ params }: Props) {
const post = await fetchPost(params.slug);
if (!post) {
notFound();
}
const coverUrl = normalizeImageUrl(post.cover);
const shareUrl = `${getSiteUrl()}/p/${post.slug}`;
return (
<article className="rounded-2xl bg-white/80 p-6 shadow-sm ring-1 ring-slate-100">
<div className="mb-4">
<div className="flex flex-wrap items-center justify-between gap-3">
<h1 className="text-2xl font-semibold text-slate-900">{post.title}</h1>
<SharePanel url={shareUrl} />
</div>
<p className="mt-2 text-sm text-slate-500">
{new Date(post.createdAt).toLocaleString("zh-CN", { hour12: false })}
</p>
{coverUrl ? (
<img
src={coverUrl}
alt=""
className="mt-3 w-full rounded-2xl object-cover"
loading="lazy"
referrerPolicy="no-referrer"
/>
) : null}
</div>
<MarkdownViewer markdown={post.markdown} />
</article>
);
}