新增 GA4 / Meta Pixel 追蹤與 Cookie 同意機制

- 追蹤 ID 存 site_settings(空字串即關閉),於 /admin/settings 管理
- Base.astro 僅在用戶同意後載入分析工具,未同意前零請求
- 新增 /privacy 私隱政策頁與「重設 Cookie 偏好」按鈕
- 前台 SSR 頁面(首頁、blog、about、privacy)傳入 gaId / metaPixelId
- seed 加入預設空值,Footer 加入私隱政策連結
- 同步更新 AGENTS.md 與 README 說明
This commit is contained in:
2026-09-13 20:01:57 +08:00
parent 6dedb5469f
commit fbc562f495
13 changed files with 434 additions and 14 deletions
+11 -3
View File
@@ -8,7 +8,7 @@
- 擁有 `src/` 全層,亦直接擁有以下冇獨立 child doc 的部分:
- `theme/system.ts` — Chakra systemtokens / semanticTokens / fonts / globalCss),前台顏色同字體的唯一來源。現行設計:editorial 極簡風(暖米白 `#FAF8F4` 底、琥珀金 `brand` accent、墨黑 `ink`、Noto Serif HK 標題),詳見 `docs/superpowers/specs/2026-09-11-editorial-redesign-design.md`
- `layouts/Base.astro` — 前台 SEO headcanonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback)。
- `layouts/Base.astro` — 前台 SEO headcanonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback+ 同意後先載入嘅 analytics(`gaId` / `metaPixelId` props,見下面 Analytics & consent
- `layouts/AdminLayout.astro` — 後台外框,純 CSS、與前台一致的色系。
- `config.ts` — build-time 靜態頁用的 `SITE_NAME` / `SITE_TAGLINE`(可管理內容一律放 D1,唔放呢度)。
- `middleware.ts` — 保護 `/admin`:設 `locals.isAdmin`,無 `ADMIN_PASSWORD` 回 503,未登入導向 `/admin/login`
@@ -28,8 +28,8 @@
### Routes & SSR`pages/`
- **預設靜態**`astro.config.mjs``output: "static"`。要讀 D1 或 request-time 資料的頁面/endpoint 必須在檔案內寫 `export const prerender = false`
- 目前 SSR`index.astro``blog/index.astro``blog/[slug].astro``admin/**``media/[...key].ts``sitemap.xml.ts`
- 保持靜態:`about.astro``robots.txt.ts`
- 目前 SSR`index.astro``about.astro``privacy.astro``blog/index.astro``blog/[slug].astro``admin/**``media/[...key].ts``sitemap.xml.ts`
- 保持靜態:`robots.txt.ts`
- **環境變數**:只用 `getEnv()`,且只可喺 `prerender = false` 的頁面/endpoint 用。
- **前台動態頁**:讀 D1 → 傳 props 畀單一 React island`client:idle`,內容仍 SSR 出 HTML),並設邊緣快取 `Cache-Control: public, s-maxage=60, stale-while-revalidate=300`
- **404**`blog/[slug].astro` 找唔到文章 → 設 `Astro.response.status = 404` 並 render noindex 頁。
@@ -38,6 +38,14 @@
- **AI 生成(同步)**`/admin/ai` 的生成喺 admin POST 內同步 `await` 完成先 redirect。如日後要改做背景處理,可用 `Astro.locals.cfContext`Cloudflare `ExecutionContext`)嘅 `waitUntil`,唔使改現有流程。
- **SEO**:用 `Base.astro`canonical / OG / sitemap 全部靠 `astro.config.mjs``site`(現為佔位 `https://example.com`,上線前要改)。所有頁面都要有單一 `h1`section 標題用 `h2`、項目用 `h3`,唔可以跳級(heading 語意一律用 Chakra `as`,唔可以淨靠字級)。JSON-LD 由 `lib/schema-org.ts` 砌,經 `Base.astro``jsonLd` prop 輸出:首頁 `LocalBusiness` + `FAQPage`、文章 `BlogPosting` + `BreadcrumbList`
### Analytics & consent
- **ID 放 D1**GA4 / Meta Pixel 的 ID 存 `site_settings``ga4_measurement_id` / `meta_pixel_id`admin `/admin/settings` 可改);**空字串=唔載入**,冇 `enabled` boolean。因為係通用 key-value**唔使 migration**,只需 `scripts/seed.sql` 有 default。
- **注入方式**`Base.astro` 收 optional props `gaId` / `metaPixelId`;只有 SSR 頁讀到 D1 再傳落去(靜態頁要傳就必須轉 `prerender = false`)。GA4 / Meta script 一律喺 **consent 之後**由 inline JS 動態 append,未同意前唔會有任何追蹤請求或 Cookie(亦**冇** Meta `<noscript>` pixel)。
- **Consent**:單一接受/拒絕,存 `localStorage` key `cookie_consent``granted` / `denied`);banner 同載入邏輯都喺 `Base.astro`(純 HTML/CSS + `is:inline`,唔用 React)。已同意者每次載入即追蹤。
- **`/privacy`**`pages/privacy.astro`SSR)記錄 Cookie / GA4 / Meta 用途,並提供「重設 Cookie 偏好」清除 `localStorage`
- 免維護原則:唔使抄參考專案嘅 singleton 表/APIReact 動態注入(本站係 MPA,每次載入=一次 pageview)。
### Admin`pages/admin/`
- 所有 `/admin` 路由**必須** `export const prerender = false`