Sprint 2 : moteur de documents + sanctuaire

Backend:
- CRUD complet documents/items/versions (update, delete, accept, reject, reorder)
- Service IPFS (upload/retrieve/pin via kubo HTTP API)
- Service sanctuaire : pipeline SHA-256 + IPFS + on-chain (system.remark)
- Verification integrite des entrees sanctuaire
- Recherche par reference (document -> entrees sanctuaire)
- Serialisation deterministe des documents pour archivage
- 14 tests unitaires supplementaires (document service)

Frontend:
- 9 composants : StatusBadge, MarkdownRenderer, DiffView, ItemCard,
  ItemVersionDiff, DocumentList, SanctuaryEntry, IPFSLink, ChainAnchor
- Page detail item avec historique des versions et diff
- Page detail sanctuaire avec verification integrite
- Modal de creation de document + proposition de version
- Archivage document vers sanctuaire depuis la page detail

Documentation:
- API reference mise a jour (9 nouveaux endpoints)
- Guides utilisateur documents et sanctuaire enrichis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-28 13:08:48 +01:00
parent 25437f24e3
commit 2bdc731639
26 changed files with 3452 additions and 397 deletions

View File

@@ -27,7 +27,14 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
| POST | `/{slug}/items` | Ajouter un item au document | Oui |
| GET | `/{slug}/items` | Lister les items d'un document | Non |
| GET | `/{slug}/items/{item_id}` | Obtenir un item avec son historique | Non |
| PUT | `/{slug}/items/{item_id}` | Mettre a jour un item (titre, texte, position, type) | Oui |
| DELETE | `/{slug}/items/{item_id}` | Supprimer un item du document | Oui |
| POST | `/{slug}/items/{item_id}/versions` | Proposer une nouvelle version d'un item | Oui |
| GET | `/{slug}/items/{item_id}/versions` | Lister les versions d'un item | Non |
| PUT | `/{slug}/items/{item_id}/versions/{version_id}/accept` | Accepter une version proposee (applique le texte a l'item) | Oui |
| PUT | `/{slug}/items/{item_id}/versions/{version_id}/reject` | Rejeter une version proposee | Oui |
| PUT | `/{slug}/items/reorder` | Reordonner les items d'un document | Oui |
| POST | `/{slug}/archive` | Archiver le document dans le sanctuaire (hash SHA-256 + IPFS + on-chain) | Oui |
## Decisions (`/api/v1/decisions`)
@@ -73,11 +80,13 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
## Sanctuaire (`/api/v1/sanctuary`)
| Methode | Endpoint | Description | Auth |
| ------- | --------- | ---------------------------------------------------------- | ---- |
| GET | `/` | Lister les entrees du sanctuaire (filtre: entry_type) | Non |
| GET | `/{id}` | Obtenir une entree du sanctuaire | Non |
| POST | `/` | Creer une entree (hash SHA-256, CID IPFS, TX on-chain) | Oui |
| Methode | Endpoint | Description | Auth |
| ------- | --------------------------------- | ---------------------------------------------------------- | ---- |
| GET | `/` | Lister les entrees du sanctuaire (filtre: entry_type) | Non |
| GET | `/{id}` | Obtenir une entree du sanctuaire | Non |
| POST | `/` | Creer une entree (hash SHA-256, CID IPFS, TX on-chain) | Oui |
| GET | `/{id}/verify` | Verifier l'integrite d'une entree (recalcul SHA-256, verification IPFS et on-chain) | Non |
| GET | `/by-reference/{reference_id}` | Obtenir les entrees liees a une entite source par son UUID | Non |
## WebSocket (`/api/v1/ws`)
@@ -91,6 +100,127 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
| ------- | -------------- | -------------------------- |
| GET | `/api/health` | Verification de sante (hors versionning) |
## Details des endpoints Sprint 2
### `PUT /api/v1/documents/{slug}/items/{item_id}` -- Mettre a jour un item
Met a jour les champs d'un item existant (titre, texte courant, position, type). Seuls les champs fournis sont mis a jour (mise a jour partielle).
**Corps de la requete** (tous les champs sont optionnels) :
```json
{
"title": "Nouveau titre",
"current_text": "Texte mis a jour...",
"position": "2.1",
"item_type": "rule"
}
```
**Reponse** : `200 OK` avec l'item mis a jour (`DocumentItemOut`).
---
### `DELETE /api/v1/documents/{slug}/items/{item_id}` -- Supprimer un item
Supprime un item d'un document. La suppression est en cascade : toutes les versions associees sont egalement supprimees.
**Reponse** : `204 No Content`.
---
### `GET /api/v1/documents/{slug}/items/{item_id}/versions` -- Lister les versions d'un item
Retourne l'historique complet des versions proposees pour un item, ordonne par date de creation decroissante.
**Parametres de requete** : `skip`, `limit` (pagination standard).
**Reponse** : `200 OK` avec une liste de `ItemVersionOut`.
---
### `PUT /api/v1/documents/{slug}/items/{item_id}/versions/{version_id}/accept` -- Accepter une version
Accepte une version proposee. Le texte propose remplace le texte courant de l'item. Toutes les autres versions en statut `proposed` ou `voting` pour cet item sont automatiquement rejetees.
**Reponse** : `200 OK` avec la version mise a jour (`ItemVersionOut`, statut `accepted`).
---
### `PUT /api/v1/documents/{slug}/items/{item_id}/versions/{version_id}/reject` -- Rejeter une version
Rejette une version proposee. Le texte courant de l'item reste inchange.
**Reponse** : `200 OK` avec la version mise a jour (`ItemVersionOut`, statut `rejected`).
---
### `PUT /api/v1/documents/{slug}/items/reorder` -- Reordonner les items
Modifie l'ordre d'affichage des items dans un document en mettant a jour le champ `sort_order` de chaque item.
**Corps de la requete** :
```json
{
"items": [
{ "item_id": "uuid-1", "sort_order": 0 },
{ "item_id": "uuid-2", "sort_order": 1 },
{ "item_id": "uuid-3", "sort_order": 2 }
]
}
```
**Reponse** : `200 OK` avec la liste des items reordonnes.
---
### `POST /api/v1/documents/{slug}/archive` -- Archiver un document
Archive le document complet dans le sanctuaire. Le processus :
1. Le contenu integral du document (metadonnees + items) est serialise.
2. Un hash SHA-256 est calcule sur le contenu.
3. Le contenu est envoye sur IPFS (CID retourne).
4. Le hash est ancre on-chain via `system.remark` sur Duniter V2.
5. Une entree `sanctuary_entries` est creee avec les references.
6. Le statut du document passe a `archived` et les champs `ipfs_cid` et `chain_anchor` sont mis a jour.
**Reponse** : `200 OK` avec le document mis a jour incluant `ipfs_cid` et `chain_anchor`.
---
### `GET /api/v1/sanctuary/{id}/verify` -- Verifier l'integrite d'une entree
Verifie l'integrite d'une entree du sanctuaire en effectuant trois controles :
1. **Hash SHA-256** : recalcul du hash a partir du contenu source et comparaison avec `content_hash`.
2. **IPFS** : verification que le CID IPFS pointe vers un contenu valide (si disponible).
3. **On-chain** : verification que le hash est present dans le `system.remark` du bloc reference (si disponible).
**Reponse** :
```json
{
"entry_id": "uuid",
"hash_valid": true,
"ipfs_valid": true,
"chain_valid": true,
"verified_at": "2026-02-28T12:00:00Z",
"details": "Tous les controles sont valides."
}
```
---
### `GET /api/v1/sanctuary/by-reference/{reference_id}` -- Entrees par reference
Retourne toutes les entrees du sanctuaire liees a une entite source (document, decision ou session de vote) identifiee par son UUID.
**Parametres de requete** : `skip`, `limit` (pagination standard).
**Reponse** : `200 OK` avec une liste de `SanctuaryEntryOut`.
## Pagination
Les endpoints de liste acceptent les parametres `skip` (offset, defaut 0) et `limit` (max 200, defaut 50).

View File

@@ -38,7 +38,7 @@ Sessions d'authentification (tokens).
### `documents`
Documents de reference modulaires.
Documents de reference modulaires. Le cycle de vie d'un document suit les statuts `draft` -> `active` -> `archived`. Lors de l'archivage (Sprint 2), les champs `ipfs_cid` et `chain_anchor` sont renseignes automatiquement par le service sanctuaire.
| Colonne | Type | Description |
| ------------ | ------------ | ----------------------------------------------------- |
@@ -49,38 +49,38 @@ Documents de reference modulaires.
| version | VARCHAR(32) | Version semantique (defaut "0.1.0") |
| status | VARCHAR(32) | Statut : draft, active, archived |
| description | TEXT | Description du document |
| ipfs_cid | VARCHAR(128) | CID IPFS de la derniere version archivee |
| chain_anchor | VARCHAR(128) | Hash de transaction on-chain |
| ipfs_cid | VARCHAR(128) | CID IPFS de la derniere version archivee (renseigne lors de l'archivage) |
| chain_anchor | VARCHAR(128) | Hash de transaction on-chain (renseigne lors de l'archivage) |
| created_at | TIMESTAMPTZ | Date de creation |
| updated_at | TIMESTAMPTZ | Date de derniere mise a jour |
### `document_items`
Items individuels composant un document (clauses, regles, verifications, etc.).
Items individuels composant un document (clauses, regles, verifications, etc.). Chaque item peut etre modifie, supprime ou reordonne individuellement (Sprint 2). Le champ `current_text` est mis a jour automatiquement lorsqu'une version est acceptee.
| Colonne | Type | Description |
| ------------------- | ------------ | ------------------------------------------------- |
| id | UUID (PK) | Identifiant unique |
| document_id | UUID (FK) | -> documents.id |
| document_id | UUID (FK) | -> documents.id (cascade delete) |
| position | VARCHAR(16) | Numero de position ("1", "1.1", "3.2") |
| item_type | VARCHAR(32) | Type : clause, rule, verification, preamble, section |
| title | VARCHAR(256) | Titre de l'item |
| current_text | TEXT | Texte courant de l'item |
| current_text | TEXT | Texte courant de l'item (mis a jour lors de l'acceptation d'une version) |
| voting_protocol_id | UUID (FK) | -> voting_protocols.id (protocole specifique) |
| sort_order | INTEGER | Ordre de tri |
| sort_order | INTEGER | Ordre de tri (modifiable via endpoint reorder) |
| created_at | TIMESTAMPTZ | Date de creation |
| updated_at | TIMESTAMPTZ | Date de derniere mise a jour |
### `item_versions`
Historique des versions proposees pour chaque item.
Historique des versions proposees pour chaque item. Lors de l'acceptation d'une version (Sprint 2), le `current_text` de l'item parent est remplace par le `proposed_text` de la version, et toutes les autres versions en attente (`proposed`, `voting`) sont automatiquement rejetees.
| Colonne | Type | Description |
| -------------- | ------------ | ------------------------------------------------------ |
| id | UUID (PK) | Identifiant unique |
| item_id | UUID (FK) | -> document_items.id |
| item_id | UUID (FK) | -> document_items.id (cascade delete) |
| proposed_text | TEXT | Texte propose |
| diff_text | TEXT | Diff unifie entre texte courant et propose |
| diff_text | TEXT | Diff unifie entre texte courant et propose (genere automatiquement) |
| rationale | TEXT | Justification de la modification |
| status | VARCHAR(32) | Statut : proposed, voting, accepted, rejected |
| decision_id | UUID (FK) | -> decisions.id (decision associee) |
@@ -240,16 +240,16 @@ Configurations de formules de seuil WoT.
### `sanctuary_entries`
Entrees du sanctuaire (archivage immuable).
Entrees du sanctuaire (archivage immuable). Le champ `reference_id` permet de retrouver toutes les entrees liees a un document, une decision ou une session de vote via l'endpoint `/by-reference/{reference_id}` (Sprint 2). L'endpoint `/verify` recalcule le hash et verifie la coherence IPFS/on-chain.
| Colonne | Type | Description |
| -------------- | ------------ | ------------------------------------------ |
| id | UUID (PK) | Identifiant unique |
| entry_type | VARCHAR(64) | Type : document, decision, vote_result |
| reference_id | UUID | UUID de l'entite source |
| reference_id | UUID | UUID de l'entite source (indexe pour recherche par reference) |
| title | VARCHAR(256) | Titre |
| content_hash | VARCHAR(128) | Hash SHA-256 du contenu |
| ipfs_cid | VARCHAR(128) | CID IPFS |
| ipfs_cid | VARCHAR(128) | CID IPFS (renseigne lors de l'upload) |
| chain_tx_hash | VARCHAR(128) | Hash de la transaction on-chain |
| chain_block | INTEGER | Numero de bloc de la transaction |
| metadata_json | TEXT | Metadonnees JSON supplementaires |
@@ -279,9 +279,10 @@ duniter_identities
documents
|-- 1:N --> document_items
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
document_items
|-- 1:N --> item_versions
|-- 1:N --> item_versions (cascade delete)
|-- N:1 --> voting_protocols
item_versions
@@ -289,6 +290,7 @@ item_versions
decisions
|-- 1:N --> decision_steps
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
decision_steps
|-- N:1 --> vote_sessions
@@ -296,6 +298,7 @@ decision_steps
vote_sessions
|-- 1:N --> votes
|-- N:1 --> voting_protocols
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
mandates
|-- 1:N --> mandate_steps
@@ -309,4 +312,9 @@ voting_protocols
formula_configs
|-- 1:N --> voting_protocols
sanctuary_entries
|-- reference_id ..> documents | decisions | vote_sessions (lien logique)
```
> **Note Sprint 2** : Les liens `..>` (pointilles) representent des references logiques via le champ `reference_id` de `sanctuary_entries`. Ce ne sont pas des cles etrangeres PostgreSQL car `reference_id` peut pointer vers differentes tables selon le `entry_type`.