🦖 第十一章:FastAPI 巨獸崛起!打造萬人打卡伺服器

在上一門課「Line 打卡網頁 (LIFF)」中,我們做出了極度炫酷的打卡按鈕,並取得了 Line 簽發的安全憑證 (JWT Token)。 但前端網頁就像是餐廳的「服務生」,他只負責點餐。真正把資料寫進資料庫、計算薪水、判斷有沒有遲到的,是躲在廚房裡的「後端伺服器 (Backend Server)」。

如果你是用傳統的 Node.js (Express) 或是 PHP 來寫後端,當早上 09:00 湧入 5000 個員工同時打卡時,傳統伺服器會因為「阻塞 (Blocking)」而崩潰,導致後面的 1000 個員工打卡失敗,全公司大暴走。

為了解決這個問題,這堂課我們將引入 Python 界目前最紅、效能足以媲美 NodeJS 和 Go 的框架:FastAPI。 如同它的名字,它極度快速、天生支援非同步 (Async)、並且能自動幫你產出 API 文件。 我們將透過 Vibe Coding,指揮 AI 幫我們建構這個可以承受萬人並發的巨獸級後台!


⚡ 實戰 1:極速搭建 FastAPI 骨架與 ngrok 穿透

要開發 Line 相關的應用程式 (Line Bot 或 LIFF 串接),我們面臨的第一個死穴是:Line 伺服器只能把資料送到「有 https 網址的公網伺服器」。 如果你在自己電腦上跑 localhost:8000,Line 根本找不到你! 這時候,我們需要兩個神器:FastAPI (架設本地伺服器) 與 ngrok (把本地伺服器打穿出去變成外網網址)。

💡 Vibe Prompt 實戰 1:讓 AI 寫出最簡潔的啟動腳本

不要自己去背那一堆 uvicorn 的參數,直接讓 AI 幫你寫一個馬上能跑的骨架。

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我正在使用 Python 開發一個 Line Bot 的後端伺服器。 請幫我用 FastAPI 寫一個最基礎的骨架程式 (main.py)。 需求: 1. 建立 FastAPI 實體 app。 2. 寫一個根目錄路由 (GET /),回傳 {"status": "ok", "message": "Punch System is running"} 3. 寫一個 Webhook 接收路由 (POST /api/webhook),接收任意 JSON 資料並在 console 印出來。 4. 提供在終端機啟動這個伺服器的 uvicorn 指令 (使用 port 8000,並開啟 reload)。 5. 提供對應的 requirements.txt 內容。

🤖 AI 生成的極速起手式:

# main.py
from fastapi import FastAPI, Request

# 建立 FastAPI 巨獸實體
app = FastAPI(title="Line Punch System API")

@app.get("/")
async def root():
    """用來檢查伺服器是否活著的健康檢查點 (Health Check)"""
    return {"status": "ok", "message": "Punch System is running 🚀"}

@app.post("/api/webhook")
async def receive_webhook(request: Request):
    """
    這是一個「開大門」的路由,準備用來接收前端或 Line 伺服器打過來的資料
    """
    # 非同步讀取傳進來的 JSON 資料
    payload = await request.json()
    print("📥 收到新的 Webhook 資料:", payload)
    
    # FastAPI 非常聰明,只要回傳字典,它會自動幫你轉成 JSON 格式送回前端
    return {"status": "success", "received_data": payload}

