Skip to content

SDD: Thiết kế hệ thống ​

1. Kiến trúc tổng quan ​

             ┌────────────────────────┐
  Trình duyệt│ spa.anhdac.com         │ Cloudflare Pages (static, SPA)
  ──────────►│ Vite + React 19        │
             └──────────┬─────────────┘
                        │ fetch (credentials: include, cookie kmx_sid)
                        ▼
             ┌────────────────────────┐        ┌─────────────────────┐
             │ api.anhdac.com         │───────►│ Cloudflare D1       │
             │ Worker (Hono)          │        │ database: kmxspa    │
             └──────────┬─────────────┘        └─────────────────────┘
                        │ verify ID token (JWKS)
                        ▼
                accounts.google.com

             ┌────────────────────────┐
             │ docs.anhdac.com        │ Cloudflare Pages (VitePress, public)
             └────────────────────────┘

  GitHub dac2205/kmxspa ──push──► Cloudflare Pages / Workers Builds (build trên Cloudflare)

2. Công nghệ ​

LớpCông nghệ
FrontendVite 8, React 19, TypeScript, Tailwind CSS v4, shadcn/ui (Base UI), Motion (Framer Motion), React Router, Recharts, sonner
Auth (client)Google Identity Services qua @react-oauth/google
APICloudflare Workers, Hono, jose (xác thực JWT Google)
DatabaseCloudflare D1 (SQLite), migrations trong apps/api/migrations
DocsVitePress
Monorepopnpm workspaces
CI/CDCloudflare Pages Git integration + Workers Builds (không dùng GitHub Actions)

3. Cấu trúc repo ​

kmxspa/
├─ apps/spa     # spa.anhdac.com
│  └─ src/{pages,components,hooks,lib}
├─ apps/api     # api.anhdac.com
│  ├─ src/{index.ts,auth.ts,types.ts}
│  └─ migrations/*.sql
└─ apps/docs    # docs.anhdac.com

4. Luồng đăng nhập ​

  1. Trình duyệt hiển thị nút Sign in with Google; Google trả về ID token (JWT).
  2. SPA gửi POST /auth/google { credential }.
  3. Worker xác thực chữ ký qua JWKS của Google, kiểm tra aud = GOOGLE_CLIENT_ID, iss và email_verified.
  4. Upsert users; tạo token ngẫu nhiên 256-bit; lưu SHA-256(token) vào login_sessions kèm IP và thông tin request.cf.
  5. Trả cookie kmx_sid (HttpOnly, Secure, SameSite=Lax, Domain=.anhdac.com, 7 ngày).
  6. Mọi request /api/* kiểm tra session còn hạn, chưa logout và cập nhật last_seen_at.
  7. POST /auth/logout ghi logout_at và logout_reason='user'.
  8. Cron mỗi giờ đóng các phiên hết hạn (logout_reason='expired').

spa.anhdac.com và api.anhdac.com là same-site, nên cookie SameSite=Lax vẫn được gửi kèm fetch có credentials: "include". CORS chỉ cho phép các origin trong ALLOWED_ORIGINS.

5. Ghi dữ liệu ​

  • Endpoint chung PUT /api/:entity/:id với whitelist cột cho từng entity, kiểm tra kiểu và giới hạn giá trị.
  • Mỗi lần ghi cập nhật updated_at, updated_by và thêm một bản ghi vào change_log (before/after JSON).
  • Client cập nhật lạc quan (optimistic), rollback khi lỗi.

6. Bảo mật & quyền riêng tư ​

  • Không lưu token phiên gốc, chỉ lưu hash.
  • Không cần client secret Google (chỉ dùng ID token).
  • Dữ liệu IP/thiết bị chỉ admin xem được (ADMIN_EMAILS).
  • Màn hình đăng nhập thông báo rõ việc ghi nhận IP và thiết bị.

7. Chi phí ​

Dịch vụGóiGhi chú
Cloudflare Pages ×2Free500 build/tháng
Cloudflare WorkersFree100k request/ngày
Cloudflare D1Free5 GB, 5M row read/ngày
GitHubFreeRepo private, không dùng Actions
Google IdentityFree

KMX Spa · anhdac.com