klg-asutk-app/backend/app/db/session.py
Yuriy aa052763f6 Безопасность и качество: 8 исправлений + обновления
- .env.example: полный шаблон, защита секретов
- .gitignore: явное исключение .env.* и секретов
- layout.tsx: XSS — заменён dangerouslySetInnerHTML на next/script для SW
- ESLint: no-console error (allow warn/error), ignore scripts/
- scripts/remove-console-logs.js: очистка console.log без glob
- backend/routes/modules: README с планом рефакторинга крупных файлов
- SECURITY.md: гид по секретам, XSS, CORS, auth, линту
- .husky/pre-commit: запуск npm run lint

+ прочие правки приложения и бэкенда

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:29:16 +03:00

67 lines
2.2 KiB
Python

"""
Database session management.
Sync engine for Alembic migrations + async-compatible session for routes.
Production: use connection pool with proper limits.
"""
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker, Session
from app.core.config import settings
# ---------------------------------------------------------------------------
# Engine configuration
# ---------------------------------------------------------------------------
_is_sqlite = "sqlite" in (settings.database_url or "")
_connect_args = {"check_same_thread": False} if _is_sqlite else {}
engine = create_engine(
settings.database_url,
pool_pre_ping=not _is_sqlite,
connect_args=_connect_args,
# Production pool settings for multi-user
pool_size=20 if not _is_sqlite else 5,
max_overflow=10 if not _is_sqlite else 0,
pool_timeout=30,
pool_recycle=1800, # recycle connections every 30 min
echo=False,
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# ---------------------------------------------------------------------------
# Multi-tenancy: set current org_id on connection (for RLS)
# ---------------------------------------------------------------------------
@event.listens_for(engine, "checkout")
def _reset_tenant(dbapi_conn, connection_record, connection_proxy):
"""Reset tenant context on connection checkout from pool."""
cursor = dbapi_conn.cursor()
try:
cursor.execute("SET LOCAL app.current_org_id = ''")
except Exception:
pass # SQLite doesn't support SET LOCAL
finally:
cursor.close()
def set_tenant(db: Session, org_id: str | None):
"""Set the current tenant for RLS policies."""
if org_id and not _is_sqlite:
db.execute(
__import__("sqlalchemy").text(
f"SET LOCAL app.current_org_id = '{org_id}'"
)
)
# ---------------------------------------------------------------------------
# Dependency injection
# ---------------------------------------------------------------------------
def get_db():
"""FastAPI dependency: yields a DB session, closes on exit."""
db = SessionLocal()
try:
yield db
finally:
db.close()