啟動指令與穿透:

  1. 在終端機輸入:uvicorn main:app --reload --port 8000
  2. 打開第二個終端機輸入:ngrok http 8000
  3. ngrok 會給你一串神奇的網址(例如 https://1234-abcd.ngrok-free.app)。把這個網址貼到你的前端或 Line Developer 後台,你的電腦就正式成為雲端伺服器了!

🔐 實戰 2:資安第一防線 (CORS 設定)

當你興高采烈地把 ngrok 網址貼到前端的 LIFF 網頁中,按下「打卡」按鈕。 你會發現瀏覽器的 Console 出現一片鮮紅色的錯誤:Blocked by CORS policy (被跨來源資源共用政策阻擋)!

這是因為現代瀏覽器有一套極度嚴格的安全機制:如果不允許,A 網站 (Vercel 上的前端) 絕對不能隨便打 API 給 B 網站 (你的 ngrok 後端)。 我們必須在 FastAPI 裡面安裝一個「警衛」,告訴他:「沒關係,Vercel 那邊是自己人,放他進來!」

💡 Vibe Prompt 實戰 2:完美設定 CORS Middleware

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我的前端網頁 (部署在 https://my-punch.vercel.app 和 http://localhost:5173) 打 API 給我的 FastAPI 伺服器時,遇到了 CORS 錯誤。 請幫我修改剛才的 main.py,加入 CORSMiddleware 的設定。 需求: 1. 允許上述兩個網址的來源 (Origins)。 2. 允許所有的 Methods (GET, POST, OPTIONS 等)。 3. 允許所有的 Headers。 請給我包含 CORS 設定的完整 FastAPI 初始化程式碼。

🤖 AI 的企業級警衛設定:

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI(title="Line Punch System API")

# 定義「白名單」:只有這些網址發出的請求,我們才接聽!
# ⚠️ 在正式上線時,絕對不能寫 ["*"] (允許所有人),那等於門戶洞開!
origins = [
    "http://localhost:5173",          # 給前端工程師在本地端測試用
    "https://my-punch.vercel.app",    # 正式上線的前端網址
]

# 安裝跨來源警衛 (Middleware)
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,       # 只允許白名單的來源
    allow_credentials=True,      # 允許攜帶 Cookie 或認證資訊
    allow_methods=["*"],         # 允許所有 HTTP 動作 (GET, POST, PUT, DELETE)
    allow_headers=["*"],         # 允許所有自訂 Header (包含我們等等要用的 Authorization)
)

# ... 下面接著寫你的 @app.get("/") 路由 ...

🕵️‍♂️ 實戰 3:解開 LIFF 的數位簽章 (JWT 驗證)

還記得我們在前端課程中說過:「前端傳過來的任何資料,都不可信任」 嗎? 如果前端傳來 {"user_name": "王老闆", "action": "clock_in"},駭客隨時可以攔截並改成 {"user_name": "李小明"} 來幫別人代打卡。

所以我們在前端傳送的是一個亂碼:JWT Token (idToken)。 現在,後端收到這串 Token 了。我們不能自己去解開它,我們必須把這串 Token 傳送給 Line 的官方伺服器,問它:「這串憑證是不是真的?它是發給誰的?」

💡 Vibe Prompt 實戰 3:呼叫 Line 官方 API 驗證 Token

這是一段牽涉到第三方 API 溝通的複雜邏輯,如果自己看官方文件寫,很容易漏掉錯誤處理。

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

在 FastAPI 中,我的前端會在 HTTP Header 的 Authorization 中傳送 Line LIFF 產生的 JWT Token (格式為 Bearer <id_token>)。 請幫我寫一個依賴注入函數 (Dependency) verify_line_token(request: Request)。 邏輯如下: 1. 從 request.headers 取得 Authorization 字串,並擷取出 token。 2. 如果沒有 token,拋出 HTTPException 401。 3. 使用 httpx 套件發送 POST 請求到 Line 驗證端點:https://api.line.me/oauth2/v2.1/verify 4. 參數需要夾帶 id_token 和 client_id (我的 LIFF ID,放在環境變數 LINE_LIFF_ID 中)。 5. 如果 Line 回傳錯誤,拋出 HTTPException 401 拒絕存取。 6. 如果驗證成功,回傳包含使用者姓名和 line_id (sub) 的字典檔。

🤖 AI 的終極防偽驗證中心:

import os
import httpx
from fastapi import Request, HTTPException, status, Depends

async def verify_line_token(request: Request):
    """
    這個函數就像是公司門口的「虹膜掃描機」。
    任何想要打卡的 API 請求,都必須先通過這個函數的檢驗!
    """
    # 1. 從 Header 中抽出通行證
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="缺少安全憑證或格式錯誤"
        )
    
    # 抽出 "Bearer " 後面的那串亂碼
    id_token = auth_header.split(" ")[1]
    client_id = os.getenv("LINE_LIFF_ID")

    # 2. 拿著這張通行證,去問 Line 警察總部:「這張是真的嗎?」
    # 使用非同步的 httpx (取代舊的 requests,不會卡死伺服器)
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.line.me/oauth2/v2.1/verify",
            data={
                "id_token": id_token,
                "client_id": client_id
            }
        )
        
    payload = response.json()

    # 3. 判斷驗證結果
    if response.status_code != 200 or "error" in payload:
        # 如果 Line 說這張是假證件,立刻把駭客踢出去!
        print("❌ Token 驗證失敗:", payload.get("error_description"))
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="憑證失效或無效,請重新登入"
        )
        
    # 4. 驗證成功!Line 伺服器會告訴我們這張證件本人的真實身份
    # payload 裡面會包含 sub (Line 使用者的唯一 ID)、name (姓名)、picture (頭像)
    print(f"✅ 歡迎光臨,真實身分確認為:{payload.get('name')}")
    
    return {
        "line_id": payload.get("sub"),
        "name": payload.get("name"),
        "picture": payload.get("picture")
    }

