""" Configuration de l'application avec pydantic-settings. """ from functools import lru_cache from pathlib import Path from typing import Optional from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): """Configuration de l'application.""" # Application app_name: str = "Notytex API" app_version: str = "2.0.0" debug: bool = False # Database - deux modes de configuration: # 1. database_path: chemin simple vers le fichier SQLite (recommandé) # 2. database_url: URL complète SQLAlchemy (rétrocompatibilité) # Si database_url est défini, il a la priorité sur database_path database_path: Optional[str] = None database_url: Optional[str] = None def _resolve_database_path(self) -> Path: """Résout le chemin de la base de données.""" if self.database_url: # Extraire le chemin depuis l'URL SQLAlchemy # Format: sqlite+aiosqlite:///path ou sqlite+aiosqlite:////absolute/path url = self.database_url # Retirer le préfixe for prefix in ["sqlite+aiosqlite://", "sqlite://"]: if url.startswith(prefix): url = url[len(prefix):] break # Le chemin commence par / ou // pour absolu, ou rien pour relatif return Path(url).resolve() elif self.database_path: path = Path(self.database_path) if not path.is_absolute(): # Par défaut, relatif au répertoire du projet path = Path(__file__).parent.parent / self.database_path return path.resolve() else: # Valeur par défaut return (Path(__file__).parent.parent / "school_management.db").resolve() def get_database_url(self) -> str: """Retourne l'URL de connexion async pour SQLAlchemy.""" if self.database_url is not None and self.database_url.startswith("sqlite"): return self.database_url # Construire depuis le chemin path = self._resolve_database_path() return f"sqlite+aiosqlite:///{path}" @property def sync_database_url(self) -> str: """Retourne l'URL de base de données synchrone.""" return self.get_database_url().replace("sqlite+aiosqlite://", "sqlite://") @property def database_path_resolved(self) -> Path: """Retourne le chemin absolu résolu de la base de données.""" return self._resolve_database_path() @property def database_path_configured(self) -> str: """Retourne le chemin tel que configuré (pour affichage).""" if self.database_url: # Extraire depuis l'URL return str(self._resolve_database_path()) return self.database_path or "school_management.db" @property def database_exists(self) -> bool: """Vérifie si le fichier de base de données existe.""" return self.database_path_resolved.exists() # API api_v2_prefix: str = "/api/v2" # CORS cors_origins: list[str] = ["http://localhost:3000", "http://localhost:5173"] # Logging log_level: str = "INFO" log_format: str = "json" # Email (repris de v1) smtp_server: Optional[str] = None smtp_port: int = 587 smtp_username: Optional[str] = None smtp_password: Optional[str] = None smtp_use_tls: bool = True email_from: Optional[str] = None model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="ignore", ) @lru_cache() def get_settings() -> Settings: """ Récupère les settings (cached). Utilise lru_cache pour éviter de relire le fichier .env à chaque appel. """ return Settings() # Export pour faciliter l'import settings = get_settings()