"""Настройки Synapse. Всё из окружения (.env), никаких секретов в коде."""
from functools import lru_cache
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
BASE_DIR = Path(__file__).resolve().parent.parent
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
app_name: str = "Gnexus Synapse"
database_url: str = "postgresql+psycopg://synapse:synapse@localhost:5432/synapse"
redis_url: str = "redis://localhost:6379/0"
# gnexus-auth (SSO). Креды OAuth-клиента synapse — только из окружения
# (зарегистрировать клиент на сервере gnexus-auth и заполнить .env).
gauth_base_url: str = "https://192.168.1.167"
gauth_client_id: str = "set-me"
gauth_client_secret: str = "set-me"
# Куда gnexus-auth возвращает пользователя — наш API-эндпоинт callback'а.
gauth_redirect_uri: str = "http://localhost:8013/auth/callback"
# gnexus-auth в LAN может сидеть за self-signed TLS (по умолчанию не проверяем),
# в проде с нормальным сертификатом выставить GAUTH_VERIFY_TLS=true.
gauth_verify_tls: bool = False
# Секрет подписи вебхуков gnexus-auth (виден только при создании/ротации клиента).
gauth_webhook_secret: str = ""
# Публичный URL SPA для редиректа после callback. По умолчанию пусто =
# тот же origin, что и API (прод: SPA раздаётся этим же контейнером).
# Для dev-сервера vite указать SPA_PUBLIC_URL=http://localhost:5174.
spa_public_url: str = ""
# Окно дедупликации событий по dedup_key (docs/05): дубль с тем же ключом
# от источника внутри окна вернёт id первого события (deduplicated: true).
dedup_window_seconds: int = 24 * 3600
# Режим маршрутизации (docs/05, «Типы и правила»):
# all — применяются ВСЕ подошедшие правила (каждое добавляет доставки)
# first — только первое подошедшее по weight
routing_match_mode: str = "all"
# Ретеншн (beat-задача synapse.expire_events): события старше N дней
# удаляются вместе с доставками; ttl_seconds удаляет раньше — по своей
# шкале. 0 — окно по возрасту отключено (ttl продолжит работать).
retention_days: int = 30
# SPA лежит в образе рядом с приложением; в dev используется vite-сервер.
spa_dist_dir: Path = BASE_DIR / "spa_static"
# MCP-сервер (docs/07): статический Bearer-токен для ИИ-агента.
# Пусто — эндпоинт /mcp не монтируется. Генерация: openssl rand -hex 32.
mcp_token: str = ""
# CORS для dev-режима (vite :5173); в проде SPA раздаётся с того же origin.
cors_origins: list[str] = ["http://localhost:5173"]
def gauth_configured(self) -> bool:
return self.gauth_client_id != "set-me" and self.gauth_client_secret != "set-me"
@lru_cache
def get_settings() -> Settings:
return Settings()