""" Configuration centralisée de l'application. Toutes les variables d'environnement et paramètres sont centralisés ici. """ import os from pathlib import Path from typing import Optional from pydantic import Field from pydantic import field_validator from pydantic_settings import BaseSettings def parse_env_bool(value: object, default: bool = False) -> bool: if value is None: return default if isinstance(value, bool): return value if isinstance(value, (int, float)): return bool(value) if isinstance(value, str): normalized = value.strip().lower() if normalized in {"1", "true", "yes"}: return True if normalized in {"0", "false", "no"}: return False return default return default class Settings(BaseSettings): """Configuration de l'application Homelab Automation.""" # === Chemins === base_dir: Path = Field(default_factory=lambda: Path(__file__).resolve().parent.parent) logs_dir: Path = Field(default_factory=lambda: Path(os.environ.get("LOGS_DIR", "/logs"))) @property def ansible_dir(self) -> Path: """Répertoire Ansible (relatif à base_dir.parent)""" return self.base_dir.parent / "ansible" @property def tasks_logs_dir(self) -> Path: """Répertoire des logs de tâches markdown""" return Path(os.environ.get("DIR_LOGS_TASKS", str(self.base_dir.parent / "tasks_logs"))) @property def db_path(self) -> Path: """Chemin de la base de données SQLite""" return self.logs_dir / "homelab.db" # === SSH === ssh_key_path: str = Field( default_factory=lambda: os.environ.get("SSH_KEY_PATH", str(Path.home() / ".ssh" / "id_rsa")) ) ssh_user: str = Field(default_factory=lambda: os.environ.get("SSH_USER", "automation")) ssh_remote_user: str = Field(default_factory=lambda: os.environ.get("SSH_REMOTE_USER", "root")) # === API === api_key: str = Field(default_factory=lambda: os.environ.get("API_KEY", "dev-key-12345")) api_title: str = "Homelab Automation Dashboard API" api_version: str = "1.0.0" api_description: str = "API REST moderne pour la gestion automatique d'homelab" # === JWT Authentication === jwt_secret_key: str = Field( default_factory=lambda: os.environ.get("JWT_SECRET_KEY", "dev-secret-key-change-in-production") ) jwt_expire_minutes: int = Field( default_factory=lambda: int(os.environ.get("JWT_EXPIRE_MINUTES", "1440")) ) jwt_algorithm: str = "HS256" # === Database === database_url: Optional[str] = Field(default=None) @property def async_database_url(self) -> str: """URL de connexion async pour SQLAlchemy""" if self.database_url: return self.database_url return f"sqlite+aiosqlite:///{self.db_path}" # === CORS === cors_origins: list = Field(default=["*"]) cors_allow_credentials: bool = True cors_allow_methods: list = Field(default=["*"]) cors_allow_headers: list = Field(default=["*"]) # === Notifications ntfy === ntfy_enabled: bool = Field( default_factory=lambda: os.environ.get("NTFY_ENABLED", "true").lower() == "true" ) ntfy_base_url: str = Field( default_factory=lambda: os.environ.get("NTFY_BASE_URL", "https://ntfy.sh") ) ntfy_default_topic: str = Field( default_factory=lambda: os.environ.get("NTFY_TOPIC", "homelab-automation") ) ntfy_timeout: int = Field( default_factory=lambda: int(os.environ.get("NTFY_TIMEOUT", "10")) ) ntfy_username: Optional[str] = Field( default_factory=lambda: os.environ.get("NTFY_USERNAME") ) ntfy_password: Optional[str] = Field( default_factory=lambda: os.environ.get("NTFY_PASSWORD") ) ntfy_token: Optional[str] = Field( default_factory=lambda: os.environ.get("NTFY_TOKEN") ) # === Scheduler === scheduler_timezone: str = Field( default_factory=lambda: os.environ.get("SCHEDULER_TIMEZONE", "America/Montreal") ) scheduler_misfire_grace_time: int = 300 # === Cache === hosts_cache_ttl: int = 60 # secondes inventory_cache_ttl: int = 60 # secondes logs_index_rebuild_interval: int = 60 # secondes # === Server === host: str = "0.0.0.0" port: int = 8008 reload: bool = Field( default_factory=lambda: os.environ.get("RELOAD", "true").lower() == "true" ) log_level: str = "info" # === Feature Flags === debug_mode: bool = Field(default=False, validation_alias="DEBUG_MODE") @field_validator("debug_mode", mode="before") @classmethod def _validate_debug_mode(cls, v: object) -> bool: return parse_env_bool(v, default=False) class Config: env_file = ".env" env_file_encoding = "utf-8" extra = "ignore" # Instance singleton de la configuration settings = Settings() DEBUG_MODE_ENABLED: bool = settings.debug_mode