Deploy lên Vercel¶
Runtime Python serverless của Vercel là môi trường production được khuyến nghị cho các cổng quy mô vừa và nhỏ: ngay cả free tier cũng xử lý được hàng nghìn học viên mỗi tháng.
Yêu cầu¶
- Tài khoản Vercel.
- Vercel CLI:
npm i -g vercel. - Một instance Upstash Redis hoặc Vercel KV (file SQLite trong
/tmplà ephemeral trên Vercel).
Bố cục project¶
lvt-cert init tạo cây tương thích Vercel:
my-portal/
├── cert.config.json
├── api/ # entrypoint serverless (Phase 15 tạo)
├── templates/
├── assets/fonts/
├── data/ # bundle cùng deploy
├── public_key.pem
├── requirements.txt
├── vercel.json
└── .gitignore
Phase 15 sẽ wire các handler
api/*.pymà Vercel gọi. Trong lúc chờ, hãy phản chiếu (mirror) các route Flask trongpackages/cli/luonvuitoi_cert_cli/server/app.py: mỗi handler là một wrapper một dòng bọc quanh hàm thuần lấy từluonvuitoi_cert.api.
Biến môi trường¶
Set trong dashboard Vercel hoặc qua vercel env add:
| Tên | Bắt buộc | Ghi chú |
|---|---|---|
JWT_SECRET |
có | 32+ ký tự random. |
ADMIN_DEFAULT_PASSWORD |
bootstrap | Dùng bởi seed script; xoay ngay. |
PUBLIC_BASE_URL |
có | Ví dụ https://mycerts.example. Ghim magic-link + URL QR chống Host-header injection. |
KV_BACKEND |
có | upstash hoặc vercel-kv (không bao giờ dùng local trên Vercel, vì /tmp chỉ là tạm thời). |
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN |
nếu KV_BACKEND=upstash |
|
KV_REST_API_URL / KV_REST_API_TOKEN |
nếu KV_BACKEND=vercel-kv |
Vercel auto-inject khi bạn link Vercel KV store. |
RESEND_API_KEY / CERT_EMAIL_FROM |
nếu admin.auth_mode là otp_email hoặc magic_link |
Thiếu key → fallback NullEmailProvider với warning; OTP bị drop âm thầm. |
GSHEET_WEBHOOK_URL |
tùy chọn | Phải https://…. Scheme khác bị reject với warning (guard SSRF). |
ALLOWED_ORIGINS |
khuyến nghị | Whitelist CORS, phân cách bằng dấu phẩy; ví dụ https://mycerts.example. Mặc định là *; hãy ghim lại ngay khi biết domain front-end. |
TRUST_PROXY_HEADERS |
có cho Vercel | Set 1. Vercel terminate TLS và forward X-Forwarded-For; không có flag này, rate limiter key theo IP proxy Vercel, mọi request share chung một bucket. |
FORCE_HSTS |
khuyến nghị | Set 1. Vercel là HTTPS-only; emit HSTS để browser từ chối downgrade. |
vercel.json¶
File scaffolded route root / admin / verify đến serverless handler và set timeout 30 giây mỗi invocation:
{
"rewrites": [
{ "source": "/", "destination": "/api/index" },
{ "source": "/admin", "destination": "/api/admin" },
{ "source": "/certificate-checker", "destination": "/api/certificate-checker" }
],
"functions": {
"api/*.py": { "maxDuration": 30 }
}
}
Pattern /tmp SQLite¶
Vercel runtime cung cấp /tmp writable mỗi warm container. Khi cold start, copy DB bundle vào đó:
_SRC = Path(__file__).parent.parent / "data" / "my-portal.db"
_TMP = Path("/tmp/my-portal.db")
def get_db_path() -> Path:
if not _TMP.exists():
shutil.copy2(_SRC, _TMP)
return _TMP
Admin mutation write vào /tmp và mirror qua Upstash/Vercel KV (xem luonvuitoi_cert.storage.kv) để cold start sau replay delta.
Deploy¶
Lần đầu prompt bạn link project và set env var. Deploy sau chỉ một lệnh.
Verify¶
Mở https://mycerts.example/admin và tạo admin đầu tiên với script one-off (xem Xác thực quản trị).
Lưu ý CSP¶
/admin phải emit Content-Security-Policy: script-src 'self' 'nonce-…'; default-src 'self'; frame-ancestors 'none'. Flask dev server làm tự động; wrapper handler Vercel phải replicate để sessionStorage JWT an toàn chống reflected XSS.
Logs¶
vercel logs stream các invocation gần đây. Lỗi admin và webhook retry đến đây qua module stdlib logging.