Nhật ký công việc
Tổng kết công việc
Nhiệm vụ đọc các phiên (session) frontend MODX hiện có ở phía haih-agent hầu như đã được giải quyết và có thể coi là hoàn thành.
Những gì đã được kiểm tra
Ở phía frontend mới, chúng tôi đã có thể đọc trực tiếp dữ liệu phiên MODX mà không cần bootstrap MODX và không cần gọi endpoint legacy modSociety. Điều này xác nhận khả năng kiến trúc về việc sử dụng MODX làm kho lưu trữ phiên hiện tại, trong khi việc phân tích cú pháp và xử lý dữ liệu tiếp theo sẽ được thực hiện bên trong haih-agent.
Ngoài ra, vấn đề kỹ thuật chính hóa ra không nằm ở việc đọc bản ghi phiên từ cơ sở dữ liệu, mà nằm ở việc giải tuần tự hóa (deserialize) payload phiên PHP của nó trong Node.js.
Vấn đề với php-serialize
php-serialize không giải quyết trực tiếp vấn đề này vì phiên MODX/PHP ở định dạng được sử dụng không chỉ đơn giản là kết quả của serialize($_SESSION). Do đó, thư viện này không phù hợp làm bộ giải mã phiên có sẵn cho trường hợp hiện tại.
Sử dụng php-session-unserialize
Để phân tích cú pháp phiên, gói php-session-unserialize đã được sử dụng. Nó đọc chính xác định dạng phiên PHP, nhưng một điểm đặc biệt trong cách triển khai của thư viện đã được phát hiện: các mảng kết hợp (associative array) của PHP bên trong readArray() luôn được tạo dưới dạng Array trong JavaScript.
Về cơ bản, thư viện thực hiện những việc sau:
const resultArray = []
resultArray[key] = value
Nếu mảng PHP chứa khóa chuỗi (string key), ví dụ như mgr, kết quả trong JavaScript là một mảng có thuộc tính được đặt tên (arr.mgr = ...). Trong console.log, dữ liệu này có thể nhìn thấy, nhưng JSON.stringify và GraphQL chỉ tuần tự hóa các phần tử mảng có chỉ mục (indexed element), do đó các thuộc tính có tên sẽ bị mất.
Ví dụ thực tế: giá trị phiên MODX dạng
modx.user.0.resourceGroups => { mgr: [] }
sau khi phân tích cú pháp ban đầu trông có vẻ chính xác trong log, nhưng qua GraphQL lại biến thành:
"modx.user.0.resourceGroups": []
Giải pháp tạm thời (Workaround) đã triển khai
Sau hàm unserialize(), một quá trình chuẩn hóa đệ quy kết quả đã được thêm vào:
function convertArraysToObjects(obj: unknown): unknown {
if (Array.isArray(obj)) {
const keys = Object.keys(obj)
const hasStringKeys = keys.some((k) => isNaN(Number(k)))
if (hasStringKeys) {
const result: Record<string, unknown> = {}
for (const key of keys) {
result[key] = convertArraysToObjects(
(obj as unknown as Record<string, unknown>)[key],
)
}
return result
}
return obj.map(convertArraysToObjects)
}
if (obj && typeof obj === 'object') {
const result: Record<string, unknown> = {}
for (const [key, value] of Object.entries(obj)) {
result[key] = convertArraysToObjects(value)
}
return result
}
return obj
}
Sau bước này, các mảng kết hợp PHP có khóa dạng chuỗi được chuyển đổi thành các đối tượng JS thông thường và đi qua JSON/GraphQL một cách chính xác.
Kết quả
Sau khi chuẩn hóa, dữ liệu phiên MODX được đọc chính xác, bao gồm cả các cấu trúc lồng nhau. Cụ thể, các nhánh sau đã được lấy thành công:
{
"modx.user.0.resourceGroups": {
"mgr": []
},
"modx.user.0.attributes": {
"web": {
"modAccessContext": {
"web": [
{
"principal": 0,
"authority": "0",
"policy": {
"load": true,
"formit": true,
"formit_encryptions": false
}
}
]
}
}
}
}
Ngoài ra, dữ liệu cho các người dùng/ ngữ cảnh khác cũng hiển thị trong phiên, ví dụ như modx.user.1.attributes và các cấu trúc ACL với tập hợp quyền quản lý (manager permissions) lớn. Tức là payload phiên giờ đây đã có sẵn hoàn toàn và không bị mất mát trong quá trình tuần tự hóa GraphQL.
Hạn chế của giải pháp hiện tại
Về mặt lý thuyết, bộ chuyển đổi hiện tại có thể chuyển đổi không rõ ràng đối với mảng PHP có cả khóa số và khóa chuỗi hỗn hợp: khi có ít nhất một khóa chuỗi, toàn bộ mảng JS sẽ biến thành Đối tượng (Object). Đối với các cấu trúc phiên MODX thông thường, điều này hiện không quá quan trọng; dữ liệu thực tế cần thiết trong dự án vẫn đến chính xác sau khi chuẩn hóa.
Do đó, ở giai đoạn này không cần thiết phải viết một trình phân tích cú pháp phiên PHP riêng hoặc fork thư viện. Nếu sau này gặp phải một payload phiên MODX thực tế nào mà sơ đồ hiện tại phân tích không chính xác, vấn đề đó có thể được tách thành một nhiệm vụ kỹ thuật riêng biệt.
Phạm vi của nhiệm vụ đã hoàn thành
Mục tiêu của nhiệm vụ này chính là khả năng cốt lõi trong việc đọc và giải tuần tự hóa chính xác phiên frontend MODX hiện có ở phía haih-agent. Mục tiêu này đã đạt được.
Logic xác định người dùng hiện tại cụ thể dựa trên nội dung phiên, lựa chọn ngữ cảnh frontend cần thiết, lấy dữ liệu người dùng bổ sung và tiếp tục xây dựng API auth/currentUser là lớp ứng dụng tiếp theo và nếu cần thiết, nên được định hình riêng biệt.
Bản sửa lỗi này đã giúp loại bỏ khoảng trắng ngắt dòng giữa các thẻ:
$html = preg_replace('/>\s+</', '><', $html);
Ngoài ra, tôi đã thêm đoạn này để gỡ lỗi (debugging) nhằm có thể xem HTML được tạo ra trước khi nó được chuyển vào trình kết xuất PDF, giúp xem chính xác nội dung nào sẽ được xuất ra.
if($this->getProperty('debug_html')){
header('Content-Type: text/html; charset=utf-8');
echo $html;
exit;
}
Nhân tiện, một đính chính quan trọng: mặc dù modxSite triển khai API, các yêu cầu không sử dụng dữ liệu json làm đầuเข้า, mà sử dụng multipart/form-data cổ điển
Bây giờ, trong API GraphQL mới của chúng ta, chúng ta cần kiểm tra việc proxy và xử lý yêu cầu.
Tôi đã thêm bản nháp resolver và ngay lúc này tôi có thể kiểm tra xem các yêu cầu có đến trình xử lý MODX hay không và liệu chúng ta có nhận được phản hồi hay không.
Ở đây tôi thấy một phản hồi chính xác ở định dạng JSON cho biết tôi không có quyền truy cập khi cố gắng tạo một yêu cầu.

