SPB Git forge

spb/api-ka

Public

API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com

48commits 1branches 0releases
5.9 MBsize
maindefault branch
19 days agolast push
Python 60.9% HTML 21% TypeScript 7.3% JavaScript 5.2% CSS 4.8% Shell 0.8%
4.3 KB · 137 lines python
Raw Blame History
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_dotenv2021from src.ka_registry import resolve_source_url2223# Nœud imposé (optionnel) : APIKA_REQUIRED_NODE=m3u96b ; vide = tourne sur n'importe quel nœud (placement par maclustr-dispatch).24REQUIRED_NODE = os.getenv("APIKA_REQUIRED_NODE", "").strip().lower()2526SERVICES: tuple[str, ...] = (27    "louka",28    "immoka",29    "foodka",30    "autoka",31    "fabrika",32    "restoka",33    "sortika",34    "creaka",35    "jobka",36    "houseka",37    "rentka",38)3940SERVICE_TABLES: dict[str, str] = {service: f"{service}_data" for service in SERVICES}4142SERVICE_SOURCE_ENV: dict[str, str] = {43    "louka": "LOUKA_SOURCE_URL",44    "immoka": "IMMOKA_SOURCE_URL",45    "foodka": "FOODKA_SOURCE_URL",46    "autoka": "AUTOKA_SOURCE_URL",47    "fabrika": "FABRIKA_SOURCE_URL",48    "restoka": "RESTOKA_SOURCE_URL",49    "sortika": "SORTIKA_SOURCE_URL",50    "creaka": "CREAKA_SOURCE_URL",51    "jobka": "JOBKA_SOURCE_URL",52    "houseka": "HOUSEKA_SOURCE_URL",53    "rentka": "RENTKA_SOURCE_URL",54}5556BASE_DIR = Path(__file__).resolve().parents[1]5758load_dotenv(BASE_DIR / ".env")596061def verify_node() -> None:62    """Refuse de démarrer en production si le hostname n'est pas m3u96b.6364    Le suffixe éventuel (ex. ``.local``) est ignoré et la comparaison est65    insensible à la casse, mais la règle reste stricte : en production,66    API-KA ne tourne que sur le node m3u96b.67    """68    hostname = socket.gethostname()69    if os.getenv("APP_ENV") == "production" and REQUIRED_NODE:70        short = hostname.split(".")[0].lower()71        if short != REQUIRED_NODE:72            raise RuntimeError(73                f"API-KA doit tourner sur {REQUIRED_NODE}, node actuel : {hostname}"74            )757677@dataclass(frozen=True)78class Settings:79    """Configuration immuable de la plateforme, chargée depuis le .env."""8081    app_env: str82    node_name: str83    database_url: str84    ngrok_authtoken: str85    ngrok_domain: str86    api_port: int87    daily_run_hour: int88    backup_retention_days: int89    rate_limit_per_minute: int90    trouveka_search_url: str91    trouveka_suggest_url: str92    source_urls: dict[str, str]93    base_dir: Path94    logs_dir: Path95    backups_dir: Path969798@lru_cache(maxsize=1)99def get_settings() -> Settings:100    """Charge et met en cache la configuration depuis les variables d'environnement."""101    logs_dir = BASE_DIR / "logs"102    backups_dir = BASE_DIR / "data" / "backups"103    logs_dir.mkdir(parents=True, exist_ok=True)104    backups_dir.mkdir(parents=True, exist_ok=True)105106    default_db = f"sqlite:///{BASE_DIR / 'data' / 'apika_dev.db'}"107    return Settings(108        app_env=os.getenv("APP_ENV", "development"),109        node_name=os.getenv("NODE_NAME", REQUIRED_NODE),110        database_url=os.getenv("DATABASE_URL", default_db),111        ngrok_authtoken=os.getenv("NGROK_AUTHTOKEN", ""),112        ngrok_domain=os.getenv("NGROK_DOMAIN", "www.api-ka.com"),113        api_port=int(os.getenv("API_PORT", "8000")),114        daily_run_hour=int(os.getenv("DAILY_RUN_HOUR", "02")),115        backup_retention_days=max(90, int(os.getenv("BACKUP_RETENTION_DAYS", "90"))),116        rate_limit_per_minute=int(os.getenv("RATE_LIMIT_PER_MINUTE", "120")),117        trouveka_search_url=os.getenv(118            "TROUVEKA_SEARCH_URL", "https://www.trouve-ka.com/api/search"119        ),120        trouveka_suggest_url=os.getenv(121            "TROUVEKA_SUGGEST_URL", "https://www.trouve-ka.com/api/suggest"122        ),123        source_urls={124            service: resolve_source_url(service, os.getenv(env_var, ""))125            for service, env_var in SERVICE_SOURCE_ENV.items()126        },127        base_dir=BASE_DIR,128        logs_dir=logs_dir,129        backups_dir=backups_dir,130    )131132133def source_url(service: str) -> str:134    """URL de collecte LIVE d'un service : registre maclustr-dispatch (suit les135    ``mld move``), repli sur ``<SERVICE>_SOURCE_URL`` du .env."""136    return resolve_source_url(service, os.getenv(SERVICE_SOURCE_ENV.get(service, ""), ""))137