Add Zod validation layer and AI blog generation

Introduce a schema-first validation layer and an AI blog generation
feature, plus one-command Cloudflare provisioning.

- src/schemas/ holds Zod input schemas for post, content, case,
  settings, AI, and keywords; parseForm() in src/lib/form.ts validates
  FormData and returns per-field errors.
- Migrate all admin POST handlers to parseForm, showing field-level
  errors and only redirecting once validation passes.
- Add blog_keywords table and posts.focus_keyword (migration 0002);
  uniqueSlug() centralised in src/data/content.ts.
- Add src/lib/ai.ts (OpenAI-compatible chat completions + optional
  Tavily research) and /admin/ai for AI settings, keyword queue, and
  draft generation.
- Add scripts/setup.mjs (npm run setup) to provision D1/KV, set
  secrets, and optionally migrate, seed, and deploy.
- Document AI secrets, Workers Builds deploy flow, and new schemas
  across README and AGENTS docs.
This commit is contained in:
2026-09-11 23:49:18 +08:00
parent 5b69bc818a
commit fdee1aabd7
41 changed files with 3240 additions and 296 deletions
+9 -5
View File
@@ -9,14 +9,18 @@ Astro 7 + React 19 + Chakra UI v3,部署在 Cloudflare Workers(單一 Worker
- `npm run preview` / `npm run deploy` — build 後用 wrangler dev / deploy
- DB(本機用 `:local`,雲端省略):`npm run db:generate``db:migrate:local` / `db:migrate``db:seed:local` / `db:seed`
- `npm run types` — 重新產生 `worker-configuration.d.ts``npm run secret` — 設定 `ADMIN_PASSWORD`
- `npm run setup` — 一鍵 Cloudflare provision(建 D1 / KV、寫 `wrangler.jsonc`、設 secret、可選套 migration + seed
- 沒有 test / lint / typecheck script(未安裝 @astrojs/check)。`npm run build` 是唯一可跑的驗證。
## 關鍵慣例與陷阱
- **SSR 頁面必須自己聲明**`astro.config.mjs``output: "static"`,所有要讀 D1 的動態頁面都靠檔案內 `export const prerender = false`(首頁、blog、admin、sitemap/robots)。新增需要 request-time 資料的頁面時一定要加。
- **SSR 頁面必須自己聲明**`astro.config.mjs``output: "static"`,所有要讀 D1 的動態頁面都靠檔案內 `export const prerender = false`(首頁、blog、admin、sitemap.xml)。新增需要 request-time 資料的頁面時一定要加。
- **環境變數只用 `getEnv()`**`src/lib/env.ts`,底層 `cloudflare:workers``env`)。Astro v6 起已移除 `Astro.locals.runtime.env``getEnv()` 只能在 `prerender = false` 的頁面/endpoint 用。
- **資料層**`getDb(getEnv().DB)` → Drizzle;查詢集中在 `src/data/content.ts`schema 在 `src/db/schema.ts`。改 schema 後跑 `npm run db:generate` 產生 migration。
- **Seed 與 migration 是分開的**README 講法有誤)`db:migrate:local` 只套 migrations`migrations/seed.sql` 要用 `db:seed:local` 另外執行;seed 可重複跑。
- **Seed 與 migration 是分開的**`db:migrate:local` / `db:migrate` 只套 migrations`migrations/seed.sql` 要用 `db:seed:local` / `db:seed` 另外執行;seed 可重複跑。
- **表單驗證集中在 `src/schemas/`**Zod)+ `src/lib/form.ts``parseForm(form, schema)`:admin 頁面唔好再手寫逐欄驗證。實體輸入型別用 `z.infer` 由 schemas 匯出。
- **AI Blog**`src/lib/ai.ts`OpenAI 相容 chat completions + Tavily)+ `/admin/ai`;同步生成、一律存草稿並標記關鍵字 `generated`。需要 secrets `AI_API_KEY`(必需)/`TAVILY_API_KEY`(可選);未設時回明確錯誤,唔會 throw。
- **部署用 Cloudflare Workers Builds**Git push 自動):build `npm run build`、deploy `npx wrangler deploy`。**Migration 唔會喺 CI 執行**,改 schema 要手動 `npm run db:migrate`(雲端)。
- **`wrangler.jsonc` 有佔位 id**`database_id` / KV `id` 要先用 `db:create` / `kv:create` 產生再貼上,未貼前無法部署。
- **上線前要改 `astro.config.mjs``site`**sitemap / canonical / OG 全部靠它(現為 `https://example.com`)。
- **後台認證**`src/middleware.ts` 保護所有 `/admin`,用 HMAC-signed cookie`src/lib/auth.ts`);未設 `ADMIN_PASSWORD`(本機在 `.dev.vars`,已 gitignore)會回 503。
@@ -110,12 +114,12 @@ When the user requests a durable behavior change, record it here or in the relev
| Path | Scope |
|---|---|
| `src/AGENTS.md` | 原始碼全層:架構、跨層慣例;擁有 `theme/``layouts/``config.ts``middleware.ts``env.d.ts`,以及 `pages/` 路由與 `/admin` 後台合約 |
| `src/AGENTS.md` | 原始碼全層:架構、跨層慣例;擁有 `theme/``layouts/``schemas/``config.ts``middleware.ts``env.d.ts`,以及 `pages/` 路由與 `/admin` 後台合約 |
| `src/components/site/AGENTS.md` | 前台 React island 與 Chakra UI 元件 |
| `src/data/AGENTS.md` | D1 讀取查詢層與後台設定欄位定義 |
| `src/db/AGENTS.md` | Drizzle schemamigration 的唯一來源) |
| `src/lib/AGENTS.md` | auth / env / db / markdown 基礎工具 |
| `src/lib/AGENTS.md` | auth / env / db / form / ai / markdown 基礎工具 |
| `migrations/AGENTS.md` | D1 migration 與 seed SQL |
| `docs/AGENTS.md` | 設計規格與實作計劃(`superpowers/` 為設計權威) |
Project-level files with no child doc are owned by this root: `astro.config.mjs``wrangler.jsonc``drizzle.config.ts``tsconfig.json``package.json``README.md``.dev.vars.example`
Project-level files with no child doc are owned by this root: `astro.config.mjs``wrangler.jsonc``drizzle.config.ts``tsconfig.json``package.json``README.md``.dev.vars.example``scripts/`