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ại | Nguồn sự thật | www đọc | Bảng D1 cũ (conan-platform-production) |
|---|---|---|---|
| Tin tức (15 bài) | CMS, collection news | api-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 announcements | api-cms (conan-web PR 6, #14) | announcements, announcement_categories: giữ, là bảng đa trường |
| Playbook (70 bài) | CMS, collection playbooks | còn đọc api.conan.school cho tới conan-web PR 9 | legacy_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
| Route | Trạng thái | Còn ai gọi | Làm gì |
|---|---|---|---|
GET /api/public/content/news, /news/{slug} | cũ, header Deprecation từ 17.10.2026 | không ai (www đọc api-cms); là đường quay lui của www (EDITORIAL_SOURCE) cho tới conan-web PR 10 | gỡ ở PR 13 |
GET /api/public/content/playbooks, /playbooks/{slug} | cũ, header Deprecation từ 17.10.2026 | www (/playbooks, sitemap-library) cho tới conan-web PR 9 | gỡ ở 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, review | xong. 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ễn | conan.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:
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"Deprecationtheo RFC 9745 (@<giây unix>),Sunsettheo RFC 8594 (HTTP-date),Linktrỏ đú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_hitvớiroute,path,user_agent,cf_worker(Worker khác gọi thì Cloudflare gắn header này), và host củaorigin/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 trongEDITORIAL_DEPRECATION; testnpm run test:contractkiể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_FALLBACK | true | api-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:
- 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_SOURCElà 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ờiSunsetđể có đủ 30 ngày tính từ lúc www thôi gọi/content/playbooks*. - Workers Logs không có dòng
deprecated_route_hitnào trong 14 ngày gần nhất (trừ lượt kiểm tay đã biết). email_orch_playbook_sourcevớireason: cms_empty_fallback_d1haycms_error_fallback_d1không xuất hiện trong 14 ngày gần nhất (Nhịp thư đã thật sự chạy bằng CMS).wrangler d1 insights --time-period 30dkhông thấy Worker nào khác (sutucon, Simba, Rafiki, garage-api, admin.inside6, chuyenchon) đọc bốn bảnglegacy_seo_*hay hai bảnglegacy_news_*(khuôn d1-cleanup).
Việc của PR 13:
- Gỡ bốn route
/content/news*,/content/playbooks*khỏisrc/domains/public/content.tscùngEDITORIAL_DEPRECATION; gỡnewsList,newsDetail,playbookList,playbookDetailkhỏipackages/api-contract(và bản chép ở conan-web cùng ngày, skillapi-contract). - Gỡ
PLAYBOOK_D1_FALLBACKvà nhánh D1 củagenerators/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ùngARCHIVED.tsv); sửa bất biến đêmplaybook-wrong-familyvà phần tin củaslug-not-ascii(scripts/lib/d1-invariants-catalog.mjs), danh sách giữ củatests/d1-cleanup.test.mts. generated/legacy*sinh lại; tài liệu này, skillplaybook-pack,seo.md,api-contract.mdcậ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_hitcủ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_d1phải biến mất. - Gọi từ Worker này sang
api-cms.conan.school(cùng zoneconan.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à ghicms_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.