| 項目 | 值 |
|---|---|
| 前端網址 | https://farm.ahpal.com |
| Worker URL | https://ahpal-farm-api.praystone.workers.dev |
| Pages 專案 | ahpal-farm |
| D1 資料庫 | ahpal-farm-db(database_id 見 wrangler.toml) |
| GitHub repo | praystone/ahpal-farm |
| 部署方式 | GitHub Actions(push 即部署) |
| 子網域 | farm.ahpal.com(CNAME 橘雲) |
database_id 見 worker/wrangler.toml,account_id 見 GitHub Secret CLOUDFLARE_ACCOUNT_ID。手冊會上網站,公開,所以不寫死。
| # | 表名 | 用途 |
|---|---|---|
| 1 | crops | 農作物靜態資料(id, name, grow_seconds, icon, sell_price) |
| 2 | farms | 每塊田(id, nickname, crop_id, planted_at, harvested, stolen_by) |
| 3 | farm_logs | 播種/收成/被偷紀錄(id, nickname, action, crop_id, created_at) |
5 種基礎農作物:🥕蘿蔔 60 秒 / 🍅番茄 300 秒 / 🌽玉米 1800 秒 / 🍓草莓 7200 秒 / 🍉西瓜 28800 秒
| # | 端點 | 方法 | 用途 |
|---|---|---|---|
| 1 | /api/crops | GET | 農作物清單 |
| 2 | /api/plant | POST | 種植(body: nickname, cropId) |
| 3 | /api/farm | GET | 查看自己的田(query: nickname) |
| 4 | /api/harvest | POST | 收成(body: id, nickname) |
/api/harvest 讀的是 body.id,不是 body.farmId。前端若送 farmId,會回「找不到這塊田」。
農場跟寵物最大的差異:離線也在長。種下去,8 小時後回來就能收。
實作方式:不要用排程器。種下去時 D1 存 planted_at,玩家來看時 Worker 算 now - planted_at,超過 grow_seconds 就回「已成熟」,收成時才寫 D1。
| 目標 | 指令 | 觸發 |
|---|---|---|
| 前端 Pages | git push → GitHub Actions | 自動 |
| Worker 後端 | wrangler deploy | 永遠手動 |
| D1 資料庫 | wrangler d1 execute --remote | 永遠手動 |
wrangler pages deploy 沒有 --account-id 參數wrangler.toml 的 account_id,只認環境變數 CLOUDFLARE_ACCOUNT_ID${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 展開成空字串onclick="fn(' + JSON.stringify(id) + ')" 雙引號打架,SyntaxError: Unexpected end of inputstatic.cloudflareinsights.com| 功能 | 狀態 |
|---|---|
| 種植 | ✅ 已上線 |
| 查看 | ✅ 已上線 |
| 收成 | ✅ 已上線(回 sell_price) |
| 金幣機制 | ⚠️ 還沒做(收成回 sell_price 但沒寫進玩家表) |
| 偷菜 | ❌ 第二期(原定第二期) |
.env,腳本只讀不寫。」onclick 用 data-id + addEventListener,不用內聯。」CLOUDFLARE_ACCOUNT_ID。」/api/harvest 讀 body.id,不是 body.farmId。」2009 年,Facebook 開心農場。你半夜設鬧鐘起來,就為了偷同學那顆快熟的番茄。那時候,我們都還年輕。放學衝回家,打開電腦,偷菜、種菜、被偷,一個都不能少。
17 年後,開心農場回來了。不是 Facebook,是 AHPAL。
你的菜,還在長。你的青春,還在等你。