Giao diện
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ình | Giá trị |
|---|---|
| Root directory | compass.conan1.com |
| Build command | npm run build |
| Build output | dist |
| Node version | 22 |
| Biến môi trường | VITE_API_BASE=https://api.conan1.com |
| Custom domain | compass.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ình | Giá trị |
|---|---|
| Root directory | docs.conan1.com |
| Build command | npm run build |
| Build output | docs/.vitepress/dist |
| Node version | 22 |
| Custom domain | docs.conan1.com |
b21.conan1.com
| Cấu hình | Giá trị |
|---|---|
| Root directory | b21.conan1.com |
| Build command | npm run build |
| Build output | dist |
| Node version | 22 |
| Custom domain | b21.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-compassLệ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 Workerapi-conan1qua service bindingAPI(bỏ tiền tố/api), nên trình duyệt chỉ nói chuyện với chínhmm.conan1.comvà cookie phiênmm_sessionlà cookie first-party, host-only. Không dùng routemm.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ình | Giá trị |
|---|---|
| Loại | Worker (Workers Builds, Connect to Git) |
| Root directory | mm.conan1.com |
| Build command | npm run build |
| Deploy command | npx wrangler deploy |
| Node version | 22 |
| Custom domain | mm.conan1.com (khai trong wrangler.jsonc → routes) |
| Service binding | API → 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/googlekiể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 cookiemm_session(HttpOnly, Secure, SameSite=Lax, 30 ngày); DB chỉ giữ SHA-256 của token. GOOGLE_CLIENT_IDlàvarstrongapi.conan1.com/wrangler.jsonc(giá trị công khai, không phải secret), đang dùng chung OAuth client vớimm.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ểmaudkhông bao giờ lệch nhau.DEV_BYPASS_EMAIL(chỉ trong.dev.vars) cho phépcredential: "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.sqlChuyển dữ liệu từ mm.nemo12.com
- Xuất từ D1 production của Nemo12 (
nemo12-platform) ra một file JSON đúng khuôn ghi ở đầuapi.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. 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/:idcũ vẫn chạy.INSERT OR IGNOREnên chạy lại không nhân đôi và không đè sửa đổi làm trênmm.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.- Thử trên local (
--local --file=mm-import.sql), rồi áp lên production bằng--remote --file=mm-import.sql. - Cho
mm.nemo12.com/*chuyển 301 sanghttps://mm.conan1.com/*(cùng đường dẫn).
Cloudflare Access
Cả compass.conan1.com và api.conan1.com đều nằm sau Cloudflare Access.
- Zero Trust → Access → Applications → Add an application → Self-hosted.
- Tạo application cho
compass.conan1.comvà một application nữa choapi.conan1.com. - Policy:
EmailshoặcEmail domain— danh sách người được vào. - 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). - Copy Application Audience (AUD) Tag của application API và đặt vào
wrangler.jsonc→vars.ACCESS_AUD, cùng vớiACCESS_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 devbash
cd compass.conan1.com
npm install
npm run devKhi 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ình | Giá trị |
|---|---|
| Loại | Worker agency-conan1 (Workers Builds, Connect to Git) |
| Root directory | agency.conan1.com |
| Build command | npm run build |
| Deploy command | npx wrangler deploy |
| Preview command | npx wrangler preview (nhánh khác main; cần "previews": {} trong wrangler.jsonc) |
| Node version | 24 (build variable NODE_VERSION, và .nvmrc) |
| Assets | docs/.vitepress/dist (khai trong wrangler.jsonc → assets) |
| Custom domain | agency.conan1.com (khai trong wrangler.jsonc → routes) |