Nghĩa là bản thân yêu cầu đã được gửi đến trang MODX và được xử lý chính xác. Bây giờ cần phải thêm tính năng ủy quyền người dùng.
Trong tài khoản cá nhân, chúng ta cần tìm cookie phiên hiện tại. Nó có thể được tìm thấy trong phần tiêu đề yêu cầu

hoặc đơn giản hơn là chuyển đến tab Ứng dụng (Application) và xem trong danh sách cookie chung.

Bây giờ cookie này có thể được thêm như một tiêu đề tùy chỉnh trong GraphQL playground của API mới của chúng ta.

Bây giờ, khi gửi yêu cầu kèm theo cookie ủy quyền, chúng ta nhận được phản hồi với danh sách các lỗi trong quá trình xác thực dữ liệu biểu mẫu.

Vậy là xong. Có thể coi như bộ khung của API đã sẵn sàng. Chỉ còn lại việc mô tả các tham số yêu cầu và phản hồi là có thể tích hợp vào frontend.
Dự án này là một cơ hội tuyệt vời để gợi lại những kỹ năng cũ và vui mừng khi thấy một số thứ đã được thiết kế tiện lợi như thế nào từ thời đó :-)
Tôi xin nhắc lại rằng các công cụ hữu ích chính từng được sử dụng trên trang web khi đó và sẽ giúp ích cho chúng ta là console, modxSite và modxSmarty.
Tại sao và bằng cách nào chúng lại giúp ích? Đã được viết tại đây.
Và ngay lúc này, tôi đang kiểm chứng điều đó trong thực tế.
Đầu tiên là việc tạo yêu cầu cấp thẻ ra vào và lấy dữ liệu này. Ở đây, công lớn thuộc về modxSite vì nó cho phép tạo các bộ xử lý (processors) riêng để làm việc với dữ liệu, đi kèm quyền truy cập API có sẵn ngay từ đầu. Thế là trong tài khoản cá nhân ở phía frontend, tôi mở devTools và xem yêu cầu tạo:

