Move seed.sql out of migrations and add SEO structured data

- Relocate seed.sql to scripts/ so wrangler d1 migrations apply
  doesn't treat it as a migration; update package.json, setup.mjs,
  README, and all AGENTS.md references.
- Add src/lib/schema-org.ts builders (LocalBusiness, FAQPage,
  BlogPosting, BreadcrumbList) and emit JSON-LD via Base.astro.
- Add lastmod to sitemap entries.
- Set real site domain, worker route, and D1/KV ids.
- Improve image loading hints and heading semantics across site
  components; switch BlogIndex to client:idle.
This commit is contained in:
2026-09-13 16:21:59 +08:00
parent f6b885efe6
commit 6dedb5469f
28 changed files with 255 additions and 108 deletions
+22 -2
View File
@@ -8,6 +8,7 @@ interface Props {
ogType?: "website" | "article";
publishedTime?: Date | null;
image?: string;
jsonLd?: object | null | (object | null)[];
}
const {
@@ -17,6 +18,7 @@ const {
ogType = "website",
publishedTime = null,
image,
jsonLd = null,
} = Astro.props;
const siteName = SITE_NAME;
@@ -24,6 +26,11 @@ const origin = (Astro.site ?? Astro.url).toString().replace(/\/$/, "");
const canonical = new URL(Astro.url.pathname, origin + "/").toString();
const fullTitle = title.includes(siteName) ? title : `${title} — ${siteName}`;
const ogImage = image ? (image.startsWith("http") ? image : new URL(image, origin + "/").toString()) : undefined;
const jsonLdItems = (Array.isArray(jsonLd) ? jsonLd : [jsonLd]).filter((item): item is object => item != null);
const jsonLdHtml = jsonLdItems.length
? JSON.stringify(jsonLdItems.length === 1 ? jsonLdItems[0] : jsonLdItems).replace(/</g, "\\u003c")
: null;
---
<!doctype html>
@@ -38,16 +45,21 @@ const ogImage = image ? (image.startsWith("http") ? image : new URL(image, origi
<meta property="og:type" content={ogType} />
<meta property="og:site_name" content={siteName} />
<meta property="og:title" content={title} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:locale" content="zh_HK" />
{ogImage && <meta property="og:image" content={ogImage} />}
{ogImage && <meta property="og:image:alt" content={title} />}
{publishedTime && <meta property="article:published_time" content={publishedTime.toISOString()} />}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
{ogImage && <meta name="twitter:image" content={ogImage} />}
{ogImage && <meta name="twitter:image:alt" content={title} />}
<meta name="theme-color" content="#FAF8F4" />
{jsonLdHtml && <script type="application/ld+json" is:inline set:html={jsonLdHtml} />}
<link rel="sitemap" href="/sitemap.xml" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -55,7 +67,15 @@ const ogImage = image ? (image.startsWith("http") ? image : new URL(image, origi
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@400;500;700;900&family=Noto+Serif+HK:wght@600;700;900&display=swap"
rel="stylesheet"
media="print"
onload="this.media='all'"
/>
<noscript>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@400;500;700;900&family=Noto+Serif+HK:wght@600;700;900&display=swap"
rel="stylesheet"
/>
</noscript>
</head>
<body>
<slot />