Estendere l'app

Aggiungi un nuovo modulo CRUD, permesso, impostazione o task pianificato, seguendo le convenzioni proprie dell'app.

In questa pagina

Estendere l'app

CBGenesis è un punto di partenza, non un prodotto finito. Questi sono gli stessi passi seguiti dai suoi moduli Users/Roles/Permissions/Settings - usali come template per qualsiasi cosa nuova.

Costruire con un agente AI

Se stai estendendo cbGenesis con un agente di coding AI (Claude Code, Copilot, Cursor o simili), puntalo verso .agents/skills-custom/ prima che scriva qualsiasi codice - queste skill codificano i passi esatti qui sotto come istruzioni leggibili da macchina, con estratti di codice reali da questo codebase, così l'agente non deve decodificarli esplorando ogni handler:

SkillCopre
cbgenesis-crud-resourceL'intera vertical slice qui sotto - entità, servizio, handler, rotta, vista, componente - dall'inizio alla fine.
cbgenesis-rbac-permissionsIl modello di permessi resource:action, @secured e le guardie contro le self-action.
cbgenesis-csrf-frontendIl pattern obbligatorio fetchWithCsrf() per qualsiasi richiesta frontend che modifica dati.
cbgenesis-alpine-componentsLa forma dei componenti Alpine.js, la registrazione e la libreria condivisa utils/.
cbgenesis-testing-conventionsBaseIntegrationSpec, il vero meccanismo di isolamento tramite rollback delle transazioni e gli helper per le fixture.
cbgenesis-settings-configQuando usare una variabile d'ambiente rispetto al registro delle impostazioni basato su DB.

Una nuova convenzione che vale la pena far sì che un agente (o un umano) non debba riscoprire per tentativi ed errori? Aggiungila come nuova skill qui piuttosto che lasciarla come conoscenza tribale in una descrizione di PR. Vedi Progettato per lo sviluppo assistito dall'AI per il perché questo conta e un confronto prima/dopo misurato.

Aggiungere un nuovo modulo CRUD

1
Crea l'entità

In app/models/<domain>/, estendendo BaseEntity — vedi Database e ORM.

2
Crea il servizio

Estendendo BaseService, marcato singleton threadSafe — vedi il pattern del servizio.

3
Crea l'handler

Estendendo BaseSecureHandler, con un'annotazione @secured — vedi Handler e Routing. Ereditare quella base significa che ogni azione POST/PUT/DELETE che aggiungi è verificata automaticamente per il CSRF; non c'è nulla da attivare, ma i tuoi form e componenti Alpine devono inviare rc.csrf.

4
Aggiungi le rotte

In app/config/Router.bx, vicino al marcatore // @app_routes@.

5
Crea le viste

In app/views/<domain>/, riutilizzando i partial esistenti di _components/ui/.

6
Crea un componente Alpine

In resources/assets/js/components/<domain>/, poi registralo in App.js — vedi Frontend.

7
Aggiungi lo SCSS

In resources/assets/scss/views/, importato da app.scss.

8
Scrivi i test

Spec unitarie in tests/specs/unit/<domain>/, più una spec di integrazione in tests/specs/integration/ per le rotte che hai aggiunto — vedi Testing.

Aggiungere un nuovo permesso

1
Semina lo slug

Aggiungi lo slug resource:action a resources/database/seeds/AdminData.bx e assegnalo al/ai ruolo/i appropriato/i.

2
Proteggi l'handler

@secured( "resource:action,resource:admin" ) — la virgola significa OR. Vedi Sicurezza e permessi.

3
Filtra la vista
<bx:if prc.authUser.hasPermission( "resource:action,resource:admin" )>

così l'interfaccia non offre mai qualcosa che l'handler rifiuterebbe.

4
Riesegui il seed

box migrate seed run su un database esistente - oppure concedi il permesso a un ruolo direttamente dalla pagina admin Ruoli.

Aggiungere un'impostazione

Aggiungi una nuova chiave alla struct DEFAULTS in SettingService.bx. preFlightCheck() la semina automaticamente al prossimo avvio, e compare nella pagina admin /settings senza ulteriore cablaggio — vedi Configurazione.

Personalizzare i layout

I layout vivono in app/layouts/. La selezione avviene per handler, tipicamente in preHandler:

function preHandler( event, rc, prc ){
    event.setLayout( "Admin" );
}

Aggiungere un task pianificato

Registra i task in app/config/Scheduler.bx, accanto ai tre che già vi vengono eseguiti - vedi Architettura per cosa fanno:

task( "My Task" )
    .call( () => getInstance( "MyService" ).doWork() )
    .everyDayAt( "03:45" )
    .onOneServer()
    .withNoOverlaps();

onOneServer() e withNoOverlaps() contano nel momento in cui distribuisci più di un'istanza: senza di essi, ogni istanza esegue il task secondo il proprio programma. Metti la logica effettiva di purge/pulizia sul servizio (doWork() sopra), non inline nella closure, così resta testabile a livello unitario.

Sovrascrivere la configurazione dei moduli

Le configurazioni dei moduli in app/config/modules/ estendono i default del modulo stesso. Sovrascrivi qualsiasi chiave lì — le modifiche hanno effetto al prossimo ?fwreinit.

Modifica questa pagina Scarica Markdown Ultimo aggiornamento Oct 1, 2026, 2:02:30 PM