Pivot strategique : DocAdenice = produit Notion-like generique. Le bridge
est livre vide a un user qui cree ses tables Baserow comme il veut. Code
sans aucune ontologie metier.
Suppressions :
- 9 entites domain metier (Personne, Formation, Bloc, Module, Attribution,
Client, Projet, Tache, Intervention) + types.ts (Role, statuts)
- baserow-repo.ts (mega-fichier 554 LOC avec 9 repos heritant BaseRepo)
- 6 routes metier (personnes, formations, projets, modules, interventions,
attributions) + tests associes
- Lookup PersonneRepo.findByEmail dans middleware auth
- Mapping DEFAULT_ROLE_SCOPES dans middleware/scopes.ts
- Cascade rollup metier dans webhooks/baserow-handler.ts
Ajouts :
- Domain generique : Table, Row, Field, View + schemas zod refondus
- 4 repos generiques : tables / rows / fields / views
- Route unique routes/tables.ts avec 9 endpoints REST CRUD generiques
- Claim JWT acadenice_permissions[] lu directement dans le middleware auth
(alimente par RBAC dynamique cote DocAdenice en R2)
- examples/acadenice-formation-hub/ : README + seed-baserow.md schema
9 tables + example-roles.md (Formateur, Developpeur, Direction, Support,
Admin avec permissions generiques)
Refactors :
- BaserowClient etendu : listTables, getTable, listFields, listViews,
getGridViewRows
- middleware/auth.ts : extractPermissions(payload), AuthenticatedUser
remplace roles[] par permissions[]
- middleware/scopes.ts : computeOidcScopes(groups, permissions, map)
- webhooks/baserow-handler.ts : invalidation generique
bridge:tables:<tableId>:* sans cascade cross-table
- lib/cache.ts : invalidateEntity -> invalidateTable(redis, tableId, rowId?)
- container.ts : drop tableIds, RepoSet={tables, rows, fields, views}
- 501 NOT_IMPLEMENTED si DB token sur endpoints /tables qui exigent JWT
Tests : 250/250 verts (depuis 319). Coverage : domain 98.9%, adapters 89%,
auth 97.08%, rate-limit 100%, cache 100%, webhooks 100%.
Quality gates verts : typecheck, lint biome, vitest, coverage thresholds.
Refs: R1 dans le pivot strategique DocAdenice Notion-like generique.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
Seed Baserow — schema formation-hub Acadenice
Schema reference des 9 tables Baserow pour cet exemple metier.
Ce document decrit le modele que l'utilisateur cree dans son Baserow. Le bridge ne sait rien de tout cela : il proxie generique. Le code de seeding (Python ou autre) peut venir plus tard ; c'est la mise en oeuvre typique de la doc 19 Bridge API design (cas Acadenice).
Database
- Workspace :
Acadenice - Database :
formation-hub - 9 tables au singulier
Table Personne
Pivot multi-roles. Capacite annuelle d'heures splittee entre formation et agence.
| Field name | Type | Notes |
|---|---|---|
personne_nom |
text (primary) | |
personne_prenom |
text | |
personne_email |
text (email) | Unique. Lie au sub Authentik via DocAdenice. |
personne_telephone |
text | Optionnel. |
personne_capacite_annuelle |
number | Heures annuelles theoriques (ex 1500). |
personne_split_formation_pct |
number | 0-100. Doit sommer 100 avec split_agence. |
personne_split_agence_pct |
number | 0-100. |
personne_roles |
multi_select | Choix : formateur, developpeur, admin, direction, support |
personne_statut |
single_select | actif / inactif |
personne_heures_attribuees_formation |
formula (rollup sum attributions) | Auto. |
personne_heures_attribuees_agence |
formula (rollup sum interventions) | Auto. |
personne_heures_restantes_formation |
formula | capacite × split_formation_pct - heures_attribuees |
Table Formation
| Field name | Type | Notes |
|---|---|---|
formation_nom |
text (primary) | |
formation_filiere |
single_select | dev, graphisme, marketing, iot, cybersec |
formation_heures_totales |
number | |
formation_statut |
single_select | draft, actif, termine, archive |
formation_date_debut |
date | |
formation_date_fin |
date | |
formation_heures_attribuees |
formula | Sum rollup blocs.heures_prevues |
Table Bloc
Decoupage pedagogique d'une Formation.
| Field name | Type | Notes |
|---|---|---|
bloc_nom |
text (primary) | |
bloc_formation |
link_row -> Formation | FK obligatoire. |
bloc_heures_prevues |
number | |
bloc_ordre |
number |
Table Module
Brique elementaire d'un Bloc, attribuable a une Personne.
| Field name | Type | Notes |
|---|---|---|
module_nom |
text (primary) | |
module_bloc |
link_row -> Bloc | |
module_heures_prevues |
number | |
module_statut |
single_select | a_attribuer, attribue, en_cours, realise, annule |
module_heures_attribuees |
formula | Sum rollup attributions. |
module_heures_realisees |
formula | Sum rollup attributions. |
Table Attribution
Lien Module <-> Personne[role=formateur] avec heures et statut.
| Field name | Type | Notes |
|---|---|---|
attribution_module |
link_row -> Module | |
attribution_personne |
link_row -> Personne | role=formateur exige cote DocAdenice |
attribution_heures_attribuees |
number | RG-01 : sum <= module.heures_prevues |
attribution_heures_realisees |
number | Saisi par le formateur |
attribution_date_debut |
date | |
attribution_date_fin |
date | |
attribution_statut |
single_select | planifie, en_cours, realise, annule |
Table Client
| Field name | Type | Notes |
|---|---|---|
client_nom |
text (primary) | |
client_contact_principal |
text | |
client_contact_email |
text (email) | |
client_contact_telephone |
text | |
client_secteur |
text | |
client_notes |
long_text | |
client_statut |
single_select | prospect, actif, inactif, archive |
Table Projet
| Field name | Type | Notes |
|---|---|---|
projet_nom |
text (primary) | |
projet_client |
link_row -> Client | |
projet_type |
single_select | site_web, app_mobile, api, infra, audit, support, autre |
projet_charge_heures |
number | Devis valide. |
projet_statut |
single_select | devis, en_cours, livre, cloture, abandonne |
projet_formation_pedagogique |
link_row -> Formation | Optionnel. Lien projet pedagogique. |
projet_heures_realisees |
formula | Sum rollup taches.heures_realisees |
Table Tache
| Field name | Type | Notes |
|---|---|---|
tache_titre |
text (primary) | |
tache_projet |
link_row -> Projet | |
tache_charge_heures |
number | |
tache_priorite |
single_select | faible, normale, haute, critique |
tache_statut |
single_select | todo, in_progress, review, done, abandoned |
tache_heures_realisees |
formula | Sum rollup interventions.heures |
Table Intervention
Lien Tache <-> Personne[role=developpeur] avec heures saisies.
| Field name | Type | Notes |
|---|---|---|
intervention_tache |
link_row -> Tache | |
intervention_personne |
link_row -> Personne | role=developpeur exige cote DocAdenice |
intervention_heures |
number | > 0 |
intervention_date |
date | |
intervention_notes |
long_text | Optionnel. |
intervention_statut |
single_select | planifie, realise, annule |
Webhooks Baserow vers le bridge
Configurer un webhook par table (ou un seul global selon la version Baserow)
qui pointe sur POST /api/webhooks/baserow du bridge avec :
- header
X-Baserow-Signature: <hmac-sha256-hex> - secret partage via env
BASEROW_WEBHOOK_SECRETdu bridge
Le bridge invalidera juste bridge:tables:<tableId>:* — sans cascade
metier. Si vous avez besoin de mettre a jour une table parente, posez une
formule Baserow qui fait le rollup ; Baserow emettra son propre webhook
quand la formule recalcule.
Regles de gestion principales (RG)
Ces regles vivent cote frontend / DocAdenice — pas dans le bridge.
| Code | Regle |
|---|---|
| RG-01 | Sum(attributions.heures_attribuees) <= module.heures_prevues |
| RG-02 | personne.split_formation_pct + split_agence_pct = 100 |
| RG-03 | Attribution exige personne.role contient 'formateur' |
| RG-04 | Intervention exige personne.role contient 'developpeur' |
| RG-05 | personne.heures_attribuees_formation <= capacite × split_formation_pct |
| RG-06 | personne.heures_attribuees_agence <= capacite × split_agence_pct |