import type { Metadata } from "next"; import { notFound, redirect } from "next/navigation"; import { db } from "@/lib/db"; import { getSession } from "@/lib/auth"; import { Attachments } from "@/app/components/attachments"; import { Markdown } from "@/app/components/markdown"; import { canonicalTags } from "@/lib/tags"; import { postAuthorLabel } from "@/lib/display"; export const dynamic = "force-dynamic"; export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise { const { id: rawId } = await params; const post = db.prepare("SELECT p.content,p.hidden,p.visibility,p.origin,u.username,s.name,s.remote_user,s.base_url AS source_base_url FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.id=?").get(Number(rawId)) as { content: string; hidden: number; visibility: string; origin: string; username: string; name: string | null; remote_user: string | null; source_base_url: string | null } | undefined; if (!post || post.hidden || post.visibility !== "PUBLIC") return { title: "找不到貼文" }; const description = post.content.replace(/\s+/g, " ").slice(0, 160); const author = postAuthorLabel(post); return { title: `${author} 的貼文|Mebbling`, description, openGraph: { title: `${author}|Mebbling`, description, type: "article" } }; } export default async function PostPage({ params, searchParams }: { params: Promise<{ id: string }>; searchParams: Promise<{ reported?: string }> }) { const { id: rawId } = await params; const id = Number(rawId); const post = db.prepare("SELECT p.*,u.username,s.name,s.base_url AS source_base_url,s.remote_user,s.remote_display_name FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.id=?").get(id) as any; const query = await searchParams; if (!post || post.hidden) notFound(); const user = await getSession(); if (post.visibility !== "PUBLIC" && post.author_id !== user?.id) redirect("/"); if (user && post.visibility === "PUBLIC") db.prepare("INSERT INTO reading_history(user_id,post_id) VALUES(?,?) ON CONFLICT(user_id,post_id) DO UPDATE SET last_read_at=CURRENT_TIMESTAMP").run(user.id, id); const bookmark = user ? db.prepare("SELECT kind FROM bookmarks WHERE user_id=? AND post_id=?").get(user.id, id) as { kind: string } | undefined : undefined; const comments = db.prepare("SELECT c.*,u.username FROM comments c JOIN users u ON u.id=c.author_id WHERE c.post_id=? AND c.hidden=0 ORDER BY c.created_at").all(id) as any[]; const reactions = db.prepare("SELECT emoji,count(*) count FROM reactions WHERE post_id=? GROUP BY emoji").all(id) as any[]; let tags: string[] = []; try { tags = canonicalTags(JSON.parse(post.tags_json)); } catch {} const publishedAt = post.remote_created_at || post.created_at; const canExport = Boolean(user && (post.author_id === user.id || (post.source_id && db.prepare("SELECT 1 FROM source_members WHERE source_id=? AND user_id=?").get(post.source_id, user.id)))); return

{postAuthorLabel(post)} · {new Date(publishedAt).toLocaleString("zh-TW")}{post.remote_url && <> · 在 Memos 開啟}

{canExport &&

匯出 JSON匯出 Markdown

}
{reactions.map((reaction: any) => {reaction.emoji} {reaction.count})}{user && <>
}{user && ["👍", "❤️", "🎉", "🤔"].map((emoji) =>
)}

留言

{user ? <>