Newer
Older
gnexus-tasks / backend / app / db.py
"""Подключение к БД и базовый класс моделей (SQLAlchemy 2.0, sync)."""

import json
from collections.abc import Generator
from typing import Any

from sqlalchemy import Engine, create_engine
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker

from app.config import get_settings


class Base(DeclarativeBase):
    pass


def json_serializer(obj: Any) -> str:
    r"""Сериализация JSON-колонок: кириллица без \u-эскейпов (видят MCP-агенты)."""
    return json.dumps(obj, ensure_ascii=False)


_engine: Engine | None = None
_session_factory: sessionmaker[Session] | None = None


def get_engine() -> Engine:
    global _engine
    if _engine is None:
        _engine = create_engine(
            get_settings().database_url,
            pool_pre_ping=True,
            # ensure_ascii=False: иначе кириллица в JSON-колонках (ai_proposal,
            # и т.п.) уезжает в в-эскейпы — MCP-агент видит «u0432» вместо букв
            json_serializer=json_serializer,
        )
    return _engine


def get_session_factory() -> sessionmaker[Session]:
    global _session_factory
    if _session_factory is None:
        _session_factory = sessionmaker(bind=get_engine(), expire_on_commit=False)
    return _session_factory


def get_db() -> Generator[Session, None, None]:
    session = get_session_factory()()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()