毛哥EM 從國二經營至今的部落格。分享技術、經驗、專案、與生活。
關於目前 2026 新架構可參考文章 - 毛哥EM資訊密技 2026:重造了個毛茸茸的大輪子!
這個 repo 使用 pnpm workspace:
.
├── apps/
│ ├── blog/ # Astro 部落格
│ └── comments-worker/ # Cloudflare Worker + D1 留言 API
├── packages/
│ └── comments-shared/ # Gravatar、sanitize、spam heuristic 等共用工具
├── package.json
├── pnpm-workspace.yaml
└── tsconfig.base.jsonpnpm install
pnpm dev
pnpm --filter blog dev
pnpm --filter comments-worker devpnpm dev 會啟動 blog。Worker 請另外開一個 terminal 跑 pnpm --filter comments-worker dev。
常用檢查:
pnpm build
pnpm typecheck
pnpm test
pnpm --filter blog build
pnpm --filter comments-worker typecheck
pnpm --filter comments-worker test先複製本機 env 範例:
cp apps/comments-worker/.dev.vars.example apps/comments-worker/.dev.varsapps/comments-worker/.dev.vars 必須填入:
GITHUB_CLIENT_ID
GITHUB_CLIENT_SECRET
GITHUB_REDIRECT_URI
SESSION_SECRET
IP_HASH_SECRET
TURNSTILE_SECRET_KEY
ALLOWED_ORIGINS
COMMENT_DEFAULT_STATUS_ANON
COMMENT_DEFAULT_STATUS_GITHUB本機開發若暫時不使用 Turnstile,可把 TURNSTILE_SECRET_KEY 設為 dev-disabled。正式環境請用 Cloudflare secret,不要 commit 真實值。
建立 D1:
pnpm --filter comments-worker exec wrangler d1 create emtech-comments把輸出的 database_id 填到 apps/comments-worker/wrangler.toml:
[[d1_databases]]
binding = "COMMENTS_DB"
database_name = "emtech-comments"
database_id = "your-database-id"執行 migrations:
pnpm --filter comments-worker db:migrate:local
pnpm --filter comments-worker db:migrate:remote在 Cloudflare Turnstile 建立 widget,網域加入正式部落格網域與本機需要的測試網域。設定:
pnpm --filter comments-worker exec wrangler secret put TURNSTILE_SECRET_KEYAstro 前端需要 site key。部署 blog 時設定:
TURNSTILE_SITE_KEY=your-site-key
COMMENT_API_BASE_URL=https://your-comments-worker.example.com到 GitHub Developer Settings 建立 OAuth App:
Homepage URL: https://your-blog.example.com
Authorization callback URL: https://your-comments-worker.example.com/api/auth/github/callback設定 Worker secrets:
pnpm --filter comments-worker exec wrangler secret put GITHUB_CLIENT_ID
pnpm --filter comments-worker exec wrangler secret put GITHUB_CLIENT_SECRET
pnpm --filter comments-worker exec wrangler secret put GITHUB_REDIRECT_URI
pnpm --filter comments-worker exec wrangler secret put SESSION_SECRET
pnpm --filter comments-worker exec wrangler secret put IP_HASH_SECRETALLOWED_ORIGINS 需要包含 blog origin,例如:
https://emtech.cc,http://localhost:4321,http://127.0.0.1:4321Cookie 使用 HttpOnly; Secure; SameSite=Lax。本機 HTTP 下 GitHub OAuth session cookie 可能無法完整測試,建議用 HTTPS tunnel 或部署到 Cloudflare 後驗證。
部署 blog:
pnpm --filter blog build部署 comments worker:
pnpm --filter comments-worker run deploy若使用 Cloudflare Pages/Workers 靜態資產部署 blog,apps/blog/wrangler.jsonc 仍保留原本靜態站設定。
管理介面:
https://your-comments-worker.example.com/admin管理介面與 moderation API 都要求 GitHub 登入使用者為 elvisdragonmao。沒有額外的 admin token。
API examples:
curl -b cookies.txt \
"https://your-comments-worker.example.com/api/admin/comments?status=pending"
curl -X POST -b cookies.txt \
"https://your-comments-worker.example.com/api/admin/comments/comment-id/approve"
curl -X POST -b cookies.txt \
"https://your-comments-worker.example.com/api/admin/comments/comment-id/reject"
curl -X POST -b cookies.txt \
"https://your-comments-worker.example.com/api/admin/comments/comment-id/delete"Public routes:
GET /api/comments?pagePath=/posts/foo
POST /api/comments
GET /api/auth/github/start?returnTo=/posts/foo
GET /api/auth/github/callback
GET /api/auth/me
POST /api/auth/logoutPOST /api/comments accepts:
{
"pagePath": "/posts/foo",
"body": "required comment text",
"name": "optional display name",
"email": "optional email for Gravatar only",
"parentId": "optional parent comment id",
"turnstileToken": "required unless TURNSTILE_SECRET_KEY=dev-disabled"
}Email 會 normalize 後只儲存 SHA-256 hash,用於產生 Gravatar URL,不回傳也不儲存 raw email。
毛哥EM 製作。原始碼以 Apache-2.0,文章以 CC BY-SA 4.0 授權釋出。
感謝以下朋友們的貢獻:
也感謝所有的讀者們。