import os from os.path import join, dirname from typing import Any, Dict, Optional from .setup_logger import logger from dotenv import load_dotenv def _required(name: str) -> str: """ Devuelve el valor de la variable de entorno `name`. Lanza RuntimeError si no existe o está vacía. """ value = os.getenv(name) if value is None or value == "": # Comentario: fail fast con mensaje claro raise RuntimeError(f"Missing required environment variable: {name}") return value def load_config() -> Dict[str, Any]: """ Carga la configuración desde variables de entorno. - En dev: carga dev.env y luego valida. - En production: NO carga ningún .env, solo usa entorno del sistema/contendor. - Si falta alguna variable requerida -> RuntimeError. """ env = os.getenv("ENV", "development") if env == "development": dotenv_path = join(dirname(__file__), "../../enviroment/dev.env") load_dotenv(dotenv_path) elif env == "production": # En producción NO se carga archivo .env # Las variables vienen del contenedor (docker run -e VAR=...) pass else: raise RuntimeError(f"Unknown ENV: {env}") config: Dict[str, Any] = { # Opcionales (con valor por defecto) "ENV": env, "LOCAL_TZ": os.getenv("LOCAL_TZ", "Europe/Madrid"), "LAST_RUN_PATH": _required("LAST_RUN_PATH"), # FACTUGES (requeridas) "FACTUGES_HOST": _required("FACTUGES_HOST"), "FACTUGES_PORT": _required("FACTUGES_PORT"), "FACTUGES_DATABASE": _required("FACTUGES_DATABASE"), "FACTUGES_USER": _required("FACTUGES_USER"), "FACTUGES_PASSWORD": _required("FACTUGES_PASSWORD"), # UECKO MySQL (requeridas salvo puerto) "FWEB_MYSQL_HOST": _required("FWEB_MYSQL_HOST"), "FWEB_MYSQL_PORT": os.getenv("FWEB_MYSQL_PORT", "3306"), "FWEB_MYSQL_DATABASE": _required("FWEB_MYSQL_DATABASE"), "FWEB_MYSQL_USER": _required("FWEB_MYSQL_USER"), "FWEB_MYSQL_PASSWORD": _required("FWEB_MYSQL_PASSWORD"), # Constantes/CTE (requeridas) "CTE_COMPANY_ID": _required("CTE_COMPANY_ID"), "CTE_SERIE": _required("CTE_SERIE"), "CTE_STATUS_INVOICE": _required("CTE_STATUS_INVOICE"), "CTE_IS_PROFORMA": _required("CTE_IS_PROFORMA"), "CTE_STATUS_VERIFACTU": _required("CTE_STATUS_VERIFACTU"), "CTE_LANGUAGE_CODE": _required("CTE_LANGUAGE_CODE"), "CTE_COUNTRY_CODE": _required("CTE_COUNTRY_CODE"), "CTE_IS_COMPANY": _required("CTE_IS_COMPANY"), "CTE_SYNC_RESULT_OK": _required("CTE_SYNC_RESULT_OK"), "CTE_SYNC_RESULT_FAIL": _required("CTE_SYNC_RESULT_FAIL"), # Verifactu (requeridas) "VERIFACTU_BASE_URL": _required("VERIFACTU_BASE_URL"), "VERIFACTU_API_KEY": _required("VERIFACTU_API_KEY"), "VERIFACTU_NIFS_API_KEY": _required("VERIFACTU_NIFS_API_KEY"), } return config def log_config(config: dict) -> None: """Loguea la configuración con una clave por línea.""" lines = ["Loaded configuration:"] for key in sorted(config.keys()): value = config[key] if any(t in key.lower() for t in ("pass", "password", "api_key", "token", "secret")): value = "***" lines.append(f" {key} = {value!r}") logger.info("\n".join(lines))