Alexis Bruneteau f825a2392c feat: Implement dark theme for frontend with toggle
Changes:
- Add next-themes dependency for theme management
- Create ThemeProvider wrapper for app root layout
- Set dark mode as default theme
- Create ThemeToggle component with Sun/Moon icons
- Add theme toggle to home page navigation
- Add theme toggle to dashboard header
- App now starts in dark mode with ability to switch to light mode

Styling uses existing Tailwind dark mode variables configured in
tailwind.config.ts and globals.css. All existing components automatically
support dark theme.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 16:35:44 +01:00

129 lines
4.2 KiB
Python

"""
Modèles de données SQLAlchemy pour la persistance.
"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Text, LargeBinary
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime, timezone
def get_utc_now():
"""Get current UTC time (timezone-aware)"""
return datetime.now(timezone.utc)
Base = declarative_base()
class Voter(Base):
"""Électeur - Enregistrement et authentification"""
__tablename__ = "voters"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
first_name = Column(String(100))
last_name = Column(String(100))
citizen_id = Column(String(50), unique=True) # Identifiant unique (CNI)
# Sécurité
public_key = Column(LargeBinary) # Clé publique ElGamal
has_voted = Column(Boolean, default=False)
created_at = Column(DateTime, default=get_utc_now)
updated_at = Column(DateTime, default=get_utc_now, onupdate=get_utc_now)
# Relations
votes = relationship("Vote", back_populates="voter")
class Election(Base):
"""Élection - Configuration et paramètres"""
__tablename__ = "elections"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(255), nullable=False)
description = Column(Text)
# Dates
start_date = Column(DateTime, nullable=False)
end_date = Column(DateTime, nullable=False)
# Paramètres cryptographiques
elgamal_p = Column(Integer) # Nombre premier
elgamal_g = Column(Integer) # Générateur
public_key = Column(LargeBinary) # Clé publique pour chiffrement
# État
is_active = Column(Boolean, default=True)
results_published = Column(Boolean, default=False)
created_at = Column(DateTime, default=get_utc_now)
updated_at = Column(DateTime, default=get_utc_now, onupdate=get_utc_now)
# Relations
candidates = relationship("Candidate", back_populates="election")
votes = relationship("Vote", back_populates="election")
class Candidate(Base):
"""Candidat - Options de vote"""
__tablename__ = "candidates"
id = Column(Integer, primary_key=True, index=True)
election_id = Column(Integer, ForeignKey("elections.id"), nullable=False)
name = Column(String(255), nullable=False)
description = Column(Text)
order = Column(Integer) # Ordre d'affichage
created_at = Column(DateTime, default=get_utc_now)
# Relations
election = relationship("Election", back_populates="candidates")
votes = relationship("Vote", back_populates="candidate")
class Vote(Base):
"""Vote - Bulletin chiffré"""
__tablename__ = "votes"
id = Column(Integer, primary_key=True, index=True)
voter_id = Column(Integer, ForeignKey("voters.id"), nullable=False)
election_id = Column(Integer, ForeignKey("elections.id"), nullable=False)
candidate_id = Column(Integer, ForeignKey("candidates.id"), nullable=False)
# Vote chiffré avec ElGamal
encrypted_vote = Column(LargeBinary, nullable=False) # Ciphertext ElGamal
# Preuves
zero_knowledge_proof = Column(LargeBinary) # ZK proof que le vote est valide
ballot_hash = Column(String(64)) # Hash du bulletin pour traçabilité
# Métadonnées
timestamp = Column(DateTime, default=get_utc_now)
ip_address = Column(String(45)) # IPv4 ou IPv6
# Relations
voter = relationship("Voter", back_populates="votes")
election = relationship("Election", back_populates="votes")
candidate = relationship("Candidate", back_populates="votes")
class AuditLog(Base):
"""Journal d'audit pour la sécurité"""
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, index=True)
action = Column(String(100), nullable=False)
description = Column(Text)
# Qui
user_id = Column(Integer, ForeignKey("voters.id"))
# Quand
timestamp = Column(DateTime, default=get_utc_now)
# Métadonnées
ip_address = Column(String(45))
user_agent = Column(String(255))