Skip to content

Deploy ​

Quy trình: push lên GitHub → Cloudflare tự build và deploy. Không chạy wrangler deploy hay wrangler pages deploy từ máy cá nhân.

Cloudflare Pages — frontend ​

Mỗi site frontend là một Pages project, kết nối với cùng một repo GitHub nhưng khác Root directory.

compass.conan1.com ​

Cấu hìnhGiá trị
Root directorycompass.conan1.com
Build commandnpm run build
Build outputdist
Node version22
Biến môi trườngVITE_API_BASE=https://api.conan1.com
Custom domaincompass.conan1.com

File public/_redirects đã có sẵn /* /index.html 200 để React Router hoạt động khi người dùng mở trực tiếp một URL con. Cả ba mặt phẳng Compass, Products, Requirements đều là route của SPA này (/products/*, /requirements/*); tách hostname riêng cho một mặt phẳng về sau chỉ là thêm một Pages project và một Access application.

docs.conan1.com ​

Cấu hìnhGiá trị
Root directorydocs.conan1.com
Build commandnpm run build
Build outputdocs/.vitepress/dist
Node version22
Custom domaindocs.conan1.com

b21.conan1.com ​

Cấu hìnhGiá trị
Root directoryb21.conan1.com
Build commandnpm run build
Build outputdist
Node version22
Custom domainb21.conan1.com

Công khai, không đặt sau Cloudflare Access. Dữ liệu đọc từ https://api.conan1.com/public/b21/* (migration 0022 + 0023, áp bằng wrangler d1 execute --remote --file=…). b21.nemo12.com chuyển 301 sang đây.

Cloudflare Worker — api.conan1.com ​

API là Worker chứ không phải Pages, vì binding D1 nằm ở Worker.

Tạo database (chạy một lần) ​

bash
cd api.conan1.com
npx wrangler d1 create conan-compass

Lệnh trên in ra database_id. Dán giá trị đó vào wrangler.jsonc, thay cho REPLACE_WITH_D1_DATABASE_ID.

Nạp schema và dữ liệu mẫu ​

bash
npx wrangler d1 export conan-compass --remote --output=backup.sql   # sao lưu trước
npm run db:remote                                                   # dựng lại toàn bộ

db:remote xoá và tạo lại mọi bảng — và chạy riêng 0005 / 0006 cũng xoá lại toàn bộ hai mặt phẳng Products và Requirements, không phải cộng thêm. Chỉ dùng khi production chưa có dữ liệu nhập tay; sau đó không chạy lại file nào từ 0001 đến 0006, mọi thay đổi phải là một file migration mới áp dụng riêng bằng wrangler d1 execute --remote --file=….

0007_requirement_depth.sql và 0008_requirement_depth_seed.sql là ví dụ đầu tiên của đường đi cộng thêm đó: CREATE TABLE IF NOT EXISTS, INSERT OR IGNORE, không DROP, không DELETE, không đặt lại sequences, không chạm bảng đã có — nên áp dụng được lên database đang chạy và chạy lại được mà không mất dòng nào do người nhập. Xem Chuyển dữ liệu.

Workers AI và AI Gateway ​

Trợ lý làm rõ yêu cầu dùng binding AI trong wrangler.jsonc — không có khoá API, không có secret:

jsonc
"ai": { "binding": "AI" },
"vars": {
  "AI_MODEL": "@cf/deepseek-ai/deepseek-v4-flash-0731",
  "AI_GATEWAY_ID": "conan1",
  "AI_DAILY_LIMIT": "50"
}

AI_DAILY_LIMIT là trần số lần gọi gợi ý mỗi ngày UTC cho cả Worker; 0 hoặc bỏ trống là không giới hạn, hết trần thì hai endpoint gợi ý trả 429 ai_daily_limit còn đường thủ công vẫn chạy. Nó là vars, không phải secret — đổi bằng một lần deploy.

AI Gateway conan1 được tạo trong Dashboard (AI → AI Gateway), bật Authenticated Gateway và nhật ký; request từ binding của Worker được xác thực tự động. Code không phụ thuộc vào gateway: AI_GATEWAY_ID rỗng thì gọi Workers AI trực tiếp; thiếu binding thì endpoint AI trả 503 và giao diện hiện AI chưa cấu hình. Chi tiết ở Làm rõ có AI hỗ trợ.

Kết nối GitHub ​

Trong Cloudflare Dashboard: Workers & Pages → Create → Worker → Connect to Git, chọn repo, đặt Root directory là api.conan1.com, deploy command npx wrangler deploy.

Sau lần deploy đầu tiên, thêm custom domain api.conan1.com (đã khai báo sẵn trong wrangler.jsonc dưới dạng routes).

Cloudflare Worker — mm.conan1.com ​

Markmap (chuyển từ mm.nemo12.com ngày 16.10.2026). Là Worker có static assets, không phải Pages, vì Worker phải chạy trước hai đường:

  • /api/* — chuyển sang Worker api-conan1 qua service binding API (bỏ tiền tố /api), nên trình duyệt chỉ nói chuyện với chính mm.conan1.com và cookie phiên mm_session là cookie first-party, host-only. Không dùng route mm.conan1.com/api/* trên Worker API như compass.conan1.com/v1/*: một hostname vừa là Custom Domain của Worker này vừa có route của Worker khác là câu hỏi thứ tự ưu tiên không cần trả lời; binding thì tường minh.
  • /m/:id — chèn thẻ og: (tiêu đề, mô tả) của đúng sơ đồ được share, vì Facebook/Zalo không chạy JavaScript. Pages Functions cũng làm được, nhưng giữ nguyên Worker của nguồn là ít thay đổi nhất.
Cấu hìnhGiá trị
LoạiWorker (Workers Builds, Connect to Git)
Root directorymm.conan1.com
Build commandnpm run build
Deploy commandnpx wrangler deploy
Node version22
Custom domainmm.conan1.com (khai trong wrangler.jsonc → routes)
Service bindingAPI → api-conan1 (khai trong wrangler.jsonc → services; Worker API phải deploy trước)

Công khai, không đặt sau Cloudflare Access. Người dùng là bất kỳ ai có tài khoản Google; họ nằm ở bảng mm_users riêng, không lẫn với users (vai trò Compass).

Phía API (api.conan1.com/src/routes/mm.ts, migration 0024_mm.sql):

  • Đường /mm/* và /public/mm/* nằm ngoài /v1/* nên không qua kiểm tra Access. POST /mm/auth/google kiểm ID token của Google (chữ ký theo JWKS của Google, iss, exp, email_verified, và aud = GOOGLE_CLIENT_ID), rồi đặt cookie mm_session (HttpOnly, Secure, SameSite=Lax, 30 ngày); DB chỉ giữ SHA-256 của token.
  • GOOGLE_CLIENT_ID là vars trong api.conan1.com/wrangler.jsonc (giá trị công khai, không phải secret), đang dùng chung OAuth client với mm.nemo12.com. Trong Google Cloud Console → APIs & Services → Credentials → client ấy → Authorized JavaScript origins phải có https://mm.conan1.com, nếu không nút Google sẽ báo lỗi origin. Đổi sang client riêng thì chỉ sửa var này; SPA đọc nó từ GET /api/mm/config, nên nút và phép kiểm aud không bao giờ lệch nhau.
  • DEV_BYPASS_EMAIL (chỉ trong .dev.vars) cho phép credential: "dev" hoặc "dev:<email>" đăng nhập khi chạy local. Production để rỗng nên nhánh này không chạy được.

Áp schema lên production (cộng thêm, chạy lại được):

bash
cd api.conan1.com
npx wrangler d1 export conan-compass --remote --output=backup.sql
npx wrangler d1 execute conan-compass --remote --file=migrations/0024_mm.sql

Chuyển dữ liệu từ mm.nemo12.com ​

  1. Xuất từ D1 production của Nemo12 (nemo12-platform) ra một file JSON đúng khuôn ghi ở đầu api.conan1.com/scripts/mm-import.mjs: users (chỉ những người có markmap: id, email, display_name, avatar_url) và markmaps (mọi cột). File chứa email: không commit.
  2. node scripts/mm-import.mjs export.json mm-import.sql — người dùng ghép theo email, id markmap giữ nguyên nên link /m/:id cũ vẫn chạy. INSERT OR IGNORE nên chạy lại không nhân đôi và không đè sửa đổi làm trên mm.conan1.com; nội dung dài được chèn theo khúc vì D1 giới hạn mỗi câu 100 KB. Trần 10 sơ đồ không áp cho lượt nhập.
  3. Thử trên local (--local --file=mm-import.sql), rồi áp lên production bằng --remote --file=mm-import.sql.
  4. Cho mm.nemo12.com/* chuyển 301 sang https://mm.conan1.com/* (cùng đường dẫn).

Cloudflare Access ​

Cả compass.conan1.com và api.conan1.com đều nằm sau Cloudflare Access.

  1. Zero Trust → Access → Applications → Add an application → Self-hosted.
  2. Tạo application cho compass.conan1.com và một application nữa cho api.conan1.com.
  3. Policy: Emails hoặc Email domain — danh sách người được vào.
  4. Bật Cookie settings → Same-site: None cho application của API nếu trình duyệt chặn cookie khi Compass gọi sang API (hai domain khác nhau nhưng cùng site conan1.com, thường không cần).
  5. Copy Application Audience (AUD) Tag của application API và đặt vào wrangler.jsonc → vars.ACCESS_AUD, cùng với ACCESS_TEAM_DOMAIN (dạng <team>.cloudflareaccess.com).

WARNING

Nếu ACCESS_AUD để rỗng, Worker vẫn kiểm tra chữ ký và hạn của JWT nhưng không kiểm tra token đó được phát cho đúng application nào. Hãy điền giá trị này trước khi dùng thật.

Phát triển tại máy ​

bash
cd api.conan1.com
npm install
npm run db:local
npm run dev
bash
cd compass.conan1.com
npm install
npm run dev

Khi chạy local, Worker không có Cloudflare Access. File .dev.vars đặt DEV_BYPASS_EMAIL để giả lập danh tính người dùng. Không bao giờ đặt biến này trên production.

wrangler dev chạy Workers AI từ xa bằng phiên đăng nhập Cloudflare sẵn có trên máy (npx wrangler login), nên các endpoint gợi ý AI hoạt động ngay tại local; không có phiên đăng nhập thì chúng trả 503 và phần còn lại của ứng dụng không bị ảnh hưởng.

Cloudflare Worker — agency.conan1.com ​

Site tĩnh VitePress, deploy bằng Workers static assets (chỉ asset, không có script) thay vì Pages vì tài khoản đã chạm giới hạn mềm số Pages project.

Cấu hìnhGiá trị
LoạiWorker agency-conan1 (Workers Builds, Connect to Git)
Root directoryagency.conan1.com
Build commandnpm run build
Deploy commandnpx wrangler deploy
Preview commandnpx wrangler preview (nhánh khác main; cần "previews": {} trong wrangler.jsonc)
Node version24 (build variable NODE_VERSION, và .nvmrc)
Assetsdocs/.vitepress/dist (khai trong wrangler.jsonc → assets)
Custom domainagency.conan1.com (khai trong wrangler.jsonc → routes)