KuroCMS DevLog Vol.5: Implemetation of Recipe Type
My implementuvaly novu funktsiyu kartky retseptu v KuroCMS na osnovi vidhukiv. Chitayte pro tekhnichnu arkhitekturu, heneratsiyu JSON-LD ta intehratsiyu.
У „KuroCMS“ та „KuroEditor“, розроблених як безголова CMS нового покоління, що працює на edge, у найновішій версії було офіційно впроваджено нову „картку рецепту“. У той час як попередні версії KuroCMS фокусувалися на загальному представленні документів за допомогою markdown та стандартного HTML, введення цієї картки рецепту дозволяє суворе поводження з метаданими, такими як час приготування, списки інгредієнтів, порції та кроки, гарантуючи послідовний вихід у вигляді структурованих даних, які пошукові системи на кшталт Google можуть легко зрозуміти (Schema.org Recipe). У цій статті ми детально описуємо технічний дизайн цієї картки рецепту та показуємо, як перевірка виконується на рівнях редактора, API та фронтенду.
Філософія дизайну та база: Розумна модель даних, обмежена 1 документом = 1 рецептом
При впровадженні картки рецепту найбільшим викликом, з яким зіткнулася команда розробників, була „узгодженість даних та усунення надмірності“. У той час як загальні сайти рецептів часто мають кілька карток рецептів, розкиданих в одній статті, це схильне створювати зламану структуру з точки зору структурованих даних пошукових систем (JSON-LD). Якщо кілька різних страв або кроків відображаються паралельно для однієї URL-адреси (статті), відповідність між загальними властивостями (такими як назва страви, готове зображення, опис та дата публікації) та індивідуальними картками рецептів стає неоднозначною.
Тому KuroCMS накладає правило „1 документ = 1 рецепт“. RecipeCard відповідає лише за „змінні частини, специфічні для рецепту“ (порції, час приготування, інгредієнти та кроки), тоді як загальна інформація, така як назва страви, опис, готове зображення та дата публікації, повторно використовується з властивостей документа (назва, резюме, зображення обкладинки, дата створення тощо). Це запобігає надлишковому введенню та запобігає проблемам, коли однакові назви рецептів повторюються у зламаному форматі в межах тих самих структурованих даних.
Чесно кажучи, з точки зору дизайну KuroEditor, ми не хотіли приймати обмеження 1 документ = 1 рецепт, оскільки воно обмежує гнучкість макета. Однак, оскільки сторінки рецептів користувачів отримують вищі оцінки SEO та їх легше читати й посилатися, ми суворо дотримуємося правила однієї картки рецепту на статтю в поточній версії.
Гарантія узгодженості за допомогою спільних чистих функцій
У безголовій CMS виконання ідентичної перевірки структури в розширеному редакторі (KuroEditor), API збереження та процесі статичного збирання публічних сторінок є непростим завданням. Якщо ви пишете логіку перевірки окремо, використовуючи різні мови або фреймворки на кожному рівні, розбіжності в реалізації неминуче призведуть до проблем, таких як „збережено в редакторі, але не вдалося на сервері API“, або „збережено в базі даних, але не вдалося під час фронтенд-збірки, зламавши веб-сайт“.
Щоб вирішити це фундаментально, KuroCMS прийняла модель проектування „спільної чистої функції“ (Shared Pure Function). Зокрема, ми інтегруємо „dist/kuro-recipe.js“ (створений у висхідному репозиторії KuroEditor) у ядро KuroCMS під назвою „src/kuro-recipe.js“, разом із файлом визначення типу „kuro-recipe.d.ts“. Це гарантує, що той самий код JavaScript перевіряє дані рецепту в тілі HTML під час редагування, викликів API збереження та статичного збирання. Така конструкція з єдиним джерелом істини (Single Source of Truth) мінімізує помилки.
*Оскільки KuroEditor і KuroCMS є "програмним забезпеченням з відкритим вихідним кодом", розробленим Kuroboo, звернення до історії GitHub полегшить розуміння.
Сувора перевірка в API збереження: Специфікації recipe-guard.ts
Коли стаття зберігається (PUT) в KuroCMS, API на стороні сервера не просто зберігає рядок. Він суворо перевіряє, чи правильно інтегровані дані рецепту в надіслане тіло HTML. Цю роль виконує функція „checkRecipeCards(bodyHtml, isRecipeType)“ у „src/recipe-guard.ts“ на бекенді.
Найбільш критичним аспектом цієї перевірки є час її проведення. KuroCMS враховує спільне редагування кількома користувачами, і остаточні збережені дані записуються в базу даних після „3-стороннього злиття“ (3-way merge) кількох версій. Якщо перевірка виконується до злиття, ми не можемо запобігти випадкам, коли злиття випадково дублює картки рецептів або вставляє пошкоджені дані. Тому перевірка завжди виконується на кінцевому тексті після злиття. Якщо перевірка не вдається, API повертає клієнту чітку помилку, блокуючи процес збереження.
| Елемент перевірки | Деталі та заходи безпеки |
|---|---|
| Кількість карток | Переконайтеся, що в статті рецепту є рівно 1 картка, а в статтях, що не є рецептами, — рівно 0 карток. |
| Дешифрування data-recipe | Дешифруйте закодовані дані JSON рецепту, інтегровані в тег HTML, і перевірте, чи відповідають вони схемі. |
| Allowlist атрибутів | Суворо перевіряйте невідомі атрибути HTML. Це усуває вразливості, коли зловмисники впроваджують XSS-скрипти на кшталт „onclick“ через текст тіла HTML. |
| Перевірка версії | Перевірте сумісність версії схеми рецепту, щоб запобігти збереженню старих версій карток або пошкоджених форматів. |
Вихід JSON-LD: Автоматична генерація структури Recipe для SEO
Це основна цінність додавання картки рецепту. Зазвичай KuroCMS генерує „Article“ як структуровані дані для сторінки, але коли документ містить картку рецепту, він автоматично замінює її та генерує „Recipe“ як основні структуровані дані.
Він зчитує атрибут „data-recipe“ із збереженого HTML і передає проаналізовані дані під змінними „article.recipe“ та „page.isRecipe“ в HTML-шаблон. Щоб уникнути „перевірки типу шляхом порівняння рядків“ у шаблоні, він використовує просту логічну гілку (#if page.isRecipe) для перемикання шаблонів. Часові дані автоматично конвертуються всередині у міжнародний стандарт ISO-8601 duration (наприклад, „PT25M“ для 25 хвилин). Оскільки HTML-відображення та структуровані дані JSON-LD генеруються з того самого єдиного джерела даних, немає ризику невідповідності інформації для пошукових систем.
Інтеграція адміністрування: Логіка динамічного відтворення редактора
Логіка інтеграції також була вбудована в область редактора панелі адміністрування. У KuroEditor активація або деактивація „кнопки Каструля“ для створення рецептів контролюється опцією „recipeUi“, що передається під час ініціалізації. Оскільки за замовчуванням поведінка KuroEditor — відображати кнопку (true), KuroCMS зазвичай не потрібно вказувати її, але якщо встановлено значення false, це виглядає без змін для користувачів, які не використовують KuroEditor для редагування рецептів, мінімізуючи вплив зміни.
Крім того, на рівні KuroEditor картки рецептів тепер заблоковані на 1 рецепт на статтю (друга кнопка неактивна і не може бути натиснута). Це зберігає основне правило рецептурних статей. KuroCMS також здійснює подвійну перевірку при збереженні, щоб переконатися, що перевірки, специфічні для рецептів, виконуються належним чином для статей з рецептами.
На цій основі було додано набір тестів, пов'язаних з рецептами „npm run test:recipe“ (src/recipe-guard.test.ts, 12 тестів усього), який перевіряє все — від кількості карток до екранування символів в атрибутах.
Завдання, що залишилися, та майбутня дорожня карта
З цим оновленням „основна система“ статті рецепту та „валідація збереження“ повністю запущені. Однак KuroCMS ще не пропонує повномасштабного макета сторінки рецепту, як великі портали. Нашою метою було дозволити легку публікацію рецептів на персональних веб-сайтах, генеруючи автоматично дані SEO без необхідності для користувача писати їх. Тепер кожен може легко запустити сайт про кулінарію.
Крім того, оскільки KuroCMS має систему шаблонів, користувачі можуть продовжувати вдосконалювати дизайн самостійно (див. Джерело 3 для деталей). Щоб зробити KuroCMS ще простішим у використанні, команда розробників продовжуватиме впроваджувати оновлення на основі ваших відгуків, тому дякуємо за вашу підтримку!