This commit fixes 5 critical bugs found during code review: Bug #1 (CRITICAL): Missing API endpoints for election filtering - Added GET /api/elections/upcoming endpoint - Added GET /api/elections/completed endpoint - Both properly filter elections by date Bug #2 (HIGH): Auth context has_voted state inconsistency - Backend schemas now include has_voted in LoginResponse and RegisterResponse - Auth routes return actual has_voted value from database - Frontend context uses server response instead of hardcoding false - Frontend API client properly typed with has_voted field Bug #3 (HIGH): Transaction safety in vote submission - Simplified error handling in vote submission endpoints - Now only calls mark_as_voted() once at the end - Vote response includes voter_marked_voted flag to indicate success - Ensures consistency even if blockchain submission fails Bug #4 (MEDIUM): Vote status endpoint - Verified endpoint already exists at GET /api/votes/status - Tests confirm proper functionality Bug #5 (MEDIUM): Response format inconsistency - Previously fixed in commit e10a882 - Frontend now handles both array and wrapped object formats Added comprehensive test coverage: - 20+ backend API tests (tests/test_api_fixes.py) - 6+ auth context tests (frontend/__tests__/auth-context.test.tsx) - 8+ elections API tests (frontend/__tests__/elections-api.test.ts) - 10+ vote submission tests (frontend/__tests__/vote-submission.test.ts) All fixes ensure frontend and backend communicate consistently. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
123 lines
2.5 KiB
Python
123 lines
2.5 KiB
Python
"""
|
|
Schémas Pydantic pour validation des requêtes/réponses.
|
|
"""
|
|
|
|
from pydantic import BaseModel, EmailStr, Field
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
|
|
|
|
class VoterRegister(BaseModel):
|
|
"""Enregistrement d'un électeur"""
|
|
email: str
|
|
password: str = Field(..., min_length=6)
|
|
first_name: str
|
|
last_name: str
|
|
citizen_id: str # Identifiant unique (CNI)
|
|
|
|
|
|
class VoterLogin(BaseModel):
|
|
"""Authentification"""
|
|
email: str
|
|
password: str
|
|
|
|
|
|
class TokenResponse(BaseModel):
|
|
"""Réponse d'authentification"""
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|
|
|
|
|
|
class LoginResponse(BaseModel):
|
|
"""Réponse de connexion - retourne le profile et le token"""
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|
|
id: int
|
|
email: str
|
|
first_name: str
|
|
last_name: str
|
|
has_voted: bool
|
|
|
|
|
|
class RegisterResponse(BaseModel):
|
|
"""Réponse d'enregistrement - retourne le profile et le token"""
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|
|
id: int
|
|
email: str
|
|
first_name: str
|
|
last_name: str
|
|
has_voted: bool
|
|
|
|
|
|
class VoterProfile(BaseModel):
|
|
"""Profil d'un électeur"""
|
|
id: int
|
|
email: str
|
|
first_name: str
|
|
last_name: str
|
|
has_voted: bool
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CandidateResponse(BaseModel):
|
|
"""Candidat"""
|
|
id: int
|
|
name: str
|
|
description: Optional[str]
|
|
order: int
|
|
|
|
|
|
class ElectionResponse(BaseModel):
|
|
"""Élection avec candidats"""
|
|
id: int
|
|
name: str
|
|
description: Optional[str]
|
|
start_date: datetime
|
|
end_date: datetime
|
|
is_active: bool
|
|
results_published: bool
|
|
candidates: List[CandidateResponse]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class VoteBulletin(BaseModel):
|
|
"""Bulletin de vote à soumettre"""
|
|
election_id: int
|
|
candidate_id: int
|
|
encrypted_vote: str # Base64 du Ciphertext ElGamal
|
|
zero_knowledge_proof: Optional[str] = None # Base64 de la preuve ZK
|
|
|
|
|
|
class VoteResponse(BaseModel):
|
|
"""Confirmaction de vote"""
|
|
id: int
|
|
ballot_hash: str
|
|
timestamp: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ResultResponse(BaseModel):
|
|
"""Résultat de l'élection"""
|
|
candidate_name: str
|
|
vote_count: int
|
|
percentage: float
|
|
|
|
|
|
class ElectionResultResponse(BaseModel):
|
|
"""Résultats complets d'une élection"""
|
|
election_id: int
|
|
election_name: str
|
|
total_votes: int
|
|
results: List[ResultResponse]
|