""" Configuration management pour IPWatch Charge et valide le fichier config.yaml """ import yaml from pathlib import Path from typing import Dict, Any, List, Optional from pydantic import BaseModel, Field class AppConfig(BaseModel): """Configuration de l'application""" name: str = "IPWatch" version: str = "1.0.0" debug: bool = False class NetworkConfig(BaseModel): """Configuration réseau""" cidr: str gateway: Optional[str] = None dns: Optional[List[str]] = None class ScanConfig(BaseModel): """Configuration des scans""" ping_interval: int = 60 # secondes port_scan_interval: int = 300 # secondes parallel_pings: int = 50 timeout: float = 1.0 class PortsConfig(BaseModel): """Configuration des ports à scanner""" ranges: List[str] = ["22", "80", "443", "3389", "8080"] protocols: Optional[Dict[int, str]] = None # Mapping port -> protocole class HistoryConfig(BaseModel): """Configuration de l'historique""" retention_hours: int = 24 class UIConfig(BaseModel): """Configuration UI""" offline_transparency: float = 0.5 show_mac: bool = True show_vendor: bool = True cell_size: int = 30 font_size: int = 10 cell_gap: float = 2 details_font_size: int = 13 details_spacing: int = 2 class ColorsConfig(BaseModel): """Configuration des couleurs""" free: str = "#75715E" online_known: str = "#A6E22E" online_unknown: str = "#66D9EF" offline_known: str = "#F92672" offline_unknown: str = "#AE81FF" class DatabaseConfig(BaseModel): """Configuration base de données""" path: str = "./data/db.sqlite" class HostConfig(BaseModel): """Configuration d'un hôte avec sa localisation""" name: str location: str class IPWatchConfig(BaseModel): """Configuration complète IPWatch""" model_config = {"arbitrary_types_allowed": True} app: AppConfig = Field(default_factory=AppConfig) network: NetworkConfig ip_classes: Dict[str, Any] = Field(default_factory=dict) scan: ScanConfig = Field(default_factory=ScanConfig) ports: PortsConfig = Field(default_factory=PortsConfig) locations: List[str] = Field(default_factory=list) hosts: List[HostConfig] = Field(default_factory=list) history: HistoryConfig = Field(default_factory=HistoryConfig) ui: UIConfig = Field(default_factory=UIConfig) colors: ColorsConfig = Field(default_factory=ColorsConfig) database: DatabaseConfig = Field(default_factory=DatabaseConfig) class ConfigManager: """Gestionnaire de configuration singleton""" _instance: Optional['ConfigManager'] = None _config: Optional[IPWatchConfig] = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def load_config(self, config_path: str = "./config.yaml") -> IPWatchConfig: """Charge la configuration depuis le fichier YAML""" path = Path(config_path) if not path.exists(): raise FileNotFoundError(f"Fichier de configuration non trouvé: {config_path}") with open(path, 'r', encoding='utf-8') as f: yaml_data = yaml.safe_load(f) self._config = IPWatchConfig(**yaml_data) self._config_path = config_path return self._config def reload_config(self) -> IPWatchConfig: """Recharge la configuration depuis le fichier""" if not hasattr(self, '_config_path'): self._config_path = "./config.yaml" return self.load_config(self._config_path) @property def config(self) -> IPWatchConfig: """Retourne la configuration actuelle""" if self._config is None: raise RuntimeError("Configuration non chargée. Appelez load_config() d'abord.") return self._config # Instance globale config_manager = ConfigManager()