API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com
1# ============================================2# Projet : API-KA3# Fichier : src/config.py4# Node : m3u96b5# Author : Simon-Pierre Boucher6# Contact : contact@spboucher.ai7# Date : 2026-08-168# ============================================9"""Configuration centrale d'API-KA : chargement du .env et vérification du node m3u96b."""1011from __future__ import annotations1213import os14import socket15from dataclasses import dataclass16from functools import lru_cache17from pathlib import Path1819from dotenv import load_dotenv2021REQUIRED_NODE = "m3u96b"2223SERVICES: tuple[str, ...] = (24 "louka",25 "immoka",26 "foodka",27 "autoka",28 "fabrika",29 "restoka",30 "sortika",31 "creaka",32 "jobka",33)3435SERVICE_TABLES: dict[str, str] = {service: f"{service}_data" for service in SERVICES}3637SERVICE_SOURCE_ENV: dict[str, str] = {38 "louka": "LOUKA_SOURCE_URL",39 "immoka": "IMMOKA_SOURCE_URL",40 "foodka": "FOODKA_SOURCE_URL",41 "autoka": "AUTOKA_SOURCE_URL",42 "fabrika": "FABRIKA_SOURCE_URL",43 "restoka": "RESTOKA_SOURCE_URL",44 "sortika": "SORTIKA_SOURCE_URL",45 "creaka": "CREAKA_SOURCE_URL",46 "jobka": "JOBKA_SOURCE_URL",47}4849BASE_DIR = Path(__file__).resolve().parents[1]5051load_dotenv(BASE_DIR / ".env")525354def verify_node() -> None:55 """Refuse de démarrer en production si le hostname n'est pas m3u96b.5657 Le suffixe éventuel (ex. ``.local``) est ignoré et la comparaison est58 insensible à la casse, mais la règle reste stricte : en production,59 API-KA ne tourne que sur le node m3u96b.60 """61 hostname = socket.gethostname()62 if os.getenv("APP_ENV") == "production":63 short = hostname.split(".")[0].lower()64 if short != REQUIRED_NODE:65 raise RuntimeError(66 f"API-KA doit tourner sur {REQUIRED_NODE}, node actuel : {hostname}"67 )686970@dataclass(frozen=True)71class Settings:72 """Configuration immuable de la plateforme, chargée depuis le .env."""7374 app_env: str75 node_name: str76 database_url: str77 ngrok_authtoken: str78 ngrok_domain: str79 api_port: int80 daily_run_hour: int81 backup_retention_days: int82 rate_limit_per_minute: int83 trouveka_search_url: str84 trouveka_suggest_url: str85 source_urls: dict[str, str]86 base_dir: Path87 logs_dir: Path88 backups_dir: Path899091@lru_cache(maxsize=1)92def get_settings() -> Settings:93 """Charge et met en cache la configuration depuis les variables d'environnement."""94 logs_dir = BASE_DIR / "logs"95 backups_dir = BASE_DIR / "data" / "backups"96 logs_dir.mkdir(parents=True, exist_ok=True)97 backups_dir.mkdir(parents=True, exist_ok=True)9899 default_db = f"sqlite:///{BASE_DIR / 'data' / 'apika_dev.db'}"100 return Settings(101 app_env=os.getenv("APP_ENV", "development"),102 node_name=os.getenv("NODE_NAME", REQUIRED_NODE),103 database_url=os.getenv("DATABASE_URL", default_db),104 ngrok_authtoken=os.getenv("NGROK_AUTHTOKEN", ""),105 ngrok_domain=os.getenv("NGROK_DOMAIN", "www.api-ka.com"),106 api_port=int(os.getenv("API_PORT", "8000")),107 daily_run_hour=int(os.getenv("DAILY_RUN_HOUR", "02")),108 backup_retention_days=max(90, int(os.getenv("BACKUP_RETENTION_DAYS", "90"))),109 rate_limit_per_minute=int(os.getenv("RATE_LIMIT_PER_MINUTE", "120")),110 trouveka_search_url=os.getenv(111 "TROUVEKA_SEARCH_URL", "https://www.trouve-ka.com/api/search"112 ),113 trouveka_suggest_url=os.getenv(114 "TROUVEKA_SUGGEST_URL", "https://www.trouve-ka.com/api/suggest"115 ),116 source_urls={117 service: os.getenv(env_var, "")118 for service, env_var in SERVICE_SOURCE_ENV.items()119 },120 base_dir=BASE_DIR,121 logs_dir=logs_dir,122 backups_dir=backups_dir,123 )124