重點速覽 6 分鐘閱讀
  • apps/root 是唯一 Vite 建置入口,四個模組為 library-only 套件透過 workspace symlink 統一打包,模組本身沒有 vite.config
  • 依模組需求選後端:待辦/習慣用 Firestore,電子書用 Workers + D1 + R2,離職集章用 Workers + Workers AI
  • 電子書 Worker API 所有端點都要求 Firebase ID token(Bearer),達成跨雲身份驗證
目錄

a920604a Labs 是一個 pnpm monorepo,把四個各自獨立的日常工具整合進同一個 repo:待辦清單、習慣追蹤、電子書閱讀器、離職集章。技術主軸是 React 19 + TypeScript + Chakra UI + Firebase + Cloudflare,目標是讓共用邏輯(身份驗證、UI 元件)真正被複用,而不是在每個 side project 裡重複實作一遍。

整個專案的設計重點不在「功能很多」,而在 monorepo 怎麼切:如何讓四個 SPA 共享同一套 auth 與 ui,又能各自獨立開發;以及如何依每個模組的實際需求,靈活搭配 Firestore 或 Cloudflare 的後端服務。

monorepo 怎麼切:單一建置入口 + library-only 模組

repo 分成三層:apps/packages/workers/

a920604a-labs/
├── apps/
│   ├── root/            # SPA 入口(路由組裝 + HubPage)
│   ├── ebook-reader/    # @a920604a/ebook-reader
│   ├── habit-tracker/   # @a920604a/habit-tracker
│   ├── resign-stamp/    # @a920604a/resign-stamp
│   └── to-do-list/      # @a920604a/to-do-list
├── packages/
│   ├── auth/            # @a920604a/auth(Firebase Auth)
│   └── ui/              # @a920604a/ui(GlobalShell, LoginPage, theme)
└── workers/
    └── ebook-api/       # Cloudflare Worker + D1(電子書 API)

關鍵設計是:apps/root 是整個 SPA 唯一的建置入口,程式碼極精簡,只做三件事——

檔案職責
src/main.tsx呼叫 Firebase initFirebase(),掛載 ChakraProvider + AuthProvider
src/App.tsxBrowserRouter、MODULES 導覽設定、<GlobalShell>、四個模組 Route
src/pages/HubPage.tsx首頁啟動頁(時段問候 + 四個模組卡片)

四個功能模組則是 library-only 的 workspace 套件:每個 apps/{module} 只 export 一個 ./src/index.tsexport { default } from './App'),本身沒有 vite.config.ts。它們透過 pnpm workspace 的 symlink,由 apps/root 的 Vite 一次打包。換句話說,模組像是被「組裝」進 root,而不是各自獨立建置再拼起來。這讓四個工具能共用同一份建置設定、同一套 Provider,卻又能在各自的目錄裡獨立開發 sub-routes、components 與 hooks。

兩個共用套件:auth 與 ui

身份驗證收斂在 @a920604a/auth,對外只暴露少數幾個 API:

initFirebase(config)      // 初始化 Firebase App(幂等)
getFirebaseAuth()         // 取得 Auth instance
getFirebaseFirestore()    // 取得 Firestore instance
AuthProvider              // React Context Provider
useAuth()                 // → { user, loading, signInWithGoogle, logout }

任何模組要登入狀態,只要 useAuth() 就好,不必各自碰 Firebase SDK。

UI 則收斂在 @a920604a/ui,核心是 GlobalShell——一個仿 macOS HIG 的 sidebar shell。它的導覽完全由呼叫端傳入的 modules prop 控制,沒有硬編碼路由

interface SidebarModule {
  path: string
  label: string
  icon?: ReactNode
  subItems?: { label: string; path: string }[]
  exact?: boolean
}

GlobalShell 的呈現細節:桌面端固定左側 240px Sidebar,Topbar 用 backdrop-filter 做磨砂玻璃效果;行動端則收成 Hamburger,點開後左滑 Drawer;active state 是仿 macOS Finder 的 filled rounded rect。套件也一併匯出 LoginPageAppShellNavBar 與 Chakra 擴充的 theme(brand 色系 + Noto Sans TC)。

