mido-learning:一個 Firebase + .NET 的學習平台架構解析

mido-learning:一個 Firebase + .NET 的學習平台架構解析

技術棧:Next.js 14 + .NET 8 Minimal API + Firebase (Auth/Firestore/Storage) + Cloud Run


這個專案做什麼

mido-learning 是一個學習組件平台。使用者可以上傳 ZIP 格式的互動式教材(HTML/CSS/JS),平台會自動解壓、版本控制、並提供嵌入式預覽。

從 codebase 看,核心功能包括:

  • 教材上傳與版本管理(MaterialEndpoints.cs:750 行)
  • 學習組件的 CRUD(ComponentEndpoints.cs:725 行)
  • 評分與願望清單系統
  • 動態分類管理
  • 三種角色:admin / teacher / member [此處可加入:你為什麼想做這個專案?解決什麼問題?]

架構選擇

為什麼是 Firebase + .NET?

這個架構有點特別——前端用 Firebase Auth SDK 直接處理登入,後端用 .NET 8 Minimal API 驗證 Token。

從 FirebaseAuthMiddleware.cs 可以看到流程:

// 1. 從 header 拿 token
var token = context.Request.Headers[“Authorization”]
.FirstOrDefault()?.Replace(“Bearer “, “”);

// 2. Firebase Admin SDK 驗證
var decodedToken = await firebaseService.VerifyIdTokenAsync(token);

// 3. 解析 custom claims(特別是 admin)
var isAdmin = decodedToken.Claims.TryGetValue(“admin”, out var adminClaim)
&& adminClaim is bool b && b;

這種「前端認證、後端驗證」的模式,讓認證邏輯分散在兩端。

[此處可加入:你選擇這個架構的原因是什麼?踩過什麼坑?]


檔案上傳的設計

MaterialEndpoints.cs 處理教材上傳,邏輯如下:

  1. 前端上傳 ZIP 檔(限制 50MB)
  2. 後端驗證 ZIP 內容必須包含 HTML 檔
  3. 自動偵測進入點(優先找 index.html)
  4. 解壓到 Cloud Storage:materials/{componentId}/v{version}/
  5. 在 Firestore 記錄 manifest POST /api/components/{componentId}/materials
    Content-Type: multipart/form-data
    Header: X-Expected-Size: {bytes} // 早期驗證用 版本控制是自動的——每次上傳就是新版本。舊版本不會被刪除。

前端結構

Next.js 14 App Router,路由群組清楚:

app/
├── (public)/ # 公開頁面
├── (auth)/ # 登入/註冊
├── (member)/ # 會員專區
├── (teacher)/ # 教師管理
├── (admin)/ # 管理後台
└── (fullscreen)/ # 全螢幕教材檢視

API 呼叫封裝在 lib/api/*,統一注入 Firebase ID Token。從 components.ts 可以看到:

const response = await fetch(${API_URL}/api/components, {
headers: {
‘Authorization’: Bearer ${await getIdToken()}
}
});


部署

GitHub Actions + Cloud Run,兩條 pipeline:

後端 (deploy-backend.yml):

  • 觸發:push 到 main 且 backend/** 有變更
  • 多階段 Docker build(.NET SDK → .NET Runtime)
  • 部署到 asia-east1,512Mi memory,0-10 instances 前端 (deploy-frontend.yml):
  • Firebase 環境變數從 GitHub Secrets 注入
  • Node 20 Alpine 映像
  • 同樣部署到 Cloud Run 自訂網域:https://learn.paulfun.net

程式碼統計

從 codebase 實際計算:
┌──────────────────────┬───────────┐
│ 區塊 │ 行數 │
├──────────────────────┼───────────┤
│ Endpoints (9 個檔案) │ 2,661 行 │
├──────────────────────┼───────────┤
│ Services │ 932 行 │
├──────────────────────┼───────────┤
│ Models │ 597 行 │
├──────────────────────┼───────────┤
│ 後端總計 │ ~4,644 行 │
└──────────────────────┴───────────┘
前端估計 3,000+ 行(未精確計算)。


Firestore 資料結構

從 firestore.rules 和 Models 推導:

components/{componentId}
– title, theme, description
– visibility: “published” | “login” | “private”
– materials: [{id, name, url, type}]
– questions: [{question, answer}]
– ratingAverage, ratingCount
– createdBy: {uid, displayName}

materials/{materialId}
– componentId, version, entryPoint
– uploadedAt, uploadedBy


本地開發

docker-compose.yml 一鍵啟動:

docker-compose up

  • 前端:http://localhost:3000
  • 後端:http://localhost:5000
  • Firebase Emulator UI:http://localhost:4000 不需要真的 GCP 帳號就能跑完整環境。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *