Newer
Older
gn-synapse / app / config.py
"""Настройки 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"

    # 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()