From c004ee6504ec25b52838a6cbca8d8d58ca8de831 Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Tue, 30 Jun 2026 22:16:47 +0700 Subject: [PATCH] Add SEO+GEO foundation + blog auto JSON-LD + robots fix --- public/robots.txt | 34 +++++++++++ src/components/PageShell.astro | 27 ++++++++- src/content.config.ts | 4 ++ src/pages/blog/[slug].astro | 106 ++++++++++++++++++++++++++++++++- src/pages/privacy.astro | 1 + src/pages/terms.astro | 1 + 6 files changed, 170 insertions(+), 3 deletions(-) diff --git a/public/robots.txt b/public/robots.txt index bd6c8e6..c41fcba 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -3,4 +3,38 @@ Allow: / Disallow: /google-apps-script/ Disallow: /_archive/ +# ── AI Crawlers (GEO — Generative Engine Optimization) ── +User-agent: GPTBot +Allow: / + +User-agent: ChatGPT-User +Allow: / + +User-agent: ClaudeBot +Allow: / + +User-agent: anthropic-ai +Allow: / + +User-agent: PerplexityBot +Allow: / + +User-agent: Perplexity-User +Allow: / + +User-agent: Google-Extended +Allow: / + +User-agent: Bytespider +Allow: / + +User-agent: Applebot-Extended +Allow: / + +User-agent: cohere-ai +Allow: / + +User-agent: meta-externalagent +Allow: / + Sitemap: https://moreminimore.com/sitemap.xml diff --git a/src/components/PageShell.astro b/src/components/PageShell.astro index 34e9b18..e0c5190 100644 --- a/src/components/PageShell.astro +++ b/src/components/PageShell.astro @@ -9,7 +9,25 @@ const { title = 'MoreminiMore', description = 'MoreminiMore ช่วย SME ดูข้อมูลจริงก่อนตัดสินใจทำเว็บ การตลาด AI หรือระบบอัตโนมัติ', image = '/images/logos/logo-long-black.png', + robotsMeta = 'index, follow', } = Astro.props; + +const siteUrl = new URL('/', Astro.site).toString(); +const logoUrl = new URL('/images/logos/logo-long-black.png', Astro.site).toString(); +const ogImageUrl = new URL(image, Astro.site).toString(); + +const organizationJsonLd = JSON.stringify({ + '@context': 'https://schema.org', + '@type': 'Organization', + name: 'MoreminiMore', + url: siteUrl, + logo: logoUrl, + description: description, + sameAs: [ + 'https://www.facebook.com/moreminimore', + 'https://www.linkedin.com/company/moreminimore', + ], +}); --- @@ -18,6 +36,7 @@ const { + {title} @@ -30,7 +49,7 @@ const { - + @@ -39,7 +58,11 @@ const { - + + + + diff --git a/src/content.config.ts b/src/content.config.ts index 634ad6d..0d7ad08 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -12,6 +12,10 @@ const blog = defineCollection({ category: z.string(), tags: z.array(z.string()).default([]), draft: z.boolean().default(false), + heroImage: z.string().optional(), + og_image: z.string().optional(), + author: z.string().optional(), + reviewer: z.string().optional(), }), }); diff --git a/src/pages/blog/[slug].astro b/src/pages/blog/[slug].astro index f3f1226..39c0d73 100644 --- a/src/pages/blog/[slug].astro +++ b/src/pages/blog/[slug].astro @@ -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, + }, + })), + }) + : ''; --- - + + + + + + + + + {faqPageJsonLd && ( + + )} +
diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro index 6c7df77..47a43b5 100644 --- a/src/pages/privacy.astro +++ b/src/pages/privacy.astro @@ -6,6 +6,7 @@ import LegalPageShell from '../components/LegalPageShell.astro';

นโยบาย

diff --git a/src/pages/terms.astro b/src/pages/terms.astro index e81fcce..33be5d1 100644 --- a/src/pages/terms.astro +++ b/src/pages/terms.astro @@ -6,6 +6,7 @@ import LegalPageShell from '../components/LegalPageShell.astro';

เงื่อนไข