ActiFlow:活動建立、報名與審核的全端平台

2026年9月15日

ActiFlow 活動列表

為什麼做這個專案

辦活動最麻煩的往往不是活動本身,而是前後的行政:建立報名表、控制名額、截止後關閉報名、整理報名名單。ActiFlow 是一個活動建立、公開招募、線上報名與主辦方審核的平台,把這些流程放在同一個地方完成。


主要功能

ActiFlow 首頁

區域功能
公開活動首頁近期活動、活動分類、活動列表、歷史活動、活動詳情
活動報名依活動設定的動態報名欄位、截止與名額檢查、報名成功頁
會員中心個人資料、頭像上傳、我的報名與單筆報名詳情
主辦後台建立與編輯活動、設定報名欄位、審核並匯出報名資料(CSV)
系統後台會員與主辦單位管理、主辦申請審核、活動強制下架、管理操作紀錄

一場活動從建立到結束的流程:

  1. 主辦單位建立活動,設定報名欄位、名額、日期與報名截止時間
  2. 活動發布後出現在公開列表;已結束的活動由 API 在查詢階段分到「歷史活動」
  3. 會員登入後送出報名,後端檢查活動狀態、截止時間、名額與重複報名
  4. 主辦單位在後台審核、匯出報名資料,會員可在「我的報名」查看狀態

權限設計:網站層級 × 活動單位層級

這個專案花最多心思的是權限。ActiFlow 不用單一的角色階級,而是拆成兩層:

層級角色
網站層級網站系統管理員、網站會員、訪客
活動單位層級owner、admin(對外都顯示為「活動單位管理員」)、member

同一個人可以是網站會員,同時是「河濱路跑協會」的 owner、「攝影學會」的 member。所以權限必須依「目前操作的是哪個資源」來判斷:

  • 進入活動單位後台時,用網址中的單位 ID 查詢這個人在該單位的身分
  • 敏感操作不信任前端傳來的角色或 ID,一律回資料庫查詢最新的成員資格
  • owner 與 admin 都能管理活動,但只有 owner 能指派管理員、轉移擁有權或解散單位
  • 系統管理員不會自動變成每個單位的 owner;平台介入(例如強制下架活動)走獨立的管理 API,必須填寫原因並寫入操作紀錄

前端隱藏按鈕只是操作體驗,真正的權限邊界在後端。例如直接打開已截止活動的報名網址,後端仍會再次檢查截止時間與名額。


圖片上傳:金鑰不離開後端

頭像、主辦單位 Logo 和活動封面都存放在 Cloudflare R2:

  1. 前端告訴後端檔案類型、大小與用途
  2. 後端驗證後,產生 5 分鐘有效的預簽章上傳網址(presigned URL)
  3. 瀏覽器直接把檔案上傳到 R2,R2 金鑰從頭到尾都留在後端
  4. 資料庫只存圖片的公開網址

上傳限制依用途區分(頭像與 Logo 5 MiB、活動封面 8 MiB),只接受 JPEG、PNG、WebP、GIF;因為有 XSS 風險,刻意不接受 SVG。


部署:Vercel + Cloud Run + Neon

服務平台
前端Vercel
後端 APIGoogle Cloud Run(新加坡)
資料庫Neon PostgreSQL(新加坡)
圖片Cloudflare R2
EmailResend(註冊驗證信與通知信)

部署時有幾個安全設定:

  • 資料庫連線字串與 JWT 金鑰放在 GCP Secret Manager,執行服務的帳號只有讀取這些機密的權限
  • Cloud Run 閒置時縮到 0 個實例、上限 1 個,並設定費用警示
  • 後端在 staging/production 環境會拒絕不安全的設定直接啟動失敗,例如過短的 JWT 金鑰、非 HTTPS 的前端網址、萬用 CORS,或開啟除錯路由
  • 登入使用 HttpOnly Cookie 中的 JWT,前端程式碼讀不到 token

技術架構

項目使用技術
前端Next.js 16(App Router)+ React 19 + TypeScript
狀態管理Redux Toolkit、Axios
樣式Tailwind CSS
後端Python 3.11 + FastAPI + Pydantic 2
資料庫PostgreSQL(Neon)+ SQLAlchemy 2 + Alembic
驗證HttpOnly Cookie + JWT、RBAC
測試Pytest(使用獨立的測試資料庫,避免動到正式資料)

🌐 線上體驗