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:
@@ -12,6 +12,7 @@ from app.database import get_db
|
||||
from app.models.sanctuary import SanctuaryEntry
|
||||
from app.models.user import DuniterIdentity
|
||||
from app.schemas.sanctuary import SanctuaryEntryCreate, SanctuaryEntryOut
|
||||
from app.services import sanctuary_service
|
||||
from app.services.auth_service import get_current_identity
|
||||
|
||||
router = APIRouter()
|
||||
@@ -37,19 +38,6 @@ async def list_entries(
|
||||
return [SanctuaryEntryOut.model_validate(e) for e in entries]
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=SanctuaryEntryOut)
|
||||
async def get_entry(
|
||||
id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> SanctuaryEntryOut:
|
||||
"""Get a single sanctuary entry by ID."""
|
||||
result = await db.execute(select(SanctuaryEntry).where(SanctuaryEntry.id == id))
|
||||
entry = result.scalar_one_or_none()
|
||||
if entry is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Entree sanctuaire introuvable")
|
||||
return SanctuaryEntryOut.model_validate(entry)
|
||||
|
||||
|
||||
@router.post("/", response_model=SanctuaryEntryOut, status_code=status.HTTP_201_CREATED)
|
||||
async def create_entry(
|
||||
payload: SanctuaryEntryCreate,
|
||||
@@ -71,3 +59,47 @@ async def create_entry(
|
||||
await db.refresh(entry)
|
||||
|
||||
return SanctuaryEntryOut.model_validate(entry)
|
||||
|
||||
|
||||
@router.get("/by-reference/{reference_id}", response_model=list[SanctuaryEntryOut])
|
||||
async def get_entries_by_reference(
|
||||
reference_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> list[SanctuaryEntryOut]:
|
||||
"""Get all sanctuary entries for a given reference ID.
|
||||
|
||||
Useful for finding all sanctuary entries associated with a document,
|
||||
decision, or vote result.
|
||||
"""
|
||||
entries = await sanctuary_service.get_entries_by_reference(reference_id, db)
|
||||
return [SanctuaryEntryOut.model_validate(e) for e in entries]
|
||||
|
||||
|
||||
@router.get("/{id}/verify")
|
||||
async def verify_entry(
|
||||
id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> dict:
|
||||
"""Verify integrity of a sanctuary entry.
|
||||
|
||||
Re-fetches the content (from IPFS if available), re-hashes it,
|
||||
and compares with the stored content_hash.
|
||||
"""
|
||||
try:
|
||||
result = await sanctuary_service.verify_entry(id, db)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=SanctuaryEntryOut)
|
||||
async def get_entry(
|
||||
id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> SanctuaryEntryOut:
|
||||
"""Get a single sanctuary entry by ID."""
|
||||
result = await db.execute(select(SanctuaryEntry).where(SanctuaryEntry.id == id))
|
||||
entry = result.scalar_one_or_none()
|
||||
if entry is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Entree sanctuaire introuvable")
|
||||
return SanctuaryEntryOut.model_validate(entry)
|
||||
|
||||
Reference in New Issue
Block a user