# Astro

> 使用 Kanorio 內容建構快速的 Astro 網站，支援靜態或伺服器渲染、發佈時重新建置、SEO 檔案與邊緣重新導向。

> Markdown version of https://kanorio.com/zh-Hant/developers/guides/astro. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp
>
> Translated from the English original: https://kanorio.com/en/developers/guides/astro.md

Astro 預設不載入 JavaScript，非常適合品牌網站與網誌。您可以在元件的 frontmatter 中使用 `fetch` 讀取 Kanorio 內容，透過靜態網站的建置時期讀取，或使用伺服器轉接器在每次請求時讀取。

## 1. 環境變數

```bash
# .env
PUBLIC_KANORIO_SITE_URL="https://s-xxxxxxxxxxxx.kanorio.com"
```

請使用來自 [網站設定 › 開發者](https://app.kanorio.com/settings/developer) 的永久系統網址。

## 2. 列出集合

```astro
---
// src/pages/projects.astro
const SITE_URL = import.meta.env.PUBLIC_KANORIO_SITE_URL;
const res = await fetch(`${SITE_URL}/api/v1/collections/projects/items?sort=-publishedAt&limit=20`);
const { data, assets } = await res.json();
---

<section>
  <h1>Projects</h1>
  {data.map((item) => {
    const cover = assets[item.fieldData.cover];
    return (
      <article>
        {cover && <img src={cover.url} alt={cover.alt ?? ""} width={cover.width} height={cover.height} loading="lazy" />}
        <h2>{item.fieldData.title}</h2>
        <p>{item.fieldData.summary}</p>
      </article>
    );
  })}
</section>
```

## 3. 網誌文章

對於靜態建置，`getStaticPaths` 會使用 `nextCursor` 遍歷網誌的每一頁：

```astro
---
// src/pages/blog/[slug].astro
const SITE_URL = import.meta.env.PUBLIC_KANORIO_SITE_URL;

export async function getStaticPaths() {
  const siteUrl = import.meta.env.PUBLIC_KANORIO_SITE_URL;
  const slugs = [];
  let cursor = null;
  do {
    const url = new URL(`${siteUrl}/api/v1/collections/blog/items`);
    url.searchParams.set("fields", "title");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const page = await (await fetch(url)).json();
    slugs.push(...page.data.map((post) => post.slug));
    cursor = page.hasMore ? page.nextCursor : null;
  } while (cursor);
  return slugs.map((slug) => ({ params: { slug } }));
}

const { slug } = Astro.params;
const res = await fetch(`${SITE_URL}/api/v1/collections/blog/items/${encodeURIComponent(slug)}?expand=authors,categories&formats=html`);
if (!res.ok) return Astro.redirect("/404");
const { item, assets } = await res.json();
const cover = item.fieldData.cover_image ? assets[item.fieldData.cover_image] : null;
---

<article>
  <h1>{item.fieldData.title}</h1>
  {cover && <img src={cover.url} alt={cover.alt ?? ""} />}
  {item.toc?.length > 0 && (
    <nav aria-label="Contents">
      <ul>{item.toc.map((h) => <li data-level={h.level}><a href={`#${h.id}`}>{h.label}</a></li>)}</ul>
    </nav>
  )}
  <div class="prose" set:html={item.contentHtml} />
</article>
```

## 4. 表單與更新

- **表單：** 使用 Elements 中的 `<kanorio-form>`，或是 [表單](https://kanorio.com/zh-Hant/developers/guides/forms) 中描述的挑戰與提交端點。將您的網域新增至允許的來源。
- **靜態建置：** 在您的主機上建立部署鉤子 (deploy hook)，並將其新增至 [網站設定 › Webhooks](https://app.kanorio.com/settings/webhooks) 下的 Webhook，訂閱集合與頁面事件。每次發佈都會觸發重新建置。
- **伺服器渲染：** 使用 `@astrojs/cloudflare` 或 `@astrojs/node`，在每次請求時讀取並透過 `X-Kanorio-Cache-Tags` 回應標頭進行快取；使用 `createRevalidateHandler` 建立的 Webhook 處理常式會清除 `data.cacheTags` 中的標籤（詞彙相同，請參閱 [Webhooks](https://kanorio.com/zh-Hant/developers/webhooks#refresh-your-front-ends-cache)）。

## 5. SEO 檔案

Astro 沒有代理重寫功能，且重新導向會將爬蟲程式導向至其他網域。請改為從端點提供檔案。`seoRewrites` 會回傳每個路徑的來源網址：

```ts
// src/lib/kanorio-seo.ts
import { seoRewrites } from "@kanorio/sdk";

const targets = seoRewrites({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL, framework: "astro" });

export async function proxySeoFile(path: string): Promise<Response> {
  const res = await fetch(targets[path]);
  return new Response(res.body, {
    status: res.status,
    headers: { "Content-Type": res.headers.get("Content-Type") ?? "text/plain; charset=utf-8" },
  });
}
```

```ts
// src/pages/robots.txt.ts, and the same for sitemap.xml, llms.txt, llms-full.txt and feed.xml
import { proxySeoFile } from "../lib/kanorio-seo";

export const GET = () => proxySeoFile("/robots.txt");
```

使用 `fetchDeliveryJsonLd` 將 JSON-LD 新增至每個頁面：

```astro
---
import { fetchDeliveryJsonLd } from "@kanorio/sdk";

const jsonLd = await fetchDeliveryJsonLd({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL, path: Astro.url.pathname });
---
{jsonLd && <script type="application/ld+json" set:html={jsonLd.scriptJson} />}
```

## 6. 重新導向與爬蟲規則

使用伺服器轉接器時，請在 middleware 中套用後台的重新導向與 AI 爬蟲規則，並提供 IndexNow 金鑰檔案：

```ts
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { createKanorioMiddleware } from "@kanorio/sdk/edge";

const kanorioEdge = createKanorioMiddleware({ siteUrl: import.meta.env.PUBLIC_KANORIO_SITE_URL });

export const onRequest = defineMiddleware(async (context, next) => {
  const ctx = context.locals.runtime?.ctx; // Cloudflare adapter only
  const response = await kanorioEdge.handleRequest(context.request, {
    waitUntil: ctx ? (promise) => ctx.waitUntil(promise) : undefined,
  });
  return response ?? next();
});
```

靜態建置無法執行 middleware。請在您的主機上設定重新導向，並將 `/{key}.txt` 轉發至 `https://{site}/api/v1/seo/indexnow-key.txt`。Middleware 僅匹配完整路徑；請將萬用字元重新導向新增至 `astro.config` 中的 `redirects`。

## 7. 分析

在您的版型中載入一次 Elements 指令碼，並新增分析元件：

```html
<script type="module" src="https://cdn.kanorio.com/elements/v1/kanorio-elements.js"></script>
<kanorio-analytics site-url="https://s-xxxxxxxxxxxx.kanorio.com"></kanorio-analytics>
```

頁面瀏覽量、參照連結網址、行銷活動參數以及來自 AI 助理的造訪將會出現在後台的分析中。
