Architecture
Architecture de haut niveau et principaux modules techniques.
Vue d'ensemble
Yayaw utilise le Next.js App Router avec des routes préfixées par la locale
(/en, /fr). Le produit est organisé autour d'un tableau de bord multi-tenant
authentifié, d'un runtime CMS public et d'un plan de contrôle (control plane)
réservé au serveur pour l'automatisation de confiance.
Parcours de lecture clés:
Tableau de bord pour le shell applicatif authentifié et les racines de sections
Catalogue des pages, Catalogue des sections et Modèles de données CMS pour le runtime CMS
Aperçu de la facturation pour les abonnements, les achats ponctuels et l'accès au code
Plan de contrôle pour MCP et l'automatisation
Principaux blocs de construction
Frontend et routage
Routes applicatives dans
src/appMiddleware de locale et routage dans
src/i18netsrc/lib/middlewareBlocs UI du tableau de bord sous
src/blocksLa configuration partagée de routes/navigation pilote la barre latérale, le menu de commande, les fils d'Ariane et la visibilité des routes.
Convention de nommage du tableau de bord
Le titre de page définit le sujet de la page (par exemple:
Media,Billing,Authorization).Le titre de bloc définit l'entité gérée et implique l'action (par exemple:
Assets manager).La description de bloc explique, en une phrase courte, ce que les utilisateurs peuvent faire dans ce bloc.
Authentification et autorisation
Configuration Better Auth dans
src/config/better-auth.config.tsIntégration serveur auth dans
src/lib/server/services/auth/auth-drizzle.tsLes clés API Better Auth sont activées pour l'accès au plan de contrôle avec des permissions explicites
control-plane:read,control-plane:write,control-plane:publishetcontrol-plane:adminLes capacités d'administration Better Auth incluent l'usurpation contrôlée et le provisionnement SCIM managé et borné à l'organisation (
@better-auth/scim). Les credentials sont émis une seule fois depuis les réglages d'organisation; les groupes provisionnés ne créent jamais automatiquement de rôle ni de policy Yayaw.L'onboarding par invitation d'organisation privilégie la clé d'accès (passkey) et utilise des tokens d'onboarding d'invitation hachés, stockés hors de la table d'invitations Better Auth
Synchronisation des rôles d'organisation dans
src/lib/server/services/authzLe modèle d'autorisation interne reste dans Yayaw (Permix + Drizzle):
groupes et appartenances
bindings scopés explicites dans
group_role_bindingspolicies de rôles dans
role_policies
Moteur d'autorisation explicable dans
src/lib/server/authz:authorizeDetailed(...)pour autorisation/refus + chaîne de décisioncan(...)comme façade booléenne utilisée par les routes et les actions
Actions serveur personnalisées d'administration AuthZ pour liste/détail/bulk/simulate/explain/audit dans
src/lib/server/actions/authz/authorization-admin-actions.ts
Couche de données
Configuration Drizzle dans
src/lib/dbSchémas organisés par domaine:
schéma Better Auth
schéma d'autorisation
schéma système
Les parents de la barre latérale du tableau de bord sont des tableaux de bord de section navigables.
/dashboard/contentest l'écran du contenu; il résume l'état de publication de l'organisation active, les pages vues, le stockage média et ce qui demande attention avant que les utilisateurs ne descendent dans les écrans de catalogue dédiés./dashboard/admin,/dashboard/organization,/dashboard/settingset/dashboard/testsont aussi des racines de section valides; les parents de barre latérale ne pointent donc jamais vers des pages masquées ou manquantes. Le shell de tableau de bord utilise des fils d'Ariane shadcn et une barre de navigation de section partagée dérivés de la hiérarchie de barre latérale filtrée; cette barre liste uniquement les routes enfants de la section dashboard active.Médiathèque d'organisation dans le schéma système:
media_foldersetmedia_assetssont scopés par organisationles ressources média sont stockées dans un stockage objet public (bucket
media) avec unpublicUrlpersistéle stockage compatible S3 couvre MinIO, S3 et les déploiements de type R2
les assets visuels peuvent stocker des miniatures WebP générées pour l'affichage en médiathèque; les miniatures manquantes sont générées opportunistement lors du listage de la médiathèque et ne comptent pas dans le quota de téléversement utilisateur
les actions serveur appliquent toujours
scope: { orgId }et rejettent les mutations inter-organisationslecture/listage disponibles pour les membres d'organisation, téléversement/édition/suppression réservés aux rôles manager ou admin
les quotas de téléversement viennent de la configuration à l'exécution de facturation des plans (par fichier et stockage total de l'organisation)
la génération d'images du page-builder stocke les sorties WebP OpenAI par le même pipeline de téléversement/persistance média
Catalogue de sections réutilisables dans le schéma système:
ui_section_registry_itemsstocke l'identité stable des sections built-in, globales et d'organisationui_section_revisionsstocke le JSONSectionDefinitionV1validé + les diagnosticsui_section_publicationsstocke l'état de cycle de vie et le pointeur vers la dernière révision publiéeles définitions de section stockent uniquement les ids de renderers, les recettes, les références de composants et les bindings
les pages ordinaires utilisent des références
PageSectionnon épinglées pour suivre la dernière section publiée ; les brouillons de design contextuels stricts reçoivent côté serveur dessectionRevisionIdafin que la review et la publication résolvent exactement les révisions immuables validées visuellementla création de section par IA utilise un shell réutilisable de plan workbench:
src/components/ai/plan-workbench/*tiroir adaptateur de section:
src/blocks/dashboard/content/sections/block-ai-create-drawer.tsxendpoint de plan streaming:
src/app/api/ai/blocks/plan/route.tsendpoints de runs/jobs persistés (resume/poll/cancel):
src/app/api/ai/blocks/plan/jobs/*tables de stockage persisté:
ui_block_ai_threadsui_block_ai_runsui_block_ai_run_events
abstraction runner:
src/lib/server/services/blocks/block-ai-plan-runner.tsle contrat de prompt IA inclut des instructions explicites de design policy (réutilisation de composants + patterns de layout cohérents avec le projet)
action de finalisation avec approbations explicites des imports manquants:
createBlockFromAiPlanAction
les points d'entrée de création IA sont gardés par le réglage de site
ai-components-enabled, afin que l'UI et les actions serveur se désactivent ensemble.
Catalogue hybride de pages dans le schéma système:
ui_page_registry_itemsstocke l'identité de page, le scope/channel, le chemin et les métadonnéesui_page_revisionsstocke unPuckPageDocumentV1versionné + diagnosticsLa génération IA de pages utilise un état durable Postgres:
ui_page_ai_runsui_page_ai_run_eventsAPIs sous
src/app/api/ai/pages/runs/*Vercel Queues peut réveiller la route worker hébergée;
bun run worker:page-aiinterroge le même état DB pour les déploiements auto-hébergés avec worker long-lived
ui_page_publicationsstocke l'état de cycle de vie et le pointeur vers la révision publiéel'UX du tableau de bord est divisée entre liste/table puis route d'éditeur dédiée par page
l'éditeur est Puck + shadcn direct (aucun chemin de fallback legacy)
le modèle de sauvegarde est autosave + publish/archive avec verrouillage optimiste (
baseRevisionId)la gestion de conflit est explicite (réponse
conflict) sans merge/rebase automatiquele runtime lit directement les documents Puck publiés
les pages globales se résolvent depuis
/[locale]/[...slug]les pages membres d'organisation se résolvent depuis
/[locale]/o/[orgSlug]/[[...slug]]la publication est bloquée en cas d'erreurs de diagnostics et de collisions avec des routes réservées
Politique d'approbation du registre global dans le schéma système:
ui_registry_templatesstocke les alias shadcn compatibles approuvés + les modèles d'URLles pipelines d'import résolvent les alias externes uniquement depuis les modèles approuvés
la page d'administration,
/dashboard/admin/registry-templates, est un écran global (voir Tableau de bord) : une table pleine page des registres approuvés pour qui détientregistry-template:manageglobalement, avec leurs formulaires de création, de modification et de suppression ; la page synchronise les registres connus (les défauts shadcn et ceux decomponents.json) à chaque chargement
Actions serveur générées dans
src/lib/server/actions/database/generatedLes modèles de données projet dynamiques sont pilotés par MCP et stockés comme tables Postgres natives générées pour les lectures/écritures opérationnelles. Admin > Dynamic Data est la surface statique de control plane pour inspecter les modèles, interroger les enregistrements déployés et gérer les formulaires create/edit quand l'acteur possède
dynamic-data:manage. Les sections dashboard orientées produit sont déclarées par MCP viaui.nav.sectionet rendues à/dashboard/{section}/dynamic-datasans changement de code dès que des modèles déployés visibles y sont rattachés. La liste de modèles du dashboard inclut un formulaire no-code borné pour créer des brouillons de modèles en table native depuis un nom, un slug, une portée, une section de navigation et des champs scalaires; la publication et le déploiement passent toujours par le même pipeline validé dynamic-data/MCP avant toute DDL générée. Les gestionnaires de modèles peuvent aussi publier le dernier brouillon prêt depuis la liste des modèles du dashboard, ouvrir le plan de déploiement généré pour la révision publiée, puis appliquer ce plan vérifié depuis le détail modèle; ces actions appellent les mêmes services de planification, publication et déploiement que MCP et restent protégées pardynamic-data:manage. Cette liste inclut les modèles visibles via des grants larges globaux/organisation et via des grants resource-scoped par modèle; l'accès manage par modèle contrôle les actions d'enregistrement générées. Les métadonnées d'extension sont visibles dans la liste et le détail modèle afin d'inspecter les bundles produit par clé d'extension, capacités, tags et liaisons d'entités sans ouvrir le JSON brut. Pour les modèles déployés, le détail modèle et les actions runtime générées sont rendus depuis le même manifest runtime normalisé que le MCP renvoie, afin que les opérateurs inspectent les routes etui.runtimeActionsinstallées plutôt qu'une UI draft non publiée. Les définitions peuvent inclure des métadonnées validéesui.tablepour les colonnes, vues table/kanban/gallery, tris et calculs, ainsi queui.formpour compiler l'UI create/edit dans le form builder Yayaw Table existant: ordre, visibilité, libellés, aide, readonly, layout drawer/modal et hints de widgets sûrs.
Observabilité et indicateurs de fonctionnalité
Intégration serveur/client PostHog dans
src/lib/posthogetsrc/providersIndicateurs dynamiques dans
src/lib/flagsValeurs par défaut des indicateurs gérés déclarées dans
src/config/flags.config.tset seedées parsrc/lib/scripts/seed.tsLes réglages de site admin s'appuient sur des lignes
feature_flagsgérées par le codeLes indicateurs personnalisés sont générés depuis les lignes
feature_flagsnon gérées et n'affectent le comportement à l'exécution que lorsque le code lit explicitement leur slugCapacités auth runtime résolues dans
src/config/authentication-runtime.config.tsLes vues d'ensemble du tableau de bord sont des écrans (
src/lib/server/services/screens, voir Tableau de bord) : des documents de tableau de bord YaYaw Table dont les widgets lisent des sources qui vérifientcan()à chaque lecture, et des blocs de l'hôte qui décident eux-mêmes :/dashboard/adminest la surface de santé plateforme superadmin, un écran global : revenus Stripe, audience PostHog ou Umami, utilisateurs, organisations, abonnements, risques de facturation et déploiement, chacun derrière uncan()global. Le parent admin de la barre latérale est un lien direct vers cette route./dashboardest un accueil de navigation conscient des permissions avec un répertoire exhaustif et recherchable localement des destinations statiques autorisées. Une racine de section n'est liée que si elle est autorisée indépendamment; un enfant autorisé ne donne pas accès à son aperçu parent.les favoris et destinations récentes utilisent un payload
localStorageversionné et scopé sur l'utilisateur et l'organisation. Chaque href stocké est re-filtré par rapport à l'ensemble courant des destinations autorisées par le serveur avant affichage ou persistance.les applications de données dynamiques vivent dans un navigateur recherchable séparé afin qu'un catalogue de modèles non borné ne surcharge pas le répertoire statique. L'accueil ne duplique ni la santé organisationnelle ni les métriques fournisseur.
la vue d'ensemble de l'organisation montre la facturation, les sièges et les membres aux membres que leurs groupes y autorisent (
billing:read,member:list), jamais d'après un rôle d'organisation, et chaque type d'activité derrière sa propre permission ; les membres ne voient sur l'accueil que les destinations auxquelles ils ont accèsles agrégats base de données, PostHog et Stripe des sources des écrans utilisent le Data Cache partagé de Next.js pendant 5 minutes, avec clés par source, organisation, accès et requête, tandis que session et autorisation restent par requête
les analytics de vues CMS lisent les événements
cms_page_viewedpour les pages de l'organisation active et incluent les pages globales seulement pour les acteurs ayantpage:manageglobal, y compris les classements de pages principales sur/dashboard/contentun fournisseur non configuré, en échec ou trop lent affiche un avis dans ses widgets au lieu de casser la page
la vue d'ensemble d'administration montre les métadonnées du déploiement, issues des variables
DEPLOYMENT_*du runtime auto-hébergé ; l'API de Vercel n'est jamais lueles événements PostHog enregistrés depuis l'app incluent
organization_idlorsqu'une organisation active est disponible, ce qui permet les analytics scopées par organisation
Fournisseurs De Déploiement
La première cible auto-hébergée portable de Yayaw est Docker standalone Next.js
avec Postgres, stockage objet compatible S3, worker Page AI adossé à la base et
proxy inverse qui préserve Host et X-Forwarded-*. Vercel reste un fournisseur
supporté pour l'hébergement, les queues, les métriques de déploiement et la
vérification des domaines projet, mais ces capacités ne sont plus présumées par
le runtime coeur.
Seams runtime:
stockage:
STORAGE_PROVIDER=s3domaines publics d'organisation:
PUBLIC_DOMAIN_PROVIDER=vercel|manual-dnsréveil Page AI:
PAGE_AI_QUEUE_DRIVER=direct|vercel-queue|db-workermétadonnées de déploiement: variables runtime Vercel ou variables statiques
DEPLOYMENT_*
Postgres + Drizzle est la source de vérité des données applicatives. Le stockage objet compatible S3 est la couche de persistance des médias.
Plan de contrôle
Endpoint MCP de production:
src/app/api/mcp/route.tsLanceur stdio local:
src/lib/scripts/mcp/yayaw-mcp-server.tsScript de gestion des clés:
src/lib/scripts/mcp/control-plane-key.tsRegistre partagé d'opérations typées:
src/lib/server/services/control-planeStockage d'audit:
control_plane_audit_eventsL'accès production exige le réglage de site
control-plane-mcp-enabled, une clé API Better Auth valide ou un access token OAuth, des permissions de plan de contrôle et l'autorisation Yayaw sous-jacenteLes outils d'écriture exigent
reason; les flux de publication optimistes exigentexpectedRevisionId; les archives destructrices exigentconfirm: true
Règle pour les futures fonctionnalités:
Traiter par défaut les workflows d'exploitation, contenu, configuration, publication, audit et statut comme candidats au plan de contrôle
Placer la logique métier réutilisable derrière des points d'entrée de service explicites et conscients de l'acteur
Garder les actions serveur UI, outils MCP, scripts CLI et APIs HTTP facultatives comme de fins adaptateurs au-dessus de la même logique de service
Ajouter outils/ressources MCP typés, couverture d'audit, docs, mises à jour de la source LLM et tests ciblés dans le même changement lorsqu'une fonctionnalité devient opérable par le plan de contrôle
Documenter les exclusions intentionnelles lorsqu'une fonctionnalité reste uniquement UI
Contrat des indicateurs de fonctionnalité
Pour les fonctionnalités produit, les indicateurs doivent contrôler:
le comportement runtime (plugins/services/actions)
l'exposition UI (cartes, actions, entrées de navigation)
Cela évite les désactivations partielles où le backend est coupé mais où les liens UI restent visibles.
Architecture de documentation
Docs humaines: pages Fumadocs sous
content/docsSource de vérité des docs LLM:
content/llm/llm-source.mdDocs assistant générées:
AGENTS.mdGEMINI.md.github/copilot-instructions.md
Lorsque vous changez un comportement produit, mettez d'abord à jour la doc de
fonctionnalité anglaise pertinente, puis mettez à jour
content/llm/llm-source.md lorsque le comportement assistant ou la source de
vérité architecturale change. Régénérez les docs assistant avec:
bun run docs:llm:generate