import { notFound } from 'next/navigation'; import Link from 'next/link'; import Header from '@/components/Header'; import Footer from '@/components/Footer'; import ReadingProgress from '@/components/ReadingProgress'; import ScrollToTop from '@/components/ScrollToTop'; import ShareButton from '@/components/ShareButton'; import ArticleCard from '@/components/ArticleCard'; import { getArticle, listArticles } from '@/lib/engine'; import { renderMarkdown, formatDate } from '@/lib/markdown'; import { Clock, ArrowLeft } from 'lucide-react'; export const dynamic = 'force-dynamic'; export async function generateMetadata({ params }) { const { slug } = await params; const article = await getArticle(slug); if (!article) return { title: 'Статья не найдена' }; return { title: article.seo_title || article.title, description: article.seo_descr || article.excerpt, openGraph: { title: article.title, description: article.excerpt, type: 'article', publishedTime: article.published_at, tags: article.tags || [], images: article.cover_url ? [{ url: article.cover_url }] : undefined, }, }; } async function loadRelated(article) { // ищем статьи с пересекающимися тегами, исключая текущую if (!article.tags?.length) return []; const all = await listArticles({ limit: 20 }); const scored = all .filter(a => a.id !== article.id) .map(a => ({ ...a, score: (a.tags || []).filter(t => article.tags.includes(t)).length, })) .filter(a => a.score > 0) .sort((a, b) => b.score - a.score) .slice(0, 3); return scored; } export default async function ArticlePage({ params }) { const { slug } = await params; const article = await getArticle(slug); if (!article) notFound(); const related = await loadRelated(article); const contentWithoutH1 = article.content.replace(/^#\s+.+$/m, '').trim(); const html = renderMarkdown(contentWithoutH1); return ( <>
Все статьи
{(article.tags || []).map(t => ( #{t} ))}

{article.title}

{article.author} · {formatDate(article.published_at)} {article.reading_time && ( <> · {article.reading_time} мин чтения )}
{article.cover_url && (
{article.title}
)}

Статья сгенерирована ИИ под редакторским присмотром.

{related.length > 0 && (

Похожее

{related.map(a => )}
)}