Files
ipwatch/backend/app/core/config.py
2026-02-07 16:57:37 +01:00

168 lines
4.8 KiB
Python
Executable File

"""
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
ping_count: int = 1 # Nombre de ping par IP
port_scan_interval: int = 300 # secondes
parallel_pings: int = 50
timeout: float = 1.0
force_vendor_update: bool = False
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
architecture_title_font_size: int = 18
class LinksConfig(BaseModel):
"""Configuration des liens"""
hardware_bench_url: Optional[str] = None
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"
mac_changed: str = "#FD971F"
network_device: str = "#1E3A8A"
class OPNsenseConfig(BaseModel):
"""Configuration OPNsense API"""
enabled: bool = False
host: str = ""
api_key: str = ""
api_secret: str = ""
verify_ssl: bool = False
protocol: str = "http" # "http" ou "https"
class DatabaseConfig(BaseModel):
"""Configuration base de données"""
path: str = "./data/db.sqlite"
class SubnetConfig(BaseModel):
"""Configuration d'un sous-réseau"""
name: str
cidr: str
start: str
end: str
description: str
class HostConfig(BaseModel):
"""Configuration d'un hôte avec sa localisation"""
name: str
location: str
ip: Optional[str] = None
ip_parent: Optional[str] = None
ip_enfant: Optional[List[str]] = None
class IPWatchConfig(BaseModel):
"""Configuration complète IPWatch"""
model_config = {"arbitrary_types_allowed": True}
app: AppConfig = Field(default_factory=AppConfig)
network: NetworkConfig
subnets: List[SubnetConfig] = Field(default_factory=list)
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)
links: LinksConfig = Field(default_factory=LinksConfig)
colors: ColorsConfig = Field(default_factory=ColorsConfig)
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
opnsense: OPNsenseConfig = Field(default_factory=OPNsenseConfig)
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()