🛡️ 實戰 4:打造滴水不漏的打卡 API (結合 Dependency)

現在我們有了虹膜掃描機 (verify_line_token)。我們要把這台機器安裝在打卡的打卡鐘前面。 在 FastAPI 中,這叫做 Dependency Injection (依賴注入)。這也是 FastAPI 之所以強大且優雅的原因。

💡 Vibe Prompt 實戰 4:串接驗證與打卡路由

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

請幫我寫一個打卡的 API 端點:POST /api/punch。 需求: 1. 使用 FastAPI 的 Depends 將剛才的 verify_line_token 注入為參數 current_user。 2. 定義一個 Pydantic BaseModel 作為接收資料的格式 (包含 action: str,值只能是 'in' 或 'out')。 3. 當請求進來時,印出:"[{current_user['name']}] 執行了打卡動作:{action}"。 4. 回傳成功的 JSON,包含使用者名稱與打卡時間。

🤖 AI 完美結合依賴注入的打卡路由:

from fastapi import APIRouter, Depends
from pydantic import BaseModel
from datetime import datetime

# 建立 Pydantic 資料模型:這是第二層防護罩,用來檢查前端傳來的資料格式對不對
class PunchRequest(BaseModel):
    action: str  # 'in' 代表上班, 'out' 代表下班

# 我們可以把路由獨立出來管理
router = APIRouter()

# 🚀 終極安全的打卡端點
@router.post("/api/punch")
async def handle_punch(
    # 這裡發生了兩件事:
    # 1. 檢查前端傳來的 body 格式對不對 (PunchRequest)
    request_data: PunchRequest, 
    
    # 2. 🔥 神級防護:強制這個請求必須先經過虹膜掃描機 (verify_line_token)!
    # 如果掃描失敗,請求在這裡就會直接被擋掉報錯,根本進不到後面的邏輯。
    # 如果掃描成功,current_user 就會拿到使用者的真實姓名與 ID。
    current_user: dict = Depends(verify_line_token)
):
    action_type = request_data.action
    user_name = current_user["name"]
    line_id = current_user["line_id"]
    
    now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # 記錄打卡行為
    # 在真實系統中,我們會在下一章把這段寫入 Supabase 資料庫
    print(f"🎯 [打卡成功] {user_name} ({line_id}) 執行了 {action_type}。時間:{now_str}")

    return {
        "status": "success",
        "message": f"打卡成功!歡迎,{user_name}。",
        "punch_time": now_str,
        "action": action_type
    }

🔍 深度解析: 這是企業級後端架構的精髓:「職責分離」。 在傳統的 PHP 程式碼中,你可能會在 /api/punch 裡面寫了 100 行驗證 Token 的邏輯,再寫 50 行檢查格式的邏輯,最後才寫打卡邏輯。程式碼變得又長又臭。 而在 FastAPI 的設計中:

  • PunchRequest 負責擋下格式錯誤的瞎子。
  • Depends(verify_line_token) 負責擋下沒有通行證的駭客。
  • 主體函數 handle_punch 只專心做一件事:寫入打卡紀錄。 這不僅讓程式碼可讀性極高,未來如果要加一個「請假 API」,你只要再掛上 Depends(verify_line_token),一秒就能獲得一模一樣的安全防護!

🗄️ 實戰 5:串接 Supabase 雲端資料庫 (Async 寫入)

現在我們確認了使用者的身份,我們要正式把他的打卡紀錄寫進資料庫了。 跟我們在網頁端用的 supabase-js 不太一樣,在 Python 中,我們使用的是 supabase-py。由於 FastAPI 是非同步架構,我們必須確保資料庫的連線不會「阻塞」其他幾千人的打卡。