四個功能模組

  • 📝 待辦清單:Firestore 即時同步,支援截止日期警示、標籤分類(工作/學習/個人/其他)、列表/統計/日曆三視圖,備註用 Tiptap 富文字編輯。
  • ✅ 習慣追蹤:每日打卡、連續天數、成就徽章(資料存 Firestore)。統計頁用 Recharts + Chart.js 畫週/月打卡率折線圖、熱力圖與長條圖,並支援瀏覽器通知提醒、pdf-lib 匯出 PDF。
  • 📚 電子書閱讀器:PDF 上傳後在 IndexedDB 做本機快取,同時同步到 Cloudflare Worker 的 D1。閱讀器用 @react-pdf-viewer,記憶閱讀進度、提供分類書庫與圓餅統計圖。
  • 🏮 離職集章:100 格印章格(點擊蓋章並輸入理由)、進度條、成就徽章、每日箴言;理由可搜尋/排序/匯出 .txt,並用 pdf-lib + fontkit 一鍵生成 PDF 離職集章證明。

雙雲架構:按模組挑後端

這個專案最值得記錄的,是它沒有強求所有模組用同一套後端,而是按需求混搭:

graph LR
  User["使用者"] --> FE["apps/root SPA<br/>Cloudflare Pages"]
  FE -->|"Google Sign-In"| FAuth["Firebase Auth"]
  FE -->|"Firestore SDK"| FS[("Firestore<br/>待辦 / 習慣")]
  FE -->|"Bearer ID token"| EBK["Worker: ebook-api"]
  FE --> RSG["Worker: resign-api<br/>Workers AI"]
  EBK -->|"驗證 ID token"| FAuth
  EBK --> D1[("D1: ebook-db")]
  EBK --> R2[("R2: ebook-pdfs")]

各模組實際用到的 Cloudflare 服務:

模組PagesWorkersD1R2
電子書閱讀器
離職集章--
待辦清單---
習慣追蹤---

待辦與習慣兩個模組資料存在 Firestore,Cloudflare 只負責靜態托管;電子書用上完整的 Workers + D1 + R2;離職集章則用 Workers + Workers AI。

跨雲驗證:Worker 認 Firebase ID token

電子書的 ebook-api Worker 是雙雲整合的關鍵點。它的所有端點都要求 Authorization: Bearer <Firebase ID token>,由 Worker 端驗證 token 後才授權對 D1 的操作:

MethodPath說明
GET/books?user_id=取得書單
POST/books新增書籍
DELETE/books/:id?user_id=刪除書籍 + 進度
GET/progress/:bookId?user_id=取得閱讀進度
PUT/progress/:bookId更新閱讀進度

這樣前端拿到的 Firebase 登入身份,就能一路傳遞到 Cloudflare 的後端做授權——身份由 Firebase 發、資料由 D1 存,兩朵雲各司其職。

技術棧

層級技術版本
UI 框架React + TypeScript19.x / 5.8
元件庫Chakra UI2.x
路由React Router DOM7.x
建置Vite + SWC6.x
Monorepopnpm workspaces + NX10.x / 20.x
認證Firebase Auth(Google Sign-In)11.x
資料庫Firebase Firestore11.x
本機儲存IndexedDB(原生 API)
後端 APICloudflare Workers
API DBCloudflare D1(SQLite)
部署Cloudflare Pages + Workers
CI/CDGitHub Actions

部署走 .github/workflows/deploy.yml:push 到 main 後自動跑兩個 job——deploy-root 建置 apps/root 並部署到 Cloudflare Pages(a920604a-labs),deploy-ebook-api 則部署 workers/ebook-api 到 Cloudflare Workers。Firebase 與 Cloudflare 的金鑰都放在 GitHub Secrets 注入。

小結

a920604a Labs 真正的價值不在四個工具各自的功能,而在它示範了一種務實的 monorepo 切法:單一 Vite 建置入口 + library-only 模組讓共用變得自然,而按模組挑後端讓你不必為了「統一」而過度設計——簡單的存 Firestore,需要關聯資料與檔案的才動用 Workers + D1 + R2。對個人全端實驗場來說,這種「共用該共用的、隔離該隔離的」分寸,比堆技術更難得。

參考資料

問這篇文章

AI 只根據這篇文章內容回答。點下方任一問題,或直接開右下對話框。

相關標籤

相關文章

Live English Tutor:用 LiveKit + Gemini Native Audio 打造即時語音 AI 英文家教

一套以即時語音為核心的 AI 英文家教系統:學生用麥克風(選配視訊/螢幕分享)跟 AI 老師 Emma 對話練習,系統即時糾錯並生成課後中文報告。技術核心是 LiveKit(Self-hosted WebRTC)+ Google Gemini 2.5 Flash Native Audio,後端 FastAPI 負責認證、課程與資料持久化。