product Software Technology

KuroCMS DevLog Vol.5: Implémentation du type Recette

Nous avons implémenté une nouvelle fonctionnalité de fiche recette dans KuroCMS d'après les retours. Découvrez l'architecture, la génération JSON-LD et l'intégration de l'éditeur.


Dans "KuroCMS" et "KuroEditor", développés comme un CMS headless de nouvelle génération fonctionnant sur le edge, une nouvelle "fiche recette" a été officiellement implémentée dans la dernière version. Alors que les versions précédentes de KuroCMS se concentraient sur l'expression générale des documents à l'aide de markdown et de HTML standard, l'introduction de cette fiche recette permet un traitement strict des métadonnées telles que le temps de cuisson, les listes d'ingrédients, les portions et les étapes, garantissant une sortie cohérente sous forme de données structurées que les moteurs de recherche comme Google peuvent facilement comprendre (Schema.org Recipe). Dans cet article, nous détaillons la conception technique de cette fiche recette et montrons comment la validation est appliquée sur les couches de l'éditeur, de l'API et du frontend.

Philosophie de conception et base : Modèle de données intelligent limité à 1 document = 1 recette

Lors de l'implémentation de la fiche recette, le plus grand défi auquel l'équipe de développement a été confrontée était "la cohérence des données et l'élimination de la redondance". Alors que les sites de recettes généraux ont souvent plusieurs fiches recettes dispersées dans un seul article, cela a tendance à générer une structure brisée du point de vue des données structurées des moteurs de recherche (JSON-LD). Si plusieurs plats ou étapes différents sont affichés en parallèle pour une seule URL (article), la correspondance entre les propriétés communes (telles que le nom du plat, l'image finie, la description et la date de publication) et les fiches recettes individuelles devient ambiguë.

Par conséquent, KuroCMS impose une contrainte de "1 document = 1 recette". La RecipeCard n'est responsable que des "parties variables spécifiques à la recette" (portions, temps de cuisson, ingrédients et étapes), tandis que les informations communes telles que le nom du plat, la description, l'image finie et la date de publication sont réutilisées à partir des propriétés du document (titre, résumé, image de couverture, date de création, etc.). Cela évite les entrées redondantes et empêche les problèmes où des noms de recettes identiques se répètent dans un format brisé au sein des mêmes données structurées.

Honnêtement, du point de vue de la conception de KuroEditor, nous ne voulions pas accepter la contrainte de 1 document = 1 recette, car elle limite la flexibilité de la mise en page. Cependant, parce que les pages de recettes des utilisateurs obtiennent des évaluations SEO plus élevées et sont plus faciles à lire et à référencer, nous appliquons strictement la règle d'une seule fiche recette par article dans la version actuelle.

Garantie de cohérence par fonctions pures partagées

Dans un CMS headless, exécuter une validation de structure identique sur l'éditeur riche (KuroEditor), l'API de sauvegarde et le processus de build statique des pages publiques n'est pas facile. Si vous écrivez la logique de validation séparément en utilisant des langages ou des frameworks différents à chaque couche, les écarts d'implémentation entraîneront inévitablement des problèmes, tels que "sauvegardé dans l'éditeur mais a échoué sur le serveur API", ou "sauvegardé dans la base de données mais a échoué lors du build frontend, cassant le site web".

Pour résoudre ce problème fondamentalement, KuroCMS a adopté un modèle de conception de "fonction pure partagée" (Shared Pure Function). Plus précisément, nous intégrons "dist/kuro-recipe.js" (construit dans le dépôt en amont de KuroEditor) dans le cœur de KuroCMS sous le nom "src/kuro-recipe.js", aux côtés du fichier de définition de type "kuro-recipe.d.ts". Cela garantit que le même code JavaScript inspecte les données de recette dans le corps HTML lors de l'édition, des appels API de sauvegarde et des builds statiques. Cette conception à source unique de vérité (Single Source of Truth) minimise les bugs.

*Puisque KuroEditor et KuroCMS sont tous deux des "logiciels open-source" développés par Kuroboo, se référer à l'historique GitHub facilitera la compréhension.

Validation stricte dans l'API de sauvegarde: Spécifications de recipe-guard.ts

Lorsqu'un article est sauvegardé (PUT) dans KuroCMS, l'API côté serveur ne se contente pas de stocker la chaîne. Elle inspecte strictement si les données de la recette sont correctement intégrées dans le corps HTML envoyé. Ce rôle est géré par la fonction "checkRecipeCards(bodyHtml, isRecipeType)" dans "src/recipe-guard.ts" sur le backend.

L'aspect le plus critique de cette validation est son timing. KuroCMS prend en compte l'édition collaborative multi-utilisateur, et les données finales sauvegardées sont écrites dans la base de données après un "merge à 3 voies" (3-way merge) de plusieurs versions. Si la validation est exécutée avant la fusion, nous ne pouvons pas empêcher les cas où la fusion duplique accidentellement les fiches recettes ou insère des données brisées. Par conséquent, la vérification est toujours exécutée sur le texte final après la fusion. Si la validation échoue, l'API renvoie une erreur claire au client, bloquant le processus de sauvegarde.

Élément d'inspection Détails et mesures de sécurité
Nombre de fiches Confirmez qu'exactement 1 fiche existe dans un article de recette, et exactement 0 fiche existe dans les articles non-recettes.
Décryptage de data-recipe Décryptez les données JSON de recette encodées et intégrées dans la balise HTML et vérifiez si elles sont conformes au schéma.
Allowlist d'attributs Vérifiez strictement les attributs HTML inconnus. Cela élimine les vulnérabilités où des utilisateurs malveillants injectent des scripts XSS comme "onclick" via le texte du corps HTML.
Vérification de version Vérifiez la compatibilité de version du schéma de recette pour empêcher la sauvegarde d'anciennes versions de fiches ou de formats corrompus.

Sortie JSON-LD: Génération automatique de la structure Recipe pour le SEO

C'est la valeur fondamentale de l'ajout de la fiche recette. Normalement, KuroCMS génère "Article" comme données structurées pour la page, mais lorsqu'un document contient une fiche recette, il la remplace automatiquement et génère "Recipe" comme données structurées principales.

Il lit l'attribut "data-recipe" à partir du HTML sauvegardé et transmet les données analysées sous les variables "article.recipe" et "page.isRecipe" au modèle HTML. Pour éviter "la vérification de type par comparaison de chaînes" dans le modèle, il utilise une simple branche booléenne (#if page.isRecipe) pour changer de modèle. Les données temporelles sont automatiquement converties en interne au format international ISO-8601 duration (par exemple, "PT25M" pour 25 minutes). Étant donné que l'affichage HTML et les données structurées JSON-LD sont générés à partir de la même source de données unique, il n'y a aucun risque d'informations erronées pour les moteurs de recherche.

Intégration administrative: Logique de re-création dynamique de l'éditeur

La logique d'intégration a également été intégrée dans la zone d'édition du panneau d'administration. Dans KuroEditor, l'activation ou la désactivation du "bouton Marmite" pour la création de recettes est contrôlée par l'option "recipeUi" passée à l'initialisation. Étant donné que le comportement par défaut de KuroEditor est d'afficher le bouton (true), KuroCMS n'a pas besoin de le spécifier normalement, mais s'il est défini sur false, il apparaît inchangé pour les utilisateurs qui n'utilisent pas KuroEditor pour l'édition de recettes, ce qui minimise l'impact du changement.

De plus, au niveau de KuroEditor, les fiches recettes sont désormais verrouillées à 1 recette par article (le second bouton est grisé et ne peut être cliqué). Cela préserve la règle de base des articles de recettes. KuroCMS effectue également une double vérification lors de l'enregistrement pour s'assurer que les vérifications spécifiques aux recettes s'exécutent correctement pour les articles de recettes.

Sur cette base, une suite de tests liés aux recettes "npm run test:recipe" (src/recipe-guard.test.ts, 12 cas de test au total) a été ajoutée, validant tout, du nombre de fiches à l'échappement des caractères dans les attributs.

Problèmes restants et feuille de route future

Avec cette mise à jour, le "système central" de l'article de recette et la "validation de sauvegarde" sont entièrement opérationnels. Cependant, KuroCMS ne propose pas encore une mise en page de page de recette complète comme les grands portails. Notre objectif était de permettre la publication facile de recettes sur des sites web personnels, en générant automatiquement des données SEO sans que l'utilisateur ait à les écrire. Désormais, tout le monde peut facilement lancer un site de cuisine.

De plus, puisque KuroCMS dispose d'un système de modèles, les utilisateurs peuvent continuer à peaufiner la conception par eux-mêmes (voir la Source 3 pour plus de détails). Pour rendre KuroCMS encore plus facile à utiliser, l'équipe de développement continuera à mettre en œuvre des mises à jour basées sur vos commentaires, alors merci pour votre soutien !



【Sources】

  1. Code source officiel de KuroCMS et spécifications d'implémentation
  2. Définition officielle des données structurées Recipe (JSON-LD) sur Schema.org
  3. Docs de modèles publics 5: Fiche dédiée aux recettes (RECIPE)