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
+42 -18
View File
@@ -9,6 +9,7 @@
- **一頁式官網** `/`(SSR,讀 D1):Hero、服務、特色、流程、完成案例、FAQ、聯絡 CTA
- **Blog** `/blog``/blog/[slug]`:封面卡片列表 + Markdown 文章頁
- **後台** `/admin`(密碼登入):管理文章、首頁內容(服務/特色/流程/FAQ)、完成案例、網站設定(公司資料/聯絡/Hero/SEO)
- **AI 生成 Blog** `/admin/ai`:設定 AI 供應商、維護關鍵字佇列,一按生成完整草稿(DeepSeek 相容 API,可選 Tavily 上網研究)
- **SEO**canonical、OG/Twitter card、`/sitemap.xml``/robots.txt`、結構化語意 HTML
- 首頁/Blog 用邊緣快取(`s-maxage`),後台改完約 1 分鐘內生效
@@ -17,7 +18,7 @@
| 層 | 選用 |
|---|---|
| 前端框架 | Astro 7`output: "static"` + per-page SSR |
| UI | React 19 + `@chakra-ui/react` v3 + `@emotion/react`清新天藍/綠色系 theme |
| UI | React 19 + `@chakra-ui/react` v3 + `@emotion/react`editorial 極簡 theme:暖米白底、琥珀金 accent |
| Adapter | `@astrojs/cloudflare`(一個 Worker |
| DB | Cloudflare D1SQLite |
| ORM | Drizzle |
@@ -32,45 +33,66 @@
```bash
npm install
# 建本機 D1 + 套 migrationmigration 會連 seed.sql 一齊套)
# 建本機 D1 + 套 migration
npm run db:migrate:local
# 重新匯入種子資料(可重複執行)
# 匯入種子資料(同 migration 分開,可重複執行)
npm run db:seed:local
# 開發 server:4321
npx astro dev
npm run dev
```
本機後台密碼喺 `.dev.vars``ADMIN_PASSWORD=changeme-local`,已 gitignore)。
想模擬 productionwrangler):`npx wrangler dev`
想模擬 productionwrangler):`npm run preview`
## 部署到 Cloudflare
### 首次設定(一鍵)
```bash
npm run login # 瀏覽器授權
npm run login # 瀏覽器授權
npm run setup # 建立 D1 / KV、寫入 wrangler.jsonc、設 secret、套 migration + seed
```
npm run db:create # 印 database_id → 填入 wrangler.jsonc
npm run kv:create # 印 namespace id → 填入 wrangler.jsonc
`npm run setup` 會逐步問你:要唔要設定後台密碼、要唔要套 migration/seed、要唔要 build + deploy。若 `astro.config.mjs``site` 仍然係 `https://example.com`,會跳過 build + deploy 並提醒你改。
npm run db:migrate # 雲端套 migration
npm run db:seed # 雲端匯入種子(公司資料 + 樣本內容)
### 之後更新
npx wrangler secret put ADMIN_PASSWORD # 設定後台密碼
改完 `astro.config.mjs``site` 做真域名後:
# 改 astro.config.mjs 嘅 site = 真網域
```bash
npm run deploy
```
之後更新:`npm run deploy`
### Git push 自動部署(Cloudflare Workers Builds
1. Cloudflare Dashboard → Workers & Pages → 連接 GitHub repo
2. Build command`npm run build`
3. Deploy command`npx wrangler deploy`
4. 喺 Dashboard 設 secrets`ADMIN_PASSWORD``AI_API_KEY``TAVILY_API_KEY`
**Migration 唔會喺 CI 自動執行**:改完 `src/db/schema.ts` 要手動跑
`npm run db:generate``npm run db:migrate`(雲端)→ `npm run db:seed`(如有新 seed)。
## AI 生成 Blog
後台 `/admin/ai`
- 設定 AI 供應商(OpenAI 相容 Base URL + 模型)、寫作風格、公司背景
- 維護關鍵字佇列(新增 / 略過 / 刪除)
- 「生成草稿」= 自由輸入主題;「生成下一篇」= 攞佇列下一個 pending
- 生成係同步進行(約 10–40 秒),完成後一律存為**草稿**並跳去文章編輯頁覆核
- 需要 secret`AI_API_KEY`(必需)、`TAVILY_API_KEY`(如開上網研究)
## 後台點用
1.`/admin`,輸入密碼登入
2. **內容總覽** → 服務 / 特色 / 流程 / 常見問題 / 完成案例 / 網站設定
2. **內容總覽** → 服務 / 特色 / 流程 / 常見問題 / 完成案例 / 網站設定 / AI 生成
3. 每個項目可新增、修改、刪除、用 ↑↓ 排序、「已發布 / 草稿」控制前台顯示
4. 圖片:直接貼圖片 URL(暫時;之後可換成上傳)
5. **AI 生成**:設定供應商同關鍵字佇列,生成草稿後去文章編輯頁覆核發布
## 結構
@@ -81,18 +103,20 @@ src/
├─ data/
│ ├─ content.ts D1 讀取 + WhatsApp/電話 link helper
│ └─ settings-fields.ts 後台設定欄位定義
├─ db/schema.ts Drizzle schemaposts / site_settings / content_items / cases
├─ db/schema.ts Drizzle schemaposts / site_settings / content_items / cases / blog_keywords
├─ schemas/ Zod 驗證 schema(表單輸入 + AI 輸出)
├─ layouts/Base.astro SEO head + fonts
├─ layouts/AdminLayout.astro 後台外框
├─ pages/
│ ├─ index.astro SSR 首頁
│ ├─ blog/… Blog
│ ├─ admin/… 後台
│ ├─ admin/… 後台(含 admin/ai
│ └─ sitemap.xml.ts / robots.txt.ts
─ lib/{auth,db,env,markdown}.ts
─ lib/{auth,db,env,form,ai,markdown}.ts
migrations/
├─ 0000_init.sql … Drizzle migrations
└─ seed.sql 種子資料(與 migration 一齊執行)
└─ seed.sql 種子資料(用 db:seed 另外執行)
scripts/setup.mjs 一鍵 Cloudflare provision
```
## 待客戶提供(可喺 `/admin/settings` 自行更新)