KuroCMS DevLog Vol.5: Implementación del tipo Receta
Implementamos una nueva función de ficha de receta en KuroCMS basada en comentarios. Conozca la arquitectura, generación JSON-LD e integración del editor.
En "KuroCMS" y "KuroEditor", desarrollados como un CMS headless de próxima generación que funciona en el edge, se ha implementado oficialmente una nueva "ficha de receta" en la última versión. Mientras que las versiones anteriores de KuroCMS se centraban en la expresión general de documentos mediante markdown y HTML estándar, la introducción de esta ficha de receta permite un manejo estricto de metadatos como el tiempo de cocción, las listas de ingredientes, las porciones y los pasos, lo que garantiza una salida coherente como datos estructurados que los motores de búsqueda como Google pueden entender fácilmente (Schema.org Recipe). En este artículo, detallamos el diseño técnico de esta ficha de receta y mostramos cómo se aplica la validación en las capas del editor, la API y el frontend.
Filosofía de diseño y diseño base: Modelo de datos inteligente limitado a 1 documento = 1 receta
Al implementar la ficha de receta, el mayor desafío al que se enfrentó el equipo de desarrollo fue "la consistencia de los datos y la eliminación de la redundancia". Mientras que los sitios de recetas generales suelen tener varias fichas de recetas dispersas en un solo artículo, esto tiende a generar una estructura rota desde la perspectiva de los datos estructurados de los motores de búsqueda (JSON-LD). Si se muestran varios platos o pasos diferentes en paralelo para una sola URL (artículo), la correspondencia entre las propiedades comunes (como el nombre del plato, la imagen terminada, la descripción y la fecha de publicación) y las fichas de recetas individuales se vuelve ambigua.
Por lo tanto, KuroCMS impone una restricción de "1 documento = 1 receta". La RecipeCard solo es responsable de las "partes variables específicas de la receta" (porciones, tiempo de cocción, ingredientes y pasos), mientras que la información común como el nombre del plato, la descripción, la imagen terminada y la fecha de publicación se reutiliza a partir de las propiedades del documento (título, resumen, imagen de portada, fecha de creación, etc.). Esto evita entradas redundantes y previene problemas en los que nombres de recetas idénticos se repiten en un formato roto dentro de los mismos datos estructurados.
Sinceramente, desde la perspectiva del diseño de KuroEditor, no queríamos aceptar la restricción de 1 documento = 1 receta, ya que limita la flexibilidad de la maquetación. Sin embargo, debido a que las páginas de recetas de los usuarios obtienen evaluaciones SEO más altas y son más fáciles de leer y referenciar, aplicamos estrictamente la regla de una ficha de receta por artículo en la versión actual.
Garantía de consistencia mediante funciones puras compartidas
En un CMS headless, ejecutar una validación de estructura idéntica en el editor enriquecido (KuroEditor), la API de guardado y el proceso de compilación estática de las páginas públicas no es fácil. Si escribe la lógica de validación por separado utilizando diferentes lenguajes o frameworks en cada capa, las diferencias de implementación causarán inevitablemente problemas, como "guardado en el editor pero falló en el servidor API", o "guardado en la base de datos pero falló en la compilación estática del frontend, rompiendo el sitio web".
Para resolver este problema fundamentalmente, KuroCMS adoptó un patrón de diseño de "función pura compartida" (Shared Pure Function). Específicamente, integramos "dist/kuro-recipe.js" (creado en el repositorio ascendente de KuroEditor) en el núcleo de KuroCMS como "src/kuro-recipe.js", junto con el archivo de definición de tipo "kuro-recipe.d.ts". Esto asegura que el mismo código JavaScript inspeccione los datos de la receta en el cuerpo HTML durante la edición, las llamadas API de guardado y las compilaciones estáticas. Este diseño de fuente única de verdad (Single Source of Truth) minimiza los errores.
*Dado que tanto KuroEditor como KuroCMS son "software de código abierto" desarrollados por Kuroboo, consultar el historial de GitHub facilitará la comprensión.
Validación estricta en la API de guardado: Especificaciones de recipe-guard.ts
Cuando se guarda (PUT) un artículo en KuroCMS, la API del lado del servidor no solo almacena la cadena. Inspecciona estrictamente si los datos de la receta están correctamente integrados en el cuerpo HTML enviado. Este rol está a cargo de la función "checkRecipeCards(bodyHtml, isRecipeType)" en "src/recipe-guard.ts" en el backend.
El aspecto más crítico de esta validación es el momento. KuroCMS considera la edición colaborativa multiusuario, y los datos finales guardados se escriben en la base de datos después de una "fusión de 3 vías" (3-way merge) de varias versiones. Si la validación se ejecuta antes de la fusión, no podemos evitar casos en los que la fusión duplique accidentalmente las fichas de recetas o inserte datos rotos. Por lo tanto, la comprobación se ejecuta siempre en el texto final después de la fusión. Si la validación falla, la API devuelve un error claro al cliente, bloqueando el proceso de guardado.
| Elemento de inspección | Detalles y medidas de seguridad |
|---|---|
| Número de fichas | Confirme que existe exactamente 1 ficha en un artículo de receta, y exactamente 0 fichas en los artículos que no son recetas. |
| Decodificación de data-recipe | Decodifique los datos JSON de receta codificados e integrados en la etiqueta HTML y compruebe si cumplen con el esquema. |
| Allowlist de atributos | Compruebe estrictamente los atributos HTML desconocidos. Esto elimina las vulnerabilidades en las que usuarios maliciosos inyectan scripts XSS como "onclick" a través del texto del cuerpo HTML. |
| Verificación de versión | Verifique la compatibilidad de versión del esquema de receta para evitar guardar versiones antiguas de fichas o formatos dañados. |
Salida JSON-LD: Generación automática de la estructura Recipe para el SEO
Este es el valor fundamental de añadir la ficha de receta. Normalmente, KuroCMS genera "Article" como datos estructurados para la página, pero cuando un documento contiene una ficha de receta, la reemplaza automáticamente y genera "Recipe" como datos estructurados principales.
Lee el atributo "data-recipe" del HTML guardado y transmite los datos analizados bajo las variables "article.recipe" y "page.isRecipe" a la plantilla HTML. Para evitar "la comprobación de tipo por comparación de cadenas" en la plantilla, utiliza una simple rama booleana (#if page.isRecipe) para cambiar de plantilla. Los datos temporales se convierten automáticamente internamente al formato internacional ISO-8601 duration (por ejemplo, "PT25M" para 25 minutos). Dado que la visualización HTML y los datos estructurados JSON-LD se generan a partir de la misma fuente de datos única, no existe riesgo de información errónea para los motores de búsqueda.
Integración de administración: Lógica de recreación dinámica del editor
La lógica de integración también se ha incorporado en el área de edición del panel de administración. En KuroEditor, la activación o desactivación del "botón Olla" para la creación de recetas está controlada por la opción "recipeUi" pasada en la inicialización. Dado que el comportamiento por defecto de KuroEditor es mostrar el botón (true), KuroCMS no necesita especificarlo normalmente, pero si se establece en false, parece inalterado para los usuarios que no utilizan KuroEditor para la edición de recetas, minimizando el impacto del cambio.
Además, a nivel de KuroEditor, las fichas de recetas ahora están bloqueadas a 1 receta por artículo (el segundo botón está en gris y no se puede hacer clic). Esto preserva la regla principal de los artículos de recetas. KuroCMS también realiza una doble comprobación al guardar para asegurar que las comprobaciones específicas de recetas se ejecuten correctamente para los artículos de recetas.
Sobre esta base, se ha añadido una suite de pruebas relacionadas con las recetas "npm run test:recipe" (src/recipe-guard.test.ts, 12 casos de prueba en total), que valida todo, desde el número de fichas hasta el escape de caracteres en atributos.
Problemas restantes y hoja de ruta futura
Con esta actualización, el "sistema central" del artículo de receta y la "validación de guardado" están completamente operativos. Sin embargo, KuroCMS no ofrece todavía un diseño de página de receta completo como los grandes portales. Nuestro objetivo era permitir la publicación fácil de recetas en sitios web personales, generando automáticamente datos SEO sin que el usuario tenga que escribirlos. Ahora cualquiera puede lanzar un sitio de cocina.
Además, dado que KuroCMS tiene un sistema de plantillas, los usuarios pueden continuar puliendo el diseño por sí mismos (ver la Fuente 3 para más detalles). Para hacer KuroCMS aún más fácil de usar, el equipo de desarrollo continuará implementando actualizaciones basadas en sus comentarios, ¡así que gracias por su apoyo!