import { prisma } from "@/lib/prisma"; import { notFound } from "next/navigation"; import Link from "next/link"; import type { Metadata } from "next"; const ARCHETYPE_RU: Record = { architect: "Архитектор", gardener: "Садовник", librarian: "Библиотекарь", pragmatist: "Прагматик", }; export async function generateMetadata({ params }: { params: Promise<{ token: string }> }): Promise { const { token } = await params; return { title: "Моя карта знаний в Obsidian — Second Brain", description: "Я собрал статистику своего хранилища знаний. Сделай свою карточку.", openGraph: { title: "Моя карта знаний в Obsidian", description: "Statистика хранилища в Obsidian — Second Brain Wrapped.", images: [{ url: `/share/wrapped/${token}/opengraph-image`, width: 1080, height: 1920 }], }, twitter: { card: "summary_large_image" }, }; } // Публичная страница карточки (без сессии). Показывает только агрегаты — никаких имён заметок. export default async function SharePage({ params }: { params: Promise<{ token: string }> }) { const { token } = await params; const run = await prisma.wrappedRun.findUnique({ where: { shareToken: token } }); if (!run) notFound(); const metric = (n: number, l: string) => (
{n} {l}
); return (

Obsidian Wrapped · 2026

{run.archetype ? ARCHETYPE_RU[run.archetype] : "Карта знаний"}

зрелость {run.score}/100

{metric(run.noteCount, "заметок")} {metric(run.linkCount, "связей")} {metric(run.tagCount, "тегов")}
{run.topTag && (
Топ-тег: #{run.topTag}
)}
Сделать свою карточку → Скачать картинку (1080×1920)

🔒 Здесь только числа. Содержимое заметок остаётся на устройстве автора.

); }