"""Конфигурация из переменных окружения. По ТЗ в env выносятся ТОЛЬКО неконфиденциальные параметры: порты, пути к БД/данным/сессиям. Секреты (Telegram api_id/hash, ключи AI, пароль дашборда) живут в базе и задаются через UI. """ from __future__ import annotations import os from pathlib import Path def _bool(name: str, default: bool = False) -> bool: raw = os.getenv(name) if raw is None: return default return raw.strip().lower() in {"1", "true", "yes", "on"} def _int(name: str, default: int) -> int: try: return int(os.getenv(name, str(default))) except ValueError: return default # Корень данных (том в docker-compose). Внутри лежат leadradar.duckdb, # telegram-сессии, файлы вложений (позже — MinIO), секрет подписи сессий. DATA_DIR = Path(os.getenv("LEADRADAR_DATA", str(Path(__file__).resolve().parent.parent / "data"))).resolve() DB_PATH = DATA_DIR / os.getenv("LEADRADAR_DB_NAME", "leadradar.duckdb") SESSIONS_DIR = DATA_DIR / "telegram_sessions" FILES_DIR = DATA_DIR / "attachments" SECRET_FILE = DATA_DIR / "session_secret.key" # Хост/порт HTTP-сервера HOST = os.getenv("LEADRADAR_HOST", "0.0.0.0") PORT = _int("LEADRADAR_PORT", 8000) # Через env допустимо задать стартовый секрет (например, из секрет-менеджера), # но если он не задан — будет создан случайный и сохранён в SECRET_FILE. SESSION_SECRET = os.getenv("LEADRADAR_SESSION_SECRET", "") or None # Учётные данные первого входа можно выставить и в env (удобно для развёртывания), # НО по умолчанию система стартует с admin/admin и требует смены пароля. BOOTSTRAP_LOGIN = os.getenv("LEADRADAR_BOOTSTRAP_LOGIN", "admin") BOOTSTRAP_PASSWORD = os.getenv("LEADRADAR_BOOTSTRAP_PASSWORD", "admin") # Шифрование секретов в БД. По договорённости ключ живёт в env; # при локальной разработке без env создаётся файл data/encryption.key. ENCRYPTION_KEY = os.getenv("LEADRADAR_ENCRYPTION_KEY", "") or None ENCRYPTION_KEY_FILE = DATA_DIR / "encryption.key" # MinIO (вложения; креды по договорённости — в env) MINIO_ENDPOINT = os.getenv("LEADRADAR_MINIO_ENDPOINT", "") MINIO_ACCESS_KEY = os.getenv("LEADRADAR_MINIO_ACCESS_KEY", "") MINIO_SECRET_KEY = os.getenv("LEADRADAR_MINIO_SECRET_KEY", "") MINIO_BUCKET = os.getenv("LEADRADAR_MINIO_BUCKET", "leadradar") MINIO_SECURE = _bool("LEADRADAR_MINIO_SECURE", False) # Автономный ML-сервис (отдельный контейнер). Если недоступен — модель не # используется в пайплайне, а события обучения копятся в outbox до его возврата. ML_URL = os.getenv("LEADRADAR_ML_URL", "http://127.0.0.1:8100").rstrip("/") ML_TIMEOUT = _int("LEADRADAR_ML_TIMEOUT", 30) # обучение батчами — тяжёлое, таймаут щедрый # Куки COOKIE_NAME = "leadradar_session" SESSION_DAYS = 30 # Telegram-сессия (Telethon) SESSION_PREFIX = "user" # Путь к статике фронтенда (собранный dist). Если папки нет — API работает # отдельно (фронт поднимается dev-сервером), статика просто не раздаётся. FRONTEND_DIST = Path(os.getenv("LEADRADAR_FRONTEND_DIST", str(Path(__file__).resolve().parent.parent.parent / "frontend" / "dist"))).resolve() # Логи LOG_LEVEL = os.getenv("LEADRADAR_LOG_LEVEL", "INFO") # Демо-эндпоинты (simulate-lead, age-lead). В проде выключено; # включается только для локальных тестов разработчика: LEADRADAR_DEMO=1 DEMO_ENABLED = _bool("LEADRADAR_DEMO", False) def ensure_dirs() -> None: DATA_DIR.mkdir(parents=True, exist_ok=True) SESSIONS_DIR.mkdir(parents=True, exist_ok=True) FILES_DIR.mkdir(parents=True, exist_ok=True) def get_or_create_secret() -> str: if SESSION_SECRET: return SESSION_SECRET if SECRET_FILE.exists(): return SECRET_FILE.read_text(encoding="utf-8").strip() import secrets secret = secrets.token_hex(32) SECRET_FILE.write_text(secret, encoding="utf-8") return secret