Files
Deal/archive/leadradar-legacy/backend/app/config.py
T
Rustam Khalimov 9e07568ddd Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
2026-09-11 02:50:17 +03:00

100 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Конфигурация из переменных окружения.
По ТЗ в 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