Wiki/examples/acadenice-formation-hub/seed-baserow.md
Corentin JOGUET 2ed73fa948
Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
feat(bridge): R1 refactor proxy generique style Notion
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>
2026-05-07 22:12:32 +02:00

157 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_SECRET` du 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 |