import React from "react"; import { notFound } from 'next/navigation'; import type { Metadata } from 'next'; import BlogPostClient from "../../../components/BlogPostClient"; import { getSeoConfig, formatPageTitle } from "../../../lib/seo"; import { safeJsonLd } from "../../../lib/schema"; function safeIso(val: any, fallback?: string): string | undefined { if (!val) return fallback; try { const d = new Date(val); return isNaN(d.getTime()) ? fallback : d.toISOString(); } catch { return fallback; } } function safeLocalDate(val: any): string { if (!val) return ''; try { const d = new Date(val); return isNaN(d.getTime()) ? '' : d.toLocaleDateString('fa-IR'); } catch { return ''; } } async function getBlog(rawSlug: string) { if (!rawSlug) return null; try { const slug = decodeURIComponent(rawSlug); const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api'; const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`; const res = await fetch(`${apiBase}/blogs/${encodeURIComponent(slug)}`, { next: { tags: ['blogs', `blog-${slug}`], revalidate: 3600, }, }); if (!res.ok) { console.warn(`[Blog] Failed to fetch blog ${slug}: ${res.status}`); return null; } const b = await res.json(); return b; } catch (error) { console.error('[Blog] Error fetching blog detail:', error); return null; } } export const revalidate = 3600; export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { const { slug } = await params; const blog = await getBlog(slug); const config = await getSeoConfig(); if (!blog) { return { title: formatPageTitle('مقاله یافت نشد', config.brandNameFa, config.titleSeparator, config.titlePosition), }; } const rawSiteUrl = process.env.NEXT_PUBLIC_SITE_URL || config.canonicalBaseUrl || 'https://canina.ir'; const siteUrl = rawSiteUrl.replace(/\/+$/, ''); const metaTitle = blog.metaTitle || blog.title || 'مقاله آموزشی و تخصصی کنینا'; const title = formatPageTitle(metaTitle, config.brandNameFa, config.titleSeparator, config.titlePosition, false); const description = blog.metaDescription || blog.excerpt || (typeof blog.content === 'string' ? blog.content.substring(0, 155).replace(/<[^>]+>/g, '') : '') || config.defaultMetaDescription; const canonicalUrl = blog.canonicalUrl ? blog.canonicalUrl.replace(/\/+$/, '') : `${siteUrl}/blog/${slug}`; const ogImageUrl = blog.ogImage || blog.imageUrl || config.ogImageUrl; const ogTitle = blog.ogTitle || title; const ogDesc = blog.ogDescription || description; const authorName = blog.author ? `${blog.author.firstName || ''} ${blog.author.lastName || ''}`.trim() || 'نویسنده کنینا' : 'نویسنده و پژوهشگر کنینا'; const publishedTime = safeIso(blog.publishedAt) || safeIso(blog.createdAt); const modifiedTime = safeIso(blog.updatedAt) || publishedTime; const keywordsList = blog.focusKeywords ? blog.focusKeywords.split(',').map((k: string) => k.trim()) : blog.keywords ? blog.keywords.split(',').map((k: string) => k.trim()) : undefined; return { title, description, keywords: keywordsList, alternates: { canonical: canonicalUrl, }, robots: { index: !blog.noIndex, follow: !blog.noFollow, googleBot: { index: !blog.noIndex, follow: !blog.noFollow, 'max-image-preview': 'large', 'max-snippet': -1, 'max-video-preview': -1, }, }, openGraph: { title: ogTitle, description: ogDesc, images: [ { url: ogImageUrl, width: 1200, height: 630, alt: blog.imageAlt || blog.title || title, }, ], url: canonicalUrl, siteName: config.brandNameFa, type: "article", publishedTime, modifiedTime, authors: [authorName], tags: blog.tags?.map((t: any) => typeof t === 'string' ? t : t?.name).filter(Boolean) || [], }, twitter: { card: "summary_large_image", title: ogTitle, description: ogDesc, images: [ogImageUrl], }, }; } export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; const blog = await getBlog(slug); const config = await getSeoConfig(); if (!blog) { notFound(); } const rawSiteUrl = process.env.NEXT_PUBLIC_SITE_URL || config.canonicalBaseUrl || 'https://canina.ir'; const siteUrl = rawSiteUrl.replace(/\/+$/, ''); const authorName = blog.author ? `${blog.author.firstName || ''} ${blog.author.lastName || ''}`.trim() || 'نویسنده کنینا' : 'نویسنده و پژوهشگر کنینا'; const publishedDate = safeLocalDate(blog.publishedAt) || safeLocalDate(blog.createdAt); const canonicalUrl = blog.canonicalUrl ? blog.canonicalUrl.replace(/\/+$/, '') : `${siteUrl}/blog/${slug}`; const publishedIso = safeIso(blog.publishedAt) || safeIso(blog.createdAt) || new Date().toISOString(); const modifiedIso = safeIso(blog.updatedAt) || publishedIso; const schemaType = blog.schemaType || 'BlogPosting'; const authorSchema = blog.author && (blog.author.firstName || blog.author.lastName) ? { '@type': 'Person', name: authorName, url: `${siteUrl}/about`, } : { '@type': 'Organization', name: config.brandNameFa || 'کنینا ایران', url: siteUrl, }; const articleJsonLd = { '@context': 'https://schema.org', '@type': schemaType, mainEntityOfPage: { '@type': 'WebPage', '@id': canonicalUrl, }, headline: blog.title, description: blog.excerpt || blog.metaDescription || blog.title, image: blog.imageUrl ? [blog.imageUrl.startsWith('http') ? blog.imageUrl : `${siteUrl}${blog.imageUrl.startsWith('/') ? '' : '/'}${blog.imageUrl}`] : [config.ogImageUrl.startsWith('http') ? config.ogImageUrl : `${siteUrl}${config.ogImageUrl.startsWith('/') ? '' : '/'}${config.ogImageUrl}`], datePublished: publishedIso, dateModified: modifiedIso, author: authorSchema, publisher: { '@type': 'Organization', name: config.brandNameFa || 'کنینا ایران', logo: { '@type': 'ImageObject', url: `${siteUrl}/assets/logo.png`, }, }, articleSection: blog.category?.name || 'دانشنامه سلامت حیوانات', keywords: blog.focusKeywords || (Array.isArray(blog.tags) ? blog.tags.map((t: any) => typeof t === 'string' ? t : t?.name).filter(Boolean).join(', ') : undefined), }; const breadcrumbJsonLd = { '@context': 'https://schema.org', '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, name: 'خانه', item: siteUrl, }, { '@type': 'ListItem', position: 2, name: 'مجله سلامت', item: `${siteUrl}/blog`, }, ...(blog.category ? [ { '@type': 'ListItem', position: 3, name: blog.category.name, item: `${siteUrl}/blog?categoryId=${blog.category.id}`, }, { '@type': 'ListItem', position: 4, name: blog.title, item: canonicalUrl, }, ] : [ { '@type': 'ListItem', position: 3, name: blog.title, item: canonicalUrl, }, ]), ], }; return ( <>