專案介紹
CloudBio 是一款自架式 Bio Link 管理工具,類似 Linktree / Portaly 的開源替代方案。使用者可透過視覺化編輯器,以拖曳方式自由組合文字按鈕、輪播橫幅、影片播放器等多種區塊,打造個人化的 Bio Link 頁面。
全端架構基於 Hono 運行於 Cloudflare Workers,搭配 D1 SQLite 資料庫、R2 物件儲存與 KV 快取,實現零伺服器維護成本的完整 Bio Link 服務。
核心功能
七種區塊類型
支援文字按鈕(含副標題、圖片、動畫效果)、輪播橫幅、方形面板、雙欄方格、影片播放器(YouTube)、分隔線與富文本區塊,滿足多樣化的內容展示需求。拖曳式編輯器
基於@dnd-kit 實現 Notion 風格的區塊拖曳排序,搭配手機模擬預覽框,即時檢視編輯效果。外觀自訂系統
支援純色、漸層、圖片三種背景模式,可自訂按鈕樣式、字型、文字顏色,並提供融入式與卡片式兩種個人資訊佈局。進階使用者可透過自訂 CSS 進一步調整。多頁面管理
支援在同一個帳號下建立多個子頁面,每個頁面有獨立的區塊配置。按鈕區塊可以直接連結到其他子頁面,讓使用者用分頁的方式組織更複雜的內容結構,同時保持操作上的簡單直覺。社群平台整合
內建 13 種社群平台圖示(Instagram、GitHub、LinkedIn、X、Threads 等),一鍵設定社群連結。圖片管理
透過Cloudflare R2 處理圖片上傳,支援客戶端 WebP 壓縮與裁切工具,減少頻寬消耗與儲存成本。拖曳排序體驗

基於 @dnd-kit 實現的拖曳排序,快速點擊(300ms 以內)開啟編輯,長按才進入拖曳模式。滑鼠操作移動超過 8px 才觸發拖曳,避免誤操作。手機跟電腦上都很直覺,不需要額外的拖曳把手或模式切換。
即時預覽

編輯器內建手機模擬預覽框,所有修改即時反映在預覽畫面上。後臺看到的效果跟前臺訪客完全一致,不需要來回切換確認。
圖片壓縮

圖片上傳前在客戶端進行 WebP 壓縮與裁切,減少傳輸量跟 R2 儲存空間。上傳後透過長期快取 header 提供存取,降低重複請求的頻寬消耗。
技術架構
前端
以React 19 搭配 shadcn/ui 元件庫與 Tailwind CSS v4 建構現代化介面。使用 SWR 進行資料同步,@dnd-kit 處理拖曳交互,react-easy-crop 實現圖片裁切功能。後端
以Hono 為 Web 框架,運行於 Cloudflare Workers 邊緣運算環境。使用 Cloudflare D1 (SQLite) 作為資料庫,R2 儲存圖片資源,KV 快取已渲染的頁面 HTML。透過 Drizzle ORM 進行 Type-safe 的資料操作。認證系統
自建 JWT 認證機制,密碼使用PBKDF2-SHA256(100,000 次迭代)進行雜湊,採用 HttpOnly + Secure + SameSite Cookie 管理 Session,支援 Email 白名單邀請制。SSR 渲染
公開 Bio 頁面採用伺服器端渲染,並將結果快取至KV,避免重複的資料庫查詢,提升訪客端的載入速度。工具選擇
整個專案建立在 Cloudflare 生態系上,工具的選擇也是圍繞這個前提展開的。
為什麼用 Hono 而不是 Express
Express 設計上是給 Node.js 環境用的,要跑在 Cloudflare Workers 上需要額外的適配層,效能跟相容性都會打折扣。Hono 從一開始就是為邊緣運算環境設計的,原生支援 Workers 的 Request / Response API,不需要任何轉換。加上它的路由系統跟 Middleware 設計都很直覺,開發體驗跟 Express 接近,但完全沒有相容性問題。為什麼用 D1 而不是其他資料庫
跟選 Hono 的理由一樣,既然整個架構都跑在 Cloudflare 上,資料庫選D1 是最自然的搭配。D1 是 Cloudflare 原生的 SQLite 服務,跟 Workers 之間的連線是零延遲的,不需要處理外部資料庫的連線池或冷啟動問題。搭配 Drizzle ORM 做 Type-safe 的資料操作跟 Migration 管理,開發流程非常順暢。R2 + KV 的分工
R2 負責圖片這類靜態資源的持久化儲存,KV 則用來快取已渲染的頁面 HTML。兩者各司其職:R2 處理大檔案、KV 處理高頻讀取的小資料,都是 Cloudflare 原生服務,不需要額外設定或維護。資料庫設計
使用 Cloudflare D1 (SQLite) 搭配 Drizzle ORM,以四張資料表涵蓋使用者、頁面、區塊內容與外觀設定。區塊採用 JSON config 欄位儲存各類型的設定資料,實現靈活的多型區塊架構。
| 資料表 | 說明 | 關鍵欄位 |
|---|---|---|
users | 使用者帳號與個人資訊 | id, email, username, password_hash, display_name, bio, avatar_url, social_links |
pages | 多頁面管理,支援主頁與子頁面 | id, user_id, slug, title, sort_order, is_default |
blocks | Bio 頁面的內容區塊(七種類型) | id, user_id, type, config (JSON), is_active, sort_order |
appearances | 使用者的外觀主題設定 | id, user_id, background, button_style, font_family, text_color, custom_css |
資料庫設計細節
JSON Config 多型設計
每種區塊需要的屬性差異很大:按鈕需要連結、副標題、動畫效果;輪播橫幅需要多張圖片跟標籤設定;影片只需要一個 YouTube URL。如果為每種類型各開一張表或加一堆 nullable 欄位,資料庫會變得很難維護。所以 blocks 表只保留通用欄位(type、is_active、sort_order),所有類型特有的設定都收進一個config JSON 欄位。新增區塊類型時,只需要定義新的 Config 介面跟預設值,不需要改資料庫結構。Fractional Sort Order
拖曳排序用的是浮點數sortOrder,而不是整數序號。把區塊從位置 A 移到 B 跟 C 之間時,新的 sortOrder 就是 (B + C) / 2。這樣每次排序只需要更新一筆資料,不用重新編號整個列表。即使經過大量排序操作,浮點數的精度也足以應付實際使用場景。多頁面支援
pages 表透過slug 欄位區分主頁(空字串)跟子頁面,並以 (userId, slug) 建立唯一索引,確保同一個使用者不會有重複的頁面路徑。區塊透過 pageId 關聯到所屬頁面,刪除頁面時會 cascade 刪除底下所有區塊。快取機制
公開的 Bio 頁面採用 SSR 渲染後快取到 Cloudflare KV,避免每次訪問都重新查詢資料庫。
快取策略
頁面 HTML 的 TTL 設為 5 分鐘,在快取期間內的訪問完全不需要碰資料庫。Key 的格式是page:{username} 跟 page:{username}:{slug},分別對應主頁跟子頁面。Index 追蹤與精準失效
為了在使用者更新內容時能正確清除快取,系統維護了一個pages-idx:{username} 索引,記錄該使用者所有被快取的子頁面 slug。這樣在需要全量失效時,可以透過索引找到所有 key 一次清除,不用猜測或掃描。單一區塊的修改則只失效該區塊所屬頁面的快取,避免不必要的全量清除。API 設計
基於 Hono 路由設計 RESTful API,所有需認證的端點皆透過 JWT Middleware 保護:
認證系統 /api/auth
個人檔案 /api/profile
區塊管理 /api/blocks
外觀設定 /api/appearance
圖片上傳 /api/upload
/api/img/* 提供長期快取的圖片存取公開頁面 /api/bio/:username