專案背景
這是我大學「網際網路系統設計」課程的期末作業。那是一堂非同步課程,每週老師都會出題,讓我們實作課程當週教的內容。
整門課我學得很開心,主要是因為老師教得非常紮實,每個主題都有配套的實作題,讓概念和動手做緊密連結。到了期末,老師讓我們自訂題目,做一個能展示整學期所學的完整 Web 應用,這個子彈日記就這樣誕生了。
最近我把 Jotly 部署到了自己的舊電腦上(Nginx + Gunicorn + Cloudflare Tunnel),讓它變成為一台 Home Server,並且利用 AI 協助修掉了一些當初趕作業沒處理好的小問題,讓整體功能更完善。
什麼是子彈日記?
Bullet Journal(子彈日記)是由 Ryder Carroll 發明的筆記方法,概念是用簡短的「子彈符號」(· 任務、○ 事件、— 筆記)快速記錄生活,並依時間尺度(每日、每週、每月)分類整理。
Jotly 的目標就是把這套方法搬到網頁上,同時保留它最重要的特質:快速、無干擾。
核心功能
- Email-Only 帳號系統:只需 Email 即可註冊,降低入門門檻
- OTP 驗證:帳號啟用與登入時使用 6 位數一次性驗碼,由後端 Email 發送
- 多尺度日誌:Daily / Weekly / Monthly / Yearly 四種日誌類型,側欄可快速切換
- 手繪風格 UI:自製 CSS 元件搭配 Bootstrap 5,模擬紙筆質感
- 安全設計:內建 CAPTCHA、Django 密碼加密、CSRF 防護
技術選擇與實作重點
Django 後端架構
這是我第一個完整的 Django 專案。過去雖然寫過 Flask,但 Django 的框架讓我第一次感受到 MTV 架構的完整運作方式:
- Model:
Log、BulletJournalKey、User等模型直接對應資料庫 Table,ORM 的查詢語法比手寫 SQL 直覺很多 - View:Class-Based Views 讓每種 HTTP 動詞(GET/POST)的邏輯分開放,比一大堆
if request.method == 'POST'乾淨 - Template:Django Template Language 的
{% block %}繼承讓我把 Navbar、Footer 抽成共用 layout,不用每頁複製貼上
OTP 驗證流程
OTP 是整個專案最有趣的部分之一。大致流程是:
- 使用者輸入 Email → 後端生成 6 位亂數碼 + 設定 10 分鐘有效期,存入資料庫
- 用 Django
send_mail寄出驗證碼 - 使用者填入驗碼 → 後端比對、確認有效期、標記已使用
- 驗證通過後建立 Session,完成登入
這讓我對「無狀態 HTTP 怎麼維持登入狀態」有了更多理解:
Session ID 存在 Cookie,每次請求 Django 都去 Session Store 比對,確認身份。
部署:舊電腦 Home Server + Cloudflare Tunnel
買了新電腦之後,舊電腦就讓它繼續發揮功效,我讓他常駐開機當 Home Server。架構是 Cloudflare Tunnel → Nginx → Gunicorn,cloudflared 主動向 Cloudflare 建立加密隧道,不需要在路由器開 Port 或設定動態 DNS,對家用網路來說非常省事。
最頭痛的地方是 ALLOWED_HOSTS 和 CSRF_TRUSTED_ORIGINS,Django 在 DEBUG=False 下對 Host Header 很嚴格,加上 Cloudflare 還會附加額外的 Header,遇到幾次 400 / 403 才調對。
AI 協助的修繕
部署後用 AI 幫忙 Review 了幾個原本趕作業沒細想的地方:
- OTP 驗碼的暴力破解防護(增加嘗試次數限制)
- 前端表單的 UX 細節(錯誤提示更明確)
requirements.txt的版本鎖定,讓部署環境更可重現
學習反思
做完這個專案,第一次感受到一個完整產品的全貌:從資料庫設計、後端邏輯、前端介面,到最後的生產環境部署。
如果現在重做,我可能會:
- 換 PostgreSQL 取代 SQLite,為未來多用戶場景做準備
- 加入 Django REST Framework 把後端改成 API,前端用 React 重寫
不過也正因為做過這一版,才知道這些決策的取捨在哪裡。
本文案使用 Claude 進行文字潤飾與架構整理。