Ở đây tôi thấy bộ xử lý nào đang được gọi — passes/visitors/create — và dữ liệu nào đang được truyền đi. Tức là ta đã có thể ném cái này vào Postman hoặc công cụ tương tự để thực hiện các truy vấn.
Nhưng lúc này chúng ta sử dụng thêm một công cụ nữa — console. Và mặc dù tôi đã không viết gì ở đó trong nhiều năm qua, tôi thậm chí còn chẳng phải viết lại gì cả. Nó có tính năng xuất và nhập mã, và ở đây tôi chỉ việc chọn từ danh sách đoạn mã gỡ lỗi đã lưu trước đó. Thế là xong. Có thể chạy trực tiếp ngay trong trang quản trị MODX :-)

Ưu điểm của cách tiếp cận này là logic hoàn toàn tách biệt với giao diện trình bày. Nghĩa là ở đây chúng ta chỉ thao tác với các yêu cầu API, truyền dữ liệu thô và nhận lại dữ liệu thô. Sau đó, chúng ta muốn định dạng phản hồi theo cách nào tùy ý. Trước đây, phản hồi này được xử lý bởi một ứng dụng JavaScript trên trang web MODX, nhưng bây giờ nó sẽ được xử lý trên trang web mới của chúng ta với các công nghệ hoàn toàn mới. Và chúng ta sẽ không cần phải chỉnh sửa bất cứ thứ gì trong MODX cả. Trang web mới chỉ đơn giản là cần biết phải gửi yêu cầu API đi đâu là đủ.
Vì ngay cả trong trung hạn, toàn bộ logic nghiệp vụ vẫn sẽ xoay quanh chính MODX, nhưng phần giao diện (frontend) phải hoạt động hoàn toàn mượt mà trên các công nghệ mới, nên chúng tôi thực hiện đồng bộ dần các tài khoản người dùng vào cơ sở dữ liệu của cỗ máy mới. Cụ thể là khi có yêu cầu từ người dùng hiện tại kèm theo cookie phiên MODX của họ, chúng tôi chỉ cần kiểm tra phiên đó trên backend mới của mình, lấy thông tin người dùng MODX và tạo một người dùng liên kết phía chúng tôi. Đồng thời, chúng tôi thậm chí không cần lưu trữ mật khẩu hay kiểm tra quyền truy cập, vì việc xác thực vẫn diễn ra ở MODX và tất cả các chính sách phân quyền tiếp tục hoạt động ở cấp độ MODX, do mọi thứ ban đầu đều được xây dựng dựa trên API, nên tại đây chúng tôi sẽ chỉ đóng vai trò proxy cho các yêu cầu. Còn ở phía giao diện, chúng tôi chỉ cần biết người dùng đã đăng nhập hay chưa.
Đã sửa lỗi này. Vấn đề là agent đang làm sai định dạng trong tài liệu yaml. Phản hồi dạng văn bản trả về như sau:
en:
name: |
Growth in Specialist Productivity Requires Corresponding Development of the Business Technological Environment
description: |
Business gets the maximum out of new technologies and strong specialists only when its own technological environment allows realizing their productivity.
content: |
New technologies increase specialist productivity, but this productivity is not realized in a vacuum.
Ở đây, description và content không được lồng vào bên trong en giống như name, mà lại trở thành các phần mới. Kết quả là chúng ta nhận được cấu trúc phản hồi cuối cùng như sau:
{
en: {
name: 'Growth in Specialist Productivity Requires Corresponding Development of the Business Technological Environment\n'
},
description: 'Business gets the maximum out of new technologies and strong specialists only when its own technological environment allows realizing their productivity.\n',
content: 'New technologies increase specialist productivity, but this productivity is not realized in a vacuum.
Nói cách khác, chỉ có tiêu đề được dịch ở đây, còn mọi thứ khác đều không được xử lý.
Cuối cùng, tôi đã làm hai việc:
-
Tách chúng thành các tin nhắn riêng biệt: prompt hệ thống với tất cả các quy tắc được đặt riêng, trong khi tài liệu cần dịch được đưa vào tin nhắn người dùng. Với cách tiếp cận này, cấu trúc dữ liệu logic đối với llm sẽ dễ phân biệt hơn.
-
Củng cố các quy tắc định dạng yaml.
Kết quả thu được như sau:
const fieldsYaml = fieldsToTranslate
.map(
({ field, value }) =>
`${field}: |\n${value
.split('\n')
.map((line) => ' ' + line)
.join('\n')}`,
)
.join('\n')
const fieldNames = fieldsToTranslate.map((f) => f.field)
const systemPrompt = `You are a professional translator specializing in technical documentation and web content.
# YAML OUTPUT RULES (CRITICAL)
You MUST output valid YAML with this EXACT structure:
\`\`\`
<lang_code>:
<field_name>: |
<translated text line 1>
<translated text line 2>
\`\`\`
**STRICT REQUIREMENTS:**
1. Each language code (en, de, etc.) MUST be at the ROOT level (no indentation)
2. Each field (name, description, content) MUST be indented with exactly 2 spaces under its language
3. Field values MUST use the literal block scalar (|) syntax
4. Text content MUST be indented with exactly 4 spaces (2 for field + 2 for content)
5. NEVER put fields at the root level - they MUST always be nested under a language code
# TRANSLATION RULES
1. **Translate, do not transliterate.** Convert meaning, not just letters.
2. Only include fields that were provided in the source.
3. Preserve all markdown and HTML formatting exactly.
4. Exception: Proper nouns, brand names, company names, and product names should be transliterated to Latin script.
# OUTPUT FORMAT
Respond ONLY with valid YAML. No markdown code blocks, no explanations, no comments - just raw YAML.`
const userPrompt = `Translate the following fields from Russian into: ${targetLangs.join(', ')}
## Source fields:
${fieldsYaml}
## Expected output structure:
${targetLangs
.map(
(lang) =>
`${lang}:\n${fieldNames.map((f) => ` ${f}: |\n <translated ${f}>`).join('\n')}`,
)
.join('\n')}`
const chatResponse = await llmChatCompletionResolver(
null,
{
input: {
provider: LlmProvider.OpenRouter,
messages: [
{
role: LLMChatMessageRole.system,
content: systemPrompt,
},
{
role: LLMChatMessageRole.user,
content: userPrompt,
},
],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
model: LlmModel.GEMINI_3_5_FLASH_LITE as any,
},
},
ctx,
)
const responseContent = chatResponse.choices?.[0]?.message?.content
Công bằng mà nói, ở đây các hướng dẫn kỹ thuật vẫn bị lẫn vào tin nhắn của người dùng một phần, nhưng dù sao thì nó cũng đã hoạt động ổn định hơn rất nhiều.
Trong quá trình phân tích sơ bộ dự án, đã xác nhận rằng kilfor.ru đã được đóng gói trong Docker, và phần MODX được xây dựng trên ngăn xếp độc quyền modxSite + modxSmarty + Console. Điều này đưa ra một giả định làm việc rằng frontend mới có thể được kết nối với logic ứng dụng hiện có với ít sự can thiệp vào backend hơn và ít rủi ro hơn so với một dự án legacy MODX điển hình. Giả định này sẽ được kiểm tra dựa trên mã nguồn thực tế và các processors hiện có.
Hiểu biết hiện tại về kiến trúc đã được ghi lại riêng: https://fi1osof.ru/concepts/kilfor-current-architecture
Тут чисто локальные случаи. Это не на всех уроках, а только отдельных. Маркдаун падает из-за кусков кода в контенте. Пока есть задачи важнее.
Một thành tựu đáng kể ngay lúc này - đã lọc ra được phần lớn các trang rác.

Sự tồn tại của chúng không hẳn là một vấn đề kỹ thuật quá lớn, nhưng dù sao Google vẫn tốn phần lớn hạn ngạch (crawl budget) cho chúng, khiến cho các trang có liên quan và chất lượng cao hơn bị lập chỉ mục chậm hơn đáng kể. Tôi không thể biết chính xác, nhưng tôi nghĩ điều này nhìn chung gây ra ảnh hưởng tiêu cực đến thái độ của Google đối với trang web. Hơn nữa, Yandex cũng đã từ lâu thay thế chỉ số TCI (Chỉ số trích dẫn theo chủ đề) bằng SQI (Chỉ số chất lượng trang web). Và chỉ số đó của chúng ta ở bên đó cũng đang tiếp tục giảm. Cần phải cải thiện tình trạng kỹ thuật của trang web.
Trang web solopreneur.prof đã được ra mắt. Hồ sơ riêng của Lira đã được tạo, lõi ngữ nghĩa đầu tiên của dự án đã được chuẩn bị và một tập hợp các Khái niệm (Concepts) cơ bản đã được công bố về solopreneur của tương lai, tính tự chủ, chuỗi cung ứng ngắn, tăng cường bằng AI, chi phí phụ thuộc bên ngoài, tích lũy năng lực, thị trường mới, mạng lưới solopreneur và mối liên hệ giữa chủ nghĩa solopreneur với chủ nghĩa tương lai thực tiễn. Khái niệm chính "Solopreneur của tương lai là một nhà tương lai học hành động" được đặt tại URI /. Các liên kết nội bộ và kết nối trực tiếp với futurist.expert và fi1osof.ru đã được bổ sung.
Trang web futurist.expert đã được ra mắt. Các phiên bản tiếng Nga và tiếng Anh đã được thiết lập. Quá trình cập nhật nội dung theo chủ đề đã bắt đầu: một số Khái niệm cơ bản về nhà tương lai học kiểu mới, chủ nghĩa tương lai thực tiễn, tư duy hệ thống, AI, công nghệ lớn (big tech) và chủ nghĩa khởi nghiệp cá nhân (solopreneurship) đã được tạo; các bản dịch đã được thêm vào cho một số tài liệu. Lõi ngữ nghĩa gắn kết đầu tiên của trang web đã được hình thành và việc liên kết nội bộ các Khái niệm đã bắt đầu.
Đã hoàn thành triển khai
Nút chỉnh sửa sản phẩm nhanh và thanh công cụ quản trị (toolbar) đã được triển khai.
Cách thực hiện kiểm tra ủy quyền
Ban đầu, phương án truyền MODX cookie vào chính trang web MODX và nhận kết quả kiểm tra phiên hiện tại từ trang web đó thông qua các cơ chế ủy quyền riêng của nó đã được cân nhắc.
Phương án này đã bị loại bỏ vì quá dư thừa đối với nhiệm vụ hiện tại: nó sẽ đòi hỏi cấu hình môi trường bổ sung, kiến thức về địa chỉ trang web MODX và thêm một tương tác mạng nữa.
Trong lần triển khai hiện tại, cookie được lấy từ các tiêu đề (headers) của yêu cầu đến và được xác thực trực tiếp thông qua client hiện có sẵn kết nối với cơ sở dữ liệu hiện tại.
Đối với frontend mới, hiện tại chưa cần đến một đối tượng MODX user hoàn chỉnh. Chỉ cần một dấu hiệu đáng tin cậy cho thấy phiên quản trị tồn tại để hiển thị thanh công cụ bổ sung.
Các thao tác quản trị cuối cùng vẫn được thực hiện trong MODX Manager. Khi chuyển hướng đến đó, MODX sẽ kiểm tra lại phiên làm việc và quyền của người dùng của chính nó, vì vậy việc kiểm tra mới trên frontend không phải là ranh giới bảo mật cuối cùng.
Tại sao lại chọn thỏa hiệp này
Quyết định được đưa ra theo nguyên tắc cân bằng giữa chi phí và tính năng:
- sử dụng client đã có sẵn kết nối với cơ sở dữ liệu hiện tại;
- không giới thiệu thêm các environment variables bổ sung;
- không cần yêu cầu riêng biệt đến MODX;
- frontend chỉ nhận tín hiệu boolean mà nó cần;
- việc kiểm tra quyền cuối cùng vẫn ở lại trong MODX Manager;
- tính năng này chỉ dành cho quản trị viên, do đó các sự cố có thể xảy ra sẽ nhanh chóng lộ diện thông qua phản hồi.
Việc triển khai hiện tại được coi là đủ cho nhiệm vụ. Nếu sự tích hợp quản trị giữa frontend mới và MODX bắt đầu mở rộng, cơ chế kiểm tra phiên có thể được xem xét lại và chuyển thành một auth bridge trang trọng hơn.
Quan sát và Quyết định về việc Chỉnh sửa Sản phẩm Quản trị
Những gì đã tìm hiểu
Phần giao diện công khai mới của HappyBaby hoạt động tách biệt với MODX, nhưng MODX vẫn là backend làm việc và môi trường quản trị hiện có.
Đối với nút chỉnh sửa nhanh, việc xây dựng một trang quản trị thứ hai trong frontend mới là không hợp lý. Ở giai đoạn đầu, việc sử dụng MODX Manager hiện có và cung cấp cho quản trị viên khả năng chuyển hướng nhanh đến việc chỉnh sửa sản phẩm hiện tại là chính xác hơn.
Vấn đề kỹ thuật chính hóa ra không nằm ở bản thân nút bấm, mà ở việc xác định quyền ủy quyền quản trị trên frontend mới.
Trên thực tế, cookie MODX cần thiết đã tồn tại và được trình duyệt gửi trong các yêu cầu của frontend mới. Điều này có nghĩa là hiện tại không cần tạo thêm hệ thống ủy quyền hoặc đồng bộ hóa người dùng MODX với cơ sở dữ liệu haih-agent.
Hướng đi đã chọn
Thêm một truy vấn GraphQL riêng biệt trả về người dùng MODX hiện tại dựa trên phiên/cookie MODX hiện có.
MODX vẫn là nguồn sự thật duy nhất (source of truth) cho việc ủy quyền này. Người dùng MODX ở giai đoạn này:
- không được chuyển sang cơ sở dữ liệu haih-agent;
- không được đồng bộ hóa với mô hình User thông thường;
- không nhận phiên thứ hai riêng biệt;
- chỉ đơn giản được trả về dưới dạng người dùng MODX hiện tại bên ngoài nếu phiên bản MODX gốc hợp lệ.
Nếu GraphQL trả về người dùng MODX hiện tại, frontend coi khách truy cập là người dùng quản trị và hiển thị thanh công cụ quản trị (admin toolbar).
Giao diện người dùng (UI)
Tốt hơn hết là không trộn lẫn hành động quản trị với các nút mua hàng của thẻ sản phẩm. Lựa chọn ưu tiên là một thanh công cụ quản trị / lớp hành động quản trị có thể tái sử dụng riêng biệt, chỉ hiển thị khi có người dùng MODX hiện tại.
Hành động đầu tiên của thanh công cụ:
- Chỉnh sửa sản phẩm → mở tài nguyên (resource) tương ứng trong MODX Manager.
Thanh công cụ này sau đó có thể được tái sử dụng cho các hành động quản trị khác và các loại trang khác mà không cần tạo trang quản trị mới.
Những gì cần thiết để triển khai
- Thêm truy vấn GraphQL cho người dùng MODX hiện tại thông qua phiên/cookie gốc.
- Chỉ trả về dữ liệu người dùng frontend cần thiết; không nhúng người dùng đó vào mô hình User cục bộ.
- Yêu cầu người dùng MODX hiện tại trên frontend.
- Nếu có người dùng, hiển thị thanh công cụ quản trị.
- Đối với thẻ sản phẩm, tạo liên kết để chỉnh sửa tài nguyên MODX tương ứng.
- Đảm bảo rằng ID tài nguyên MODX gốc có sẵn cho sản phẩm; nếu chưa, hãy thêm nó vào dữ liệu sản phẩm.
Tại sao tùy chọn này được chọn
Nó sử dụng hệ thống quản trị và ủy quyền hiện có, không trùng lặp MODX ACL và không tạo ra chu trình xác thực bổ sung chỉ vì một tính năng duy nhất. Đồng thời, frontend nhận được tín hiệu tối thiểu cần thiết cho UI quản trị và vẫn duy trì mối liên kết lỏng lẻo với mô hình người dùng nội bộ của MODX.
Đang tiến hành đo đạc. Tên miền từ năm 2000. Nội dung trên trang web đã có ít nhất vài tháng. Yandex cách nào đó đã thêm được 3 trang vào chỉ mục.

Hãy cùng xem các biện pháp được áp dụng trong những ngày gần đây sẽ mang lại kết quả nhanh đến mức nào.
Tiến độ: Đã triển khai xác thực và chuẩn hóa phía máy chủ cho nội dung Concept
Phần chính của nhiệm vụ đã được triển khai trong commit: https://github.com/haih-net/agent/commit/5d2a3c1
Đã thêm:
- mutation
validateConceptsđể tìm kiếm các liên kết nội bộ bị hỏng; - tích hợp kiểm tra liên kết vào
createConceptvàupdateConcept; buildValidUrisSet()cho tập hợp các URI hợp lệ;validateInternalLinks()với phân tích AST Markdown/MDX và hỗ trợ tự động sửa lỗi (auto-fix);normalizeMarkdownContent()để chuẩn hóa MDX/Markdown trước khi lưu;- tài liệu kỹ năng (skill documentation) cho
validateConcepts; write: truechỉ khả dụng cho người dùng sudo, người dùng thông thường chỉ nhận được kết quả xác thực và tự sửa liên kết thủ công.
Do đó, máy chủ hiện bắt đầu đảm bảo tính toàn vẹn của nội dung Concept được quản lý bởi AI, thay vì chỉ dựa vào tính chính xác của văn bản do người dùng hoặc agent tạo ra.
Bối cảnh kiến trúc được ghi lại trong Concept: Sistema chelovekoponyatnyh URI v haih-agent sohranyaet stabilnost adresov i SEO-signaly.
Commit lân cận 8afc203 thực hiện tái cấu trúc ConceptItem: chế độ xem danh sách (list-view) bắt đầu dựa trên intro/description, và trường kỹ thuật type đã bị xóa khỏi phần hiển thị công khai chính của Concept. Đây không phải là phần chính của nhiệm vụ, nhưng phù hợp với định hướng tách biệt phân loại hệ thống khỏi nội dung công khai mang tính ngữ nghĩa.
Tiến độ: Đã triển khai bản vá
Các bản sửa lỗi xử lý URL đã được triển khai lên môi trường production. Dự kiến việc này sẽ khắc phục được phần lớn các lỗi 404 giả mạo liên quan đến URL được mã hóa phần trăm (percent-encoded) và việc thiếu giải mã.
Bước kiểm soát bắt buộc tiếp theo — sau khi tích lũy các yêu cầu mới — là kiểm tra lại nhật ký nội bộ để tìm lỗi 404 và đảm bảo rằng sự cố hàng loạt thực sự đã biến mất, trong khi các lỗi 404 còn lại chỉ thuộc về các trường hợp riêng lẻ.
Để thực hiện việc này, một tác vụ con liên quan đã được tạo với lịch hoàn thành vào ngày 29 tháng 8 năm 2026.
Làm rõ về việc giải mã URI
Sau khi kiểm tra, hóa ra decodeURI(asPath) không giải quyết được tất cả các trường hợp cần thiết.
Ví dụ cụ thể:
/projects/%40prisma-cms/sendmail
được kỳ vọng là:
/projects/@prisma-cms/sendmail
Nhưng decodeURI() lại giữ nguyên %40, bởi vì @ thuộc về các ký tự URI được bảo lưu và toàn bộ URI không được giải mã hoàn toàn.
Đối với URI của các thực thể, việc xử lý đường dẫn theo từng phân đoạn (segment) sẽ chính xác hơn:
- Đầu tiên, tách pathname theo
/, giữ nguyên cấu trúc định tuyến. - Cho từng phân đoạn đi qua
decodeURIComponent()một cách riêng biệt. - Ráp lại đường dẫn.
Bằng cách này, chúng ta nhận được kết quả giải mã mong muốn %40 -> @, mà không gặp rủi ro là một ký tự %2F được mã hóa bên trong một slug đơn lẻ sẽ sớm biến thành dấu / cấu trúc và làm thay đổi số lượng phân đoạn tuyến đường.
Nói cách khác, logic đích phải nhận biết phân đoạn (segment-aware), ví dụ về mặt khái niệm:
const uri = pathname
.split('/')
.map(segment => decodeURIComponent(segment))
.join('/')
Điều này cũng tương xứng với cách triển khai hiện tại của slugifyUri(), vốn đã hoạt động theo từng phân đoạn.
decodeURI(asPath) hiện tại có thể được coi là một bản sửa lỗi tạm thời: nó giải quyết các trường hợp như %22, nhưng không xử lý %40 và các ký tự được bảo lưu khác nếu chúng là một phần của slug/URI thực thể.
Lỗi URIError riêng biệt không được xem xét ở đây như một vấn đề của nhiệm vụ này: các lỗi kỹ thuật liên quan đến URL bị lỗi định dạng (malformed URL) phải được xử lý riêng.
Tiến độ: bản vá đã được thực hiện và mở rộng đến nền tảng CNC (ChPU)
Việc triển khai đã được thực hiện trong commit: https://github.com/haih-net/agent/commit/1f99a51dd407eb4e58f9e2f83f411a265db2f24c
Các URI đến giờ đây được giải mã thông qua decodeURI tại các điểm quan trọng khi làm việc với router.asPath, giúp loại bỏ lỗi 404 giả cho các trang hiện có chứa các ký tự mã hóa phần trăm (percent-encoded).
Đồng thời, công việc đã được mở rộng ra nền tảng URI chung của haih-agent: đã thêm tính năng tạo slugified URI từ tên khái niệm và tự động chuyển hướng 301 khi thay đổi URI. Điều này được thực hiện như một nền tảng cho việc chuyển đổi các ứng dụng dựa trên haih-agent sang các URL thân thiện với con người.
Phần tiếp theo thực tế là SEO/GEO của fi1osof.ru, nơi các URL nhiệm vụ và dự án hiện tại đang sử dụng CUID kỹ thuật. Nhiệm vụ liên quan: Nâng cấp SEO/GEO.
Tiến độ: Đã đặt nền móng cho URL thân thiện với SEO (ЧПУ) trong haih-agent
Một trong những hướng có thể cản trở SEO đã được xác định: các URL công khai của fi1osof.ru được xây dựng xung quanh các CUID kỹ thuật (/tasks/<id>, /projects/<id>) và không chứa ngữ nghĩa của trang.
Vì fi1osof.ru hoạt động dựa trên haih-agent, nền tảng cốt lõi đã được cải tiến trước tiên. Commit https://github.com/haih-net/agent/commit/1f99a51dd407eb4e58f9e2f83f411a265db2f24c đã triển khai:
- slugify các URI dễ đọc đối với con người;
- tạo URI từ tên của concept;
- chuyển hướng
301từ URI cũ khi nó thay đổi; - logic chung cho các quy tắc chuyển hướng (redirect rules);
decodeURIđể đọc chính xác các địa chỉ được mã hóa theo dạng phần trăm (percent-encoded).
Giai đoạn tiếp theo là cập nhật các thay đổi vào fi1osof.ru và áp dụng URL thân thiện với SEO cho các thực thể được lập chỉ mục, trước hết là URL của task/project, đồng thời duy trì các liên kết cũ thông qua redirects/canonical.
Task cơ sở liên quan: Sửa lỗi giải mã URL trong haih-agent.