Add SEO+GEO foundation + blog auto JSON-LD + robots fix

This commit is contained in:
Kunthawat Greethong
2026-06-30 22:16:47 +07:00
parent 479ed4722e
commit c004ee6504
6 changed files with 170 additions and 3 deletions

View File

@@ -14,12 +14,116 @@ export async function getStaticPaths() {
const { post } = Astro.props;
const { Content } = await render(post);
const siteUrl = new URL('/', Astro.site).toString();
// --- Date formatting ---
const formattedDate = new Intl.DateTimeFormat('th-TH', {
dateStyle: 'long',
}).format(post.data.pubDate);
// --- Image: og_image > heroImage > default logo ---
const ogImage = new URL(
post.data.og_image || post.data.heroImage || '/images/logos/logo-long-black.png',
Astro.site,
).toString();
// --- FAQPage auto-extraction ---
const rawBody = post.body || '';
const faqSectionRegex = /##\s*คำถามที่พบบ่อย\s*\n([\s\S]*?)(?=##\s|\Z)/;
const faqMatch = rawBody.match(faqSectionRegex);
const faqPairs: { question: string; answer: string }[] = faqMatch
? [...faqMatch[1].matchAll(/###\s+(.+?)\n\s*\n?(.+?)(?=\n###|\Z)/gs)]
.map(([, q, a]) => ({ question: q.trim(), answer: a.trim() }))
: [];
// --- Escaped body for JSON-LD safety ---
const safeBody = rawBody.replace(/<\//g, '<\\/');
// --- BlogPosting JSON-LD ---
const blogPostingJsonLd = JSON.stringify({
'@context': 'https://schema.org',
'@id': `${siteUrl}blog/${post.id}/#blogposting`,
'@type': 'BlogPosting',
headline: post.data.title,
description: post.data.description,
datePublished: post.data.pubDate.toISOString(),
dateModified: (post.data.updatedDate || post.data.pubDate).toISOString(),
...(post.data.author && {
author: { '@type': 'Person', name: post.data.author },
}),
...(post.data.reviewer && {
reviewedBy: { '@type': 'Person', name: post.data.reviewer },
}),
publisher: {
'@id': `${siteUrl}#organization`,
'@type': 'Organization',
name: 'MoreminiMore',
logo: {
'@type': 'ImageObject',
url: new URL('/images/logos/logo-long-black.png', Astro.site).toString(),
},
},
mainEntityOfPage: new URL(`/blog/${post.id}/`, Astro.site).toString(),
articleBody: safeBody,
});
// --- BreadcrumbList JSON-LD ---
const breadcrumbJsonLd = JSON.stringify({
'@context': 'https://schema.org',
'@id': `${siteUrl}blog/${post.id}/#breadcrumb`,
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'บทความ',
item: new URL('/blog/', Astro.site).toString(),
},
{
'@type': 'ListItem',
position: 2,
name: post.data.title,
},
],
});
// --- FAQPage JSON-LD ---
const faqPageJsonLd = faqPairs.length > 0
? JSON.stringify({
'@context': 'https://schema.org',
'@id': `${siteUrl}blog/${post.id}/#faq`,
'@type': 'FAQPage',
mainEntity: faqPairs.map(({ question, answer }) => ({
'@type': 'Question',
name: question,
acceptedAnswer: {
'@type': 'Answer',
text: answer,
},
})),
})
: '';
---
<PageShell title={`${post.data.title} | MoreminiMore`} description={post.data.description}>
<PageShell
title={`${post.data.title} | MoreminiMore`}
description={post.data.description}
image={ogImage}
>
<!-- JSON-LD: BreadcrumbList -->
<script type="application/ld+json" set:html={breadcrumbJsonLd}>
</script>
<!-- JSON-LD: BlogPosting -->
<script type="application/ld+json" set:html={blogPostingJsonLd}>
</script>
<!-- JSON-LD: FAQPage (auto — only if FAQ section found) -->
{faqPageJsonLd && (
<script type="application/ld+json" set:html={faqPageJsonLd}>
</script>
)}
<article class="blog-article">
<header class="blog-article-hero scene scene-light" data-scene="light">
<div class="blog-article-heading">