iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Vibe Coding

神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 系列 第 6

[Day 6] 投票頁上線:建立正式 D1、部署 Worker,上線前我檢查了什麼

  • 分享至 

  • xImage
  •  

Day 6

文章同步發表在我的個人 Blog

投票頁上線:建立正式 D1、部署 Worker,上線前我檢查了什麼

前五天都在本機跑,今天要把投票頁放出去。

投票頁在這裡:https://room.gooliya.com/

走進小孩房,在白板上貼三個圓點就好,大概一分鐘。貼完才看得到大家怎麼選,如果你家裡也在帶小孩,歡迎進來貼一下,這份結果我後面幾天會拿來排功能的順序。

在開始之前想先聊聊。這次的問卷我想跳脫過去的做法,上線前有找 Jeff 聊過,他是資深研究員,平常也在教問卷設計。問卷要有趣味性,也要顧到填答效率,這兩件事得互相權衡。這一版設計的時間不夠,只能先做到這裡,未來有時間再回頭研究。

這篇寫的是從本機到正式環境的那一段:正式資料庫怎麼建、部署指令怎麼下、上線前我用什麼確認沒有出包。

1. 先看成品

你們家也有嗎?:https://room.gooliya.com/

你們家也有嗎?

走進一間小孩房,走到牆上的家庭白板前,用三個圓點貼出你們家比較常發生的情況。題目是「最近 1 個月,帶小孩的日常裡,哪些事最常讓你或另一半說出『你怎麼沒跟我說』?」,貼完才看得到大家怎麼選。

家庭白板

桌機用 WASD 走路,走到白板前按 E。手機請橫著拿,左下角有搖桿。

投票結果

貼滿三票才看得到結果。百分比算的是「所有圓點中,各情境占多少」,不是「多少家庭選了這個」,因為一個人可以把三票押在同一張。

收尾畫面

2. 用到 Cloudflare 的哪些東西

這個投票頁整套都放在 Cloudflare 上,用到四個服務,先簡單說明各自負責什麼。

服務 是什麼 這個專案用它做什麼
Workers Cloudflare 的程式執行環境,程式碼部署上去之後會散在它全球的節點,有人連進來就由比較近的節點執行 跑投票的 API
Static Assets 掛在 Workers 上的靜態檔案服務 送前端 build 出來的 dist,不必另外開一個 Pages 專案,網頁和 API 就會在同一個網域底下
D1 Cloudflare 的 SQLite 資料庫,用一般的 SQL 操作 存選項、票數和 Email 訂閱,Worker 透過 binding 直接讀寫,程式裡不用寫連線字串,也不用管密碼
Custom Domain 自訂網域,綁定之後 DNS 記錄和憑證都由 Cloudflare 建好 綁上這個投票頁的 room.gooliya.com

這些都寫在同一個設定檔 wrangler.jsonc 裡:

{
  "name": "little-room-mvp",
  "main": "worker/index.ts",                                    // the API
  "routes": [{ "pattern": "room.gooliya.com", "custom_domain": true }],
  "assets": {                                                   // the web page
    "directory": "./dist",
    "not_found_handling": "single-page-application",
    "run_worker_first": ["/api", "/api/*"]                      // API first, then static files
  },
  "d1_databases": [{ "binding": "DB", "database_name": "little-room-mvp", "database_id": "..." }]
}

run_worker_first 這一行是關鍵:/api 開頭的請求先交給 Worker,其他路徑才去找靜態檔案。少了它,API 會被當成找不到的頁面,直接回前端的 index.html

操作這些服務的指令列工具叫 Wrangler,建資料庫、套 migration、部署都是用它。下面的步驟都會用到。

3. 部署做了什麼

總共五個步驟。

建立正式資料庫

wrangler d1 create little-room-mvp   # returns the production database id

把 ID 填進設定檔wrangler.jsonc 換成剛拿到的 ID。本機開發仍然用同一個 binding 加 --local,兩邊不會互相影響。

這裡有個小問題:本機的 D1 檔案是按 database_id 存的,ID 一換就等於指向一個全新的空資料庫。換完之後本機的 API 全部回 500,重跑一次 npm run db:migrate:local 才恢復。

套用 migration

npm run db:migrate:remote   # wrangler d1 migrations apply DB --remote

兩份 migration 根據順序執行:第一份建四張表並寫入五個正式選項,第二份是 Email 訂閱的表。

確認 seed。直接查正式資料庫,確認五個選項都在、排序正確、都是啟用狀態。

wrangler d1 execute DB --remote --command \
  "SELECT sort_order, id, category FROM survey_options ORDER BY sort_order"

部署

npm run build && wrangler deploy

build 產生的 dist 和 Worker 會一起上去,部署完就拿到網址。

4. 上線前檢查了什麼

正式環境跟本機差比較多的是 cookie 和權限,所以我沒有只在畫面上點一點,而是直接對正式網址打 API。

檢查 結果
cookie 是否帶 Secure 有,本機因為是 http 所以沒有,正式環境自動加上
三票上限 第 4 票回 409
取消別人的票 回 404,對方票數不變
未投滿三票就查結果 回 403
結果 API 的回應內容 只有選項 id、票數、百分比,沒有 session、沒有原始投票
補充情境的狀態 一律 pending,其他人看不到
無效 Email 直接打 API 全部擋下來
公開 API 會不會回傳 Email 不會
伺服器 log 沒有 Email

另外開一個無痕視窗走過一次,確認新訪客看到的是乾淨的狀態,不會看到別人的票或別人寫的便利貼。

5. 沒有先做的部分

  • Turnstile 真人驗證:現在完全沒有。
  • IP 限流:沒有。
  • 審核後台:補充情境寫進資料庫是 pending,但還沒有介面可以審,所以目前不會公開顯示。
  • 結果分析:資料都在 D1,搜集完後再決定如何分析。

另外:清掉 cookie 或換一個瀏覽器,就可以再投三票。 這是匿名投票的先天限制,要擋住得加真人驗證或 IP 限流,而這兩項我這次都沒做。

也因為這樣,畫面上不會寫「每個人只能投一次」。而這份結果我會當成互動式的意見收集,不是具代表性的統計研究。

6. 目前的狀態

正式資料庫建好了、migration 套用完成、五個選項確認無誤、Worker 部署上線,桌機和手機都完整走過一次流程。

還沒處理的是上線 smoke test 留下的測試資料,現在還在正式資料庫裡,開始收真實資料之前會先清掉。

明天

Day 7 開始進 Swift。我想先找一些方法,讓自己可以比較快地探索和學習 Swift 的程式碼,


上一篇
[Day 5] 兩個 AI Agent 的 Harness 差在哪:誰能開瀏覽器,誰就負責驗收
系列文
神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言