Étendre l'application
Ajouter un nouveau module CRUD, une permission, un paramètre, ou une tâche planifiée, en suivant les conventions propres de l'application.
Sur cette page
Étendre l'application
CBGenesis est un tremplin, pas un produit fini. Ce sont les mêmes étapes que suivent ses propres modules Users/Roles/Permissions/Settings - utilisez-les comme modèle pour tout ce que vous ajoutez.
Construire avec un agent IA
Si vous étendez cbGenesis avec un agent de codage IA (Claude Code, Copilot, Cursor, ou similaire), pointez-le vers .agents/skills-custom/ avant qu'il n'écrive la moindre ligne de code - ces skills encodent les étapes exactes ci-dessous sous forme d'instructions lisibles par une machine, avec de vrais extraits de code de cette base de code, afin que l'agent n'ait pas à les rétro-ingénierer en explorant chaque handler :
| Skill | Couvre |
|---|---|
cbgenesis-crud-resource | La tranche verticale complète ci-dessous - entité, service, handler, route, vue, composant - de bout en bout. |
cbgenesis-rbac-permissions | Le modèle de permissions resource:action, @secured, et les protections contre les auto-actions. |
cbgenesis-csrf-frontend | Le patron obligatoire fetchWithCsrf() pour toute requête frontend mutative. |
cbgenesis-alpine-components | La forme des composants Alpine.js, leur enregistrement, et la bibliothèque utils/ partagée. |
cbgenesis-testing-conventions | BaseIntegrationSpec, le véritable mécanisme d'isolation par annulation de transaction, et les aides de fixtures. |
cbgenesis-settings-config | Quand utiliser une variable d'environnement plutôt que le registre de paramètres en base de données. |
Une nouvelle convention qui mériterait qu'un agent (ou un humain) n'ait pas à la redécouvrir par essais et erreurs ? Ajoutez-la comme nouvelle skill ici plutôt que de la laisser comme savoir tribal dans une description de PR. Voir Conçu pour le développement assisté par IA pour comprendre pourquoi cela compte, avec une comparaison chiffrée avant/après.
Ajouter un nouveau module CRUD
Dans app/models/<domain>/, en étendant BaseEntity — voir Base de données & ORM.
En étendant BaseService, marqué singleton threadSafe — voir le patron de service.
En étendant BaseSecureHandler, avec une annotation @secured — voir Handlers & Routage. Hériter de cette base signifie que chaque action POST/PUT/DELETE que vous ajoutez est vérifiée CSRF automatiquement ; il n'y a rien à activer, mais vos formulaires et composants Alpine doivent envoyer rc.csrf.
Dans app/config/Router.bx, près du marqueur // @app_routes@.
Dans app/views/<domain>/, en réutilisant les partials _components/ui/ existants.
Dans resources/assets/js/components/<domain>/, puis l'enregistrer dans App.js — voir Frontend.
Dans resources/assets/scss/views/, importé depuis app.scss.
Spécifications unitaires dans tests/specs/unit/<domain>/, plus une spécification d'intégration dans tests/specs/integration/ pour les routes ajoutées — voir Tests.
Ajouter une nouvelle permission
Ajouter le slug resource:action à resources/database/seeds/AdminData.bx et l'assigner au(x) rôle(s) approprié(s).
@secured( "resource:action,resource:admin" ) — la virgule signifie OU. Voir Sécurité & Permissions.
<bx:if prc.authUser.hasPermission( "resource:action,resource:admin" )>
afin que l'interface n'offre jamais quelque chose que le handler rejetterait.
box migrate seed run sur une base de données existante - ou accorder la permission à un rôle directement depuis la page d'administration des Rôles.
Ajouter un paramètre
Ajoutez une nouvelle clé à la structure DEFAULTS dans SettingService.bx. preFlightCheck() l'insère automatiquement en base au prochain démarrage, et elle apparaît dans la page d'administration /settings sans câblage supplémentaire — voir Configuration.
Personnaliser les mises en page
Les mises en page vivent dans app/layouts/. La sélection se fait par handler, typiquement dans preHandler :
function preHandler( event, rc, prc ){
event.setLayout( "Admin" );
}
Ajouter une tâche planifiée
Enregistrez les tâches dans app/config/Scheduler.bx, à côté des trois qui s'y exécutent déjà - voir Architecture pour ce qu'elles font :
task( "My Task" )
.call( () => getInstance( "MyService" ).doWork() )
.everyDayAt( "03:45" )
.onOneServer()
.withNoOverlaps();
onOneServer() et withNoOverlaps() comptent dès l'instant où vous déployez plus d'une instance : sans eux, chaque instance exécute la tâche selon son propre planning. Placez la logique réelle de purge/nettoyage sur le service (doWork() ci-dessus), pas en ligne dans la closure, afin qu'elle reste testable unitairement.
Surcharger la configuration des modules
Les configurations de module dans app/config/modules/ étendent les valeurs par défaut du module lui-même. Surchargez n'importe quelle clé là - les modifications prennent effet au prochain ?fwreinit.
Pourquoi les skills personnalisées existent, et une comparaison chiffrée de tokens/appels d'outils.
Les conventions complètes de handler/route sur lesquelles s'appuie cette section.
Les patrons d'entité et de service en détail.
Livrez ce que vous avez construit.