💡 Vibe Prompt 實戰 5:撰寫安全高效的資料庫寫入函數

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我需要在 FastAPI 中把打卡紀錄寫入 Supabase 的 "punch_logs" 資料表。 請幫我寫一個初始化 Supabase 客戶端的程式碼 (使用 os.getenv 取得 url 和 key)。 接著,寫一個 async 函數 record_punch_to_db(line_id: str, action: str)。 邏輯: 1. 將 line_id, action, 並且附上現在的 timestamp,寫入 Supabase。 2. 使用 supabase.table("punch_logs").insert(...).execute()。 3. 請加上完整的 try...except 防呆保護,確保資料庫斷線時不會讓 API 當機崩潰,並印出錯誤訊息。

🤖 AI 的高階非同步資料庫模組:

import os
from supabase import create_client, Client
from datetime import datetime

# 1. 建立 Supabase 連線引擎
# (注意:Python 版本的 supabase-py 目前底層還是同步的,
# 但我們把它包在 async 函數中,或是使用 postgrest-py 的 async 版本,這裡示範最穩定的通用寫法)
url: str = os.getenv("SUPABASE_URL")
key: str = os.getenv("SUPABASE_SERVICE_KEY") # ⚠️ 後端伺服器可以使用特權 Key 來繞過 RLS

try:
    supabase_db: Client = create_client(url, key)
    print("✅ Supabase 資料庫連線成功!")
except Exception as e:
    print("❌ 資料庫連線失敗,請檢查環境變數:", e)

# 2. 建立防呆寫入函數
async def record_punch_to_db(line_id: str, action: str) -> bool:
    """
    將打卡紀錄安全地寫入雲端資料庫。
    回傳 True 代表寫入成功,False 代表寫入失敗。
    """
    try:
        # 準備要塞進資料庫的包裹
        data_packet = {
            "user_id": line_id,       # 關聯到員工表的 ID
            "action": action,         # 'in' 或 'out'
            "created_at": datetime.utcnow().isoformat() # 使用 UTC 國際標準時間是後端潛規則!
        }

        # 執行寫入動作
        response = supabase_db.table("punch_logs").insert(data_packet).execute()
        
        print(f"📦 紀錄已成功寫入資料庫!資料 ID: {response.data[0]['id']}")
        return True
        
    except Exception as e:
        # 如果 Supabase 掛了,或是憑證過期,我們在這裡攔截爆炸!
        print(f"🚨 [嚴重錯誤] 無法寫入 Supabase 資料庫:{str(e)}")
        # 你甚至可以在這裡加一行寄發警報 Email 給工程師的程式碼!
        return False

🔍 深度整合: 現在,你只需要把這段 await record_punch_to_db(line_id, action_type) 放進剛才的 handle_punch 路由裡面。 當王小明按下打卡按鈕 ➡️ FastAPI 瞬間解開 JWT 憑證 ➡️ 抓出王小明的真實身分 ➡️ 將身分與時間打包成包裹寫入 Supabase ➡️ 系統回傳「打卡成功」。 這整個極度複雜、防偽、且高併發的流程,在 FastAPI 的加持下,只需要不到 0.05 秒就能完成!這就是為什麼它可以承受萬人並發的原因!


✅ 本章總結與除錯心法

在這個長達 6000 字的高階架構章節中,我們完成了一個企業級的後端核心。

回顧你建立的無敵防線:

  1. ngrok 內網穿透:讓你的筆電瞬間變成可以接收 Line 訊號的公網伺服器。
  2. CORS 白名單:架設警衛,嚴禁未授權的網站隨便呼叫你的 API。
  3. JWT 虹膜驗證:絕對不信任前端傳來的身分,強制送到 Line 官方伺服器做二次驗證。
  4. FastAPI 依賴注入 (Depends):將複雜的驗證邏輯模組化,保護核心業務邏輯的乾淨。
  5. Supabase 容錯寫入:即使資料庫連線中斷,也要確保系統不會陷入當機死鎖。

現在,我們的系統可以記錄「誰」在「什麼時候」打卡了。 但是,這是一個人性本惡的世界。 如果有個聰明的員工,他在家裡的床上,早上 8:59 用手機連上你的打卡網頁,按下了打卡按鈕。系統會判定他準時上班! 這對公司來說是不可接受的!

在真正的百萬級考勤系統中,我們必須加入**「Anti-Cheat 防作弊機制」**。 下一章:第十二章:Anti-Cheat 防作弊機制:定位驗證與假 GPS 防護,我們將教你如何用 Vibe Coding 寫出最強的幾何數學演算法,利用前端回傳的經緯度與後端的中心點比對,甚至阻擋使用「虛擬定位 (Fake GPS)」的員工!準備好迎接真正的挑戰了嗎?我們下章見!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!