Skip to content

Nội dung biên tập đã sang CMS (17.10.2026) ​

Giai đoạn 4 của Conan CMS đưa tin tức, thông báo và playbook của www sang cms.conan.school: soạn ở CMS, lưu ở D1 conan-cms, phục vụ bởi api-cms.conan.school (cả ba ở repo dac2205/conan-web). Trang này ghi phần còn ở repo này: route nào của api.conan.school thành đường cũ, bảng D1 nào đóng băng, ai còn đọc chúng, và kế hoạch gỡ ở PR 13. Bản thiết kế đầy đủ ở conan-web, cms-docs.conan.school/dev/migration-plan.md mục Giai đoạn 4.

Ai là nguồn sự thật ​

LoạiNguồn sự thậtwww đọcBảng D1 cũ (conan-platform-production)
Tin tức (15 bài)CMS, collection newsapi-cms (conan-web PR 4, #12)legacy_news_articles, legacy_news_categories: đóng băng, không đường ghi nào
Thông báo của Conan School (20 bài scope='school')CMS, collection announcementsapi-cms (conan-web PR 6, #14)announcements, announcement_categories: giữ, là bảng đa trường
Playbook (70 bài)CMS, collection playbookscòn đọc api.conan.school cho tới conan-web PR 9legacy_seo_content_briefs, legacy_seo_content_versions, legacy_seo_categories, legacy_seo_content_scores: đóng băng từ conan-content PR 7 (PlaybookPack thôi ghi)

Playbook mới: viết thẳng trong CMS, hoặc viết PlaybookPack ở conan-content rồi --target cms ra migration nháp trong conan-web (skill playbook-pack). Không còn đường nào ghi playbook vào D1 của repo này.

Route của api.conan.school ​

RouteTrạng tháiCòn ai gọiLàm gì
GET /api/public/content/news, /news/{slug}cũ, header Deprecation từ 17.10.2026không ai (www đọc api-cms); là đường quay lui của www (EDITORIAL_SOURCE) cho tới conan-web PR 10gỡ ở PR 13
GET /api/public/content/playbooks, /playbooks/{slug}cũ, header Deprecation từ 17.10.2026www (/playbooks, sitemap-library) cho tới conan-web PR 9gỡ ở PR 13, sau khi PR 9 deploy và log trống
GET /api/public/misc/home, khối articlesđã gỡ (PR 12)không ai: www, conan.media, inside6 build-seed.mjs chỉ đọc khoá học, workshop, reviewxong. Bớt ~60.777 lượt SELECT vào legacy_news_articles mỗi 31 ngày
GET /api/public/announcements*giữ vĩnh viễnconan.media (Pitstop School), inside6 seed (Inside School), Ran (mọi trường)không đổi. Chỉ 20 bài của Conan School chuyển đi

Cả hai mount (/api/public/... và /public/...) cùng mang header. Hình dạng phản hồi cũ (newsList, newsDetail, playbookList, playbookDetail trong packages/api-contract) là hợp đồng cũ, không thêm trường mới vào đó nữa; gỡ cùng route ở PR 13.

Header và log của route cũ ​

Middleware deprecatedRoutes (api.conan.school/src/shared/deprecation.ts), gắn ở src/domains/public/content.ts với một chỗ khai ngày EDITORIAL_DEPRECATION:

http
Deprecation: @1792195200
Sunset: Mon, 16 Nov 2026 00:00:00 GMT
Link: <https://api-cms.conan.school/public/playbooks/<slug>>; rel="successor-version", <https://docs.conan.school/platform/editorial-cms-cutover>; rel="deprecation"
  • Deprecation theo RFC 9745 (@<giây unix>), Sunset theo RFC 8594 (HTTP-date), Link trỏ đúng bài thay thế ở api-cms (route chi tiết trỏ bài chi tiết).
  • Mỗi lượt gọi ghi một dòng Workers Logs event: deprecated_route_hit với route, path, user_agent, cf_worker (Worker khác gọi thì Cloudflare gắn header này), và host của origin/referer (không query, không IP, không cookie). Đó là thứ PR 13 đọc để biết "không còn ai gọi".
  • Sunset = ngày merge PR 12 + 30 ngày. Merge muộn hơn 17.10.2026 thì dời cả hai ngày trong EDITORIAL_DEPRECATION; test npm run test:contract kiểm khoảng cách đúng 30 ngày.

Hai chỗ đọc chéo ở repo này ​

Email playbook_pick (Nhịp thư) đọc GET https://api-cms.conan.school/public/playbooks?locale=vi&limit=500 qua HTTP (src/shared/cms.ts, bộ sinh ở src/shared/email/orchestrator/generators/playbook.ts). Trước đó nó đọc thẳng legacy_seo_content_briefs, nên bài mới viết ở CMS không bao giờ vào thư, và bài sửa ở CMS gửi đi bản cũ. Hai hằng số:

Hằng sốGiá trịNghĩa
PLAYBOOK_SOURCE'cms'nguồn chính; quay lui = đổi thành 'd1' rồi push
PLAYBOOK_D1_FALLBACKtrueapi-cms rỗng hay lỗi thì đọc D1 cũ; PR 13 gỡ

Cho tới khi conan-web PR 8 (nhập 70 playbook) deploy, api-cms trả 0 playbook, nên Nhịp thư chạy bằng đường lùi và mỗi lượt lập kế hoạch ghi một dòng email_orch_playbook_source với reason: cms_empty_fallback_d1. Thấy dòng đó sau PR 8 là CMS mất bài, không phải chuyện bình thường. Các lý do khác: cms_error_fallback_d1 (mạng, hết giờ 5 giây, 5xx, collection 404, lệch hợp đồng), cms_error_skip / cms_empty_skip (không đường lùi: không gửi playbook nào), pool_empty_skip, cms_body_unreadable (thiếu đoạn trích, thư vẫn đi). Không bao giờ dựng thư rỗng: máy chọn chỉ sinh ứng viên cho bài có thật. stillValid lúc gửi kiểm ở cùng nguồn (danh sách CMS giữ 5 phút trong isolate, không gọi lại cho từng thư); bài gỡ khỏi CMS thì không gửi dù D1 còn.

Hợp đồng: src/shared/cms.ts giữ bản chép chỉ các trường api đọc của publicContracts.publicList / publicEntry (conan-web/packages/cms-contract/src/schemas.ts), không import chéo repo. npm run test:orchestrator so tên trường với bản gốc khi máy có repo anh em (CI không có, bỏ qua).

Ref của thư là playbook:<slug> ở cả hai nguồn: slug giữ nguyên lúc nhập, nên không ai nhận lại bài đã nhận.

Ran không lập chỉ mục tin tức hay playbook (src/workflows/ran-knowledge-sources.ts chỉ có nguồn announcements, 100 bài mới nhất của mọi trường, kèm school_id). Thông báo ở lại D1 vì là bảng đa trường, nên Ran không đổi ở PR 12. Ran vẫn lập chỉ mục bản đóng băng của 40 thông báo Conan School (cả 20 bài lớp); đưa nguồn CMS vào Ran là một quyết định riêng, chưa làm.

Kế hoạch PR 13: gỡ đường cũ ​

Ngày sớm nhất: 16.11.2026 (ngày Sunset, nếu PR 12 merge 17.10.2026; merge muộn thì cộng tương ứng). Chỉ làm khi cả bốn điều đúng:

  1. conan-web PR 8 (nhập playbook), PR 9 (www đọc playbook từ api-cms) và PR 10 (gỡ nhánh loader cũ cùng www.conan.school/app/lib/editorial-source.ts) đã deploy. Trước PR 10, route cũ là đường quay lui của www: đổi một chữ ở EDITORIAL_SOURCE là www đọc lại api.conan.school, nên gỡ route trước đó là cắt đường quay lui. Nếu PR 9 deploy muộn, dời Sunset để có đủ 30 ngày tính từ lúc www thôi gọi /content/playbooks*.
  2. Workers Logs không có dòng deprecated_route_hit nào trong 14 ngày gần nhất (trừ lượt kiểm tay đã biết).
  3. email_orch_playbook_source với reason: cms_empty_fallback_d1 hay cms_error_fallback_d1 không xuất hiện trong 14 ngày gần nhất (Nhịp thư đã thật sự chạy bằng CMS).
  4. wrangler d1 insights --time-period 30d không thấy Worker nào khác (sutucon, Simba, Rafiki, garage-api, admin.inside6, chuyenchon) đọc bốn bảng legacy_seo_* hay hai bảng legacy_news_* (khuôn d1-cleanup).

Việc của PR 13:

  • Gỡ bốn route /content/news*, /content/playbooks* khỏi src/domains/public/content.ts cùng EDITORIAL_DEPRECATION; gỡ newsList, newsDetail, playbookList, playbookDetail khỏi packages/api-contract (và bản chép ở conan-web cùng ngày, skill api-contract).
  • Gỡ PLAYBOOK_D1_FALLBACK và nhánh D1 của generators/playbook.ts.
  • Lưu legacy_news_*, legacy_seo_* vào R2 riêng tư conan-archive, rồi xoá bằng migration mới (tên không trùng ARCHIVED.tsv); sửa bất biến đêm playbook-wrong-family và phần tin của slug-not-ascii (scripts/lib/d1-invariants-catalog.mjs), danh sách giữ của tests/d1-cleanup.test.mts.
  • generated/legacy* sinh lại; tài liệu này, skill playbook-pack, seo.md, api-contract.md cập nhật thành "đã gỡ".

Không gỡ: announcements, announcement_categories, route /api/public/announcements*.

Còn chờ conan-web PR 8/9 ​

  • Playbook vẫn được www đọc từ route cũ, nên log deprecated_route_hit của /content/playbooks* không trống cho tới PR 9. Đó là chuyện đúng, không phải người gọi lạ.
  • Nhịp thư gửi playbook bằng đường lùi D1 cho tới PR 8. Sau PR 8 deploy, kiểm một lượt lập kế hoạch: dòng cms_empty_fallback_d1 phải biến mất.
  • Gọi từ Worker này sang api-cms.conan.school (cùng zone conan.school, custom domain) chưa từng chạy ở production trước PR 12. Lỗi ở đó (ví dụ một lỗi 52x của lượt gọi cùng zone) rơi về D1 và ghi cms_error_fallback_d1, không làm hỏng thư; đọc log sau lượt lập kế hoạch đầu tiên.

Xem thêm: known-risks, skill playbook-pack, Nhịp thư, hợp đồng API.

Tài liệu nội bộ nền tảng Conan School.