From 6dedb5469fc0bd77750e291feacd2483269bf810 Mon Sep 17 00:00:00 2001 From: philipcheung Date: Sun, 13 Sep 2026 16:21:59 +0800 Subject: [PATCH] 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. --- .astro/dev.json | 12 +-- AGENTS.md | 6 +- README.md | 5 +- astro.config.mjs | 23 +++-- .../plans/2026-09-11-structure-ai-deploy.md | 6 +- ...-09-12-ai-blog-template-deepseek-design.md | 2 +- migrations/AGENTS.md | 15 +-- package.json | 4 +- {migrations => scripts}/seed.sql | 0 scripts/setup.mjs | 2 +- src/AGENTS.md | 8 +- src/components/site/AGENTS.md | 2 + src/components/site/BlogIndex.tsx | 2 + src/components/site/BlogPost.tsx | 2 +- src/components/site/Cases.tsx | 4 + src/components/site/Faq.tsx | 20 ++-- src/components/site/Hero.tsx | 3 + src/components/site/Process.tsx | 6 +- src/data/AGENTS.md | 2 +- src/db/AGENTS.md | 4 +- src/layouts/Base.astro | 24 ++++- src/lib/AGENTS.md | 5 +- src/lib/schema-org.ts | 79 ++++++++++++++++ src/pages/blog/[slug].astro | 15 ++- src/pages/blog/index.astro | 2 +- src/pages/index.astro | 7 +- src/pages/sitemap.xml.ts | 12 ++- wrangler.jsonc | 91 ++++++++++--------- 28 files changed, 255 insertions(+), 108 deletions(-) rename {migrations => scripts}/seed.sql (100%) create mode 100644 src/lib/schema-org.ts diff --git a/.astro/dev.json b/.astro/dev.json index ea9cdba..3271b51 100644 --- a/.astro/dev.json +++ b/.astro/dev.json @@ -1,14 +1,14 @@ { - "pid": 98774, - "port": 4321, - "url": "http://localhost:4321", + "pid": 52048, + "port": 4322, + "url": "http://localhost:4322", "urls": { "local": [ - "http://localhost:4321/" + "http://localhost:4322/" ], "network": [], "networkInterfaceNames": [] }, - "background": true, - "startedAt": "2026-09-11T08:49:14.193Z" + "background": false, + "startedAt": "2026-09-13T08:12:40.425Z" } \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 2daf953..de88885 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ Astro 7 + React 19 + Chakra UI v3,部署在 Cloudflare Workers(單一 Worker - **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 是分開的**:`db:migrate:local` / `db:migrate` 只套 migrations,`migrations/seed.sql` 要用 `db:seed:local` / `db:seed` 另外執行;seed 可重複跑。 +- **Seed 與 migration 是分開的**:`db:migrate:local` / `db:migrate` 只套 migrations,`scripts/seed.sql` 要用 `db:seed:local` / `db:seed` 另外執行;seed 可重複跑。**`seed.sql` 必須放喺 `migrations/` 以外**,因為 `wrangler d1 migrations apply` 會將 `migrations_dir`(`wrangler.jsonc`,預設 `migrations`)內所有 `.sql` 當成 migration。 - **表單驗證集中在 `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`;**預設供應商 DeepSeek 官方(`https://api.deepseek.com`、`deepseek-flash`)**,提示詞通用唔綁行業(角色/公司資料由後台 `ai_context_prompt`/`ai_business_context` 提供)。同步生成、一律存草稿並標記關鍵字 `generated`。需要 secrets `AI_API_KEY`(必需)/`TAVILY_API_KEY`(可選);未設時回明確錯誤,唔會 throw。已部署 DB 轉供應商要喺 `/admin/ai` 改,唔好重跑 `db:seed`(會覆蓋後台設定)。 - **部署用 Cloudflare Workers Builds**(Git push 自動):build `npm run build`、deploy `npx wrangler deploy`。**Migration 唔會喺 CI 執行**,改 schema 要手動 `npm run db:migrate`(雲端)。 @@ -121,7 +121,7 @@ When the user requests a durable behavior change, record it here or in the relev | `src/data/AGENTS.md` | D1 讀取查詢層與後台設定欄位定義 | | `src/db/AGENTS.md` | Drizzle schema(migration 的唯一來源) | | `src/lib/AGENTS.md` | auth / env / db / form / media / ai / markdown 基礎工具 | -| `migrations/AGENTS.md` | D1 migration 與 seed SQL | +| `migrations/AGENTS.md` | D1 migration SQL(`migrations_dir` 內所有 `.sql` 都當 migration;seed 喺 `scripts/`) | | `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`、`scripts/`。 +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/`(含 `setup.mjs` 同 `seed.sql`)。 diff --git a/README.md b/README.md index d47fb57..f26b21f 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,9 @@ src/ ├─ lib/{auth,db,env,form,media,ai,markdown}.ts migrations/ ├─ 0000_init.sql … Drizzle migrations -└─ seed.sql 種子資料(用 db:seed 另外執行) -scripts/setup.mjs 一鍵 Cloudflare provision +scripts/ +├─ setup.mjs 一鍵 Cloudflare provision +└─ seed.sql 種子資料(用 db:seed 另外執行,唔可以放喺 migrations/) ``` ## 待客戶提供(可喺 `/admin/settings` 自行更新) diff --git a/astro.config.mjs b/astro.config.mjs index ff81de0..7bc1693 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -3,18 +3,17 @@ import cloudflare from "@astrojs/cloudflare"; import react from "@astrojs/react"; export default defineConfig({ + integrations: [react()], + // ⚠️ 上線前改咗呢個做你嘅真域名(sitemap、canonical、OG 全部靠佢) + site: "https://solar.develop-cat.com", - integrations: [react()], - // ⚠️ 上線前改咗呢個做你嘅真域名(sitemap、canonical、OG 全部靠佢) - site: "https://example.com", + output: "static", - output: "static", - - adapter: cloudflare({ - platformProxy: { - enabled: true, - // 本機 astro dev 時讀 wrangler.jsonc 嘅 bindings - configPath: "./wrangler.jsonc", - }, - }), + adapter: cloudflare({ + platformProxy: { + enabled: true, + // 本機 astro dev 時讀 wrangler.jsonc 嘅 bindings + configPath: "./wrangler.jsonc", + }, + }), }); diff --git a/docs/superpowers/plans/2026-09-11-structure-ai-deploy.md b/docs/superpowers/plans/2026-09-11-structure-ai-deploy.md index 0a8e4ae..7d17d28 100644 --- a/docs/superpowers/plans/2026-09-11-structure-ai-deploy.md +++ b/docs/superpowers/plans/2026-09-11-structure-ai-deploy.md @@ -45,7 +45,7 @@ src/pages/admin/content/[kind].astro 用 parseForm src/pages/admin/cases.astro 用 parseForm src/pages/admin/settings.astro 用 parseForm src/pages/admin/index.astro 加入口連結 -migrations/seed.sql 加 AI 設定 + 示範關鍵字 +scripts/seed.sql 加 AI 設定 + 示範關鍵字 .dev.vars.example 加 AI_API_KEY / TAVILY_API_KEY README.md / AGENTS.md 各層 同步文件 ``` @@ -620,7 +620,7 @@ Expected: PASS。`npm run dev` → 提交一個空標題,見到紅色錯誤字 **Files:** - Modify: `src/db/schema.ts` -- Modify: `migrations/seed.sql` +- Modify: `scripts/seed.sql` - Generate: `migrations/0002_*.sql`(由 drizzle-kit) - [x] **Step 1: `posts` 加 `focusKeyword`** @@ -1399,7 +1399,7 @@ async function main() { const migrate = await rl.question("而家套用雲端 migration + seed?(y/N):"); if (migrate.trim().toLowerCase() === "y") { execSync(`npx wrangler d1 migrations apply ${DB_NAME} --remote`, { stdio: "inherit" }); - execSync(`npx wrangler d1 execute ${DB_NAME} --remote --file=./migrations/seed.sql`, { stdio: "inherit" }); + execSync(`npx wrangler d1 execute ${DB_NAME} --remote --file=./scripts/seed.sql`, { stdio: "inherit" }); } console.log("\n提示:上線前記得改 astro.config.mjs 嘅 site 做真域名。"); diff --git a/docs/superpowers/specs/2026-09-12-ai-blog-template-deepseek-design.md b/docs/superpowers/specs/2026-09-12-ai-blog-template-deepseek-design.md index d66eceb..2592e1f 100644 --- a/docs/superpowers/specs/2026-09-12-ai-blog-template-deepseek-design.md +++ b/docs/superpowers/specs/2026-09-12-ai-blog-template-deepseek-design.md @@ -97,7 +97,7 @@ DeepSeek 思考模式**預設開啟**(effort `high`),會令:`temperature |---|---| | `src/lib/ai.ts` | 通用化 `buildSystemPrompt`;預設 baseUrl/model 改 DeepSeek;`callChat` 加 conditional thinking disabled、`max_tokens: 4000` | | `src/schemas/ai.ts` | `ai_base_url` / `ai_chat_model` 預設值同步 | -| `migrations/seed.sql` | `ai_base_url` / `ai_chat_model` 改預設;`ai_business_context` 改通用格式範本 | +| `scripts/seed.sql` | `ai_base_url` / `ai_chat_model` 改預設;`ai_business_context` 改通用格式範本 | | `src/pages/admin/ai.astro` | 模型/URL 預設值同步;公司背景+寫作風格加 placeholder/格式說明 | | `src/lib/AGENTS.md`、根 `AGENTS.md` | 更新 AI Blog 合約描述(DeepSeek 預設、通用提示詞) | diff --git a/migrations/AGENTS.md b/migrations/AGENTS.md index a5b8af9..0e38302 100644 --- a/migrations/AGENTS.md +++ b/migrations/AGENTS.md @@ -2,28 +2,29 @@ ## Purpose -Cloudflare D1 的 schema migration 同種子資料。 +Cloudflare D1 的 schema migration SQL。 ## Ownership -- 擁有 `migrations/` 全部 SQL、`migrations/meta/` 同 `seed.sql`。 +- 擁有 `migrations/` 全部 SQL 同 `migrations/meta/`(產生檔)。 - Drizzle schema 定義喺 `src/db/schema.ts`(見 `src/db/AGENTS.md`)。 +- 種子資料 `seed.sql` 喺 `scripts/`(由 root AGENTS.md 擁有)。 ## Local Contracts - **Migration 由工具產生**:`0000_*.sql`、`0001_*.sql`、`0002_*.sql` 由 `npm run db:generate`(drizzle-kit)依 `schema.ts` 產生;`migrations/meta/` 係產生檔。**唔好手改**呢啲檔或 meta。`0002_*.sql` 加咗 `posts.focus_keyword` 同 `blog_keywords` 表。 -- **`seed.sql` 係手寫、獨立於 migration**:全部用 `INSERT OR REPLACE`,可重複執行。`db:migrate` **唔會**執行 seed。 +- **`migrations/` 內所有 `.sql` 都會被當成 migration**:`wrangler d1 migrations apply` 會套用 `migrations_dir`(`wrangler.jsonc`,現為 `migrations`)內全部 `.sql`。所以**種子資料一定要放喺 `migrations/` 以外**(現時 `scripts/seed.sql`),否則 `db:migrate` 會連 seed 一齊種。 - **Migration 唔會喺 CI 執行**:Cloudflare Workers Builds 只跑 build + deploy;改 schema 後要手動 `npm run db:migrate`(雲端)。 - **指令**(本機用 `:local`,雲端省略): - `npm run db:generate` — 由 schema 產生 migration - `npm run db:migrate:local` / `npm run db:migrate` — 套用 migration - - `npm run db:seed:local` / `npm run db:seed` — 匯入種子 -- **Seed 內容**:公司資料(`site_settings`)、首頁內容(`content_items`:service/feature/step/faq)、`cases`、一篇示範 `posts`、三個示範 `blog_keywords`(`pending`);另有 7 個 `ai_*` AI 設定 keys(獨立 seed block)。 -- DB 名稱 `yingfung-solar-db`,binding 喺 `wrangler.jsonc`(`database_id` 仍係佔位,部署前要填)。 + - `npm run db:seed:local` / `npm run db:seed` — 匯入種子(`scripts/seed.sql`) +- **Seed 內容**(`scripts/seed.sql`,全部 `INSERT OR REPLACE`、可重複跑):公司資料(`site_settings`)、首頁內容(`content_items`:service/feature/step/faq)、`cases`、一篇示範 `posts`、三個示範 `blog_keywords`(`pending`);另有 7 個 `ai_*` AI 設定 keys(獨立 seed block)。 +- DB 名稱 `yingfung-solar-db`,binding 喺 `wrangler.jsonc`(`database_id` / KV `id` 由 `npm run setup` 或 `db:create` / `kv:create` 寫入)。 ## Work Guidance -- 改 schema → 先 `db:generate`,再 `db:migrate:local`;改 seed → 直接 `db:seed:local`。 +- 改 schema → 先 `db:generate`,再 `db:migrate:local`;改 seed → 直接改 `scripts/seed.sql` 再 `db:seed:local`。 - 新增 setting key 時同步更新 `src/data/settings-fields.ts`。 ## Verification diff --git a/package.json b/package.json index f0fa4b3..1131682 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "db:generate": "drizzle-kit generate", "db:migrate": "wrangler d1 migrations apply yingfung-solar-db --remote", "db:migrate:local": "wrangler d1 migrations apply yingfung-solar-db --local", - "db:seed": "wrangler d1 execute yingfung-solar-db --remote --file=./migrations/seed.sql", - "db:seed:local": "wrangler d1 execute yingfung-solar-db --local --file=./migrations/seed.sql", + "db:seed": "wrangler d1 execute yingfung-solar-db --remote --file=./scripts/seed.sql", + "db:seed:local": "wrangler d1 execute yingfung-solar-db --local --file=./scripts/seed.sql", "db:studio": "drizzle-kit studio", "kv:create": "wrangler kv namespace create CACHE", "r2:create": "wrangler r2 bucket create yingfung-solar-media", diff --git a/migrations/seed.sql b/scripts/seed.sql similarity index 100% rename from migrations/seed.sql rename to scripts/seed.sql diff --git a/scripts/setup.mjs b/scripts/setup.mjs index 59cd8f0..3c464c4 100644 --- a/scripts/setup.mjs +++ b/scripts/setup.mjs @@ -13,7 +13,7 @@ const D1_PLACEHOLDER = "PASTE_D1_DATABASE_ID_HERE"; const KV_PLACEHOLDER = "PASTE_KV_NAMESPACE_ID_HERE"; const R2_BUCKET = "yingfung-solar-media"; const WRANGLER = join(ROOT, "wrangler.jsonc"); -const SEED_FILE = join(ROOT, "migrations", "seed.sql"); +const SEED_FILE = join(ROOT, "scripts", "seed.sql"); const ASTRO_CONFIG = join(ROOT, "astro.config.mjs"); const DB_NAME = "yingfung-solar-db"; diff --git a/src/AGENTS.md b/src/AGENTS.md index 7ad4571..5b643fc 100644 --- a/src/AGENTS.md +++ b/src/AGENTS.md @@ -8,7 +8,7 @@ - 擁有 `src/` 全層,亦直接擁有以下冇獨立 child doc 的部分: - `theme/system.ts` — Chakra system(tokens / 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 head(canonical / OG / Twitter / sitemap link)+ Noto Sans HK。 + - `layouts/Base.astro` — 前台 SEO head(canonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `