| """ |
| Async Database Setup |
| Supports PostgreSQL (local dev) and SQLite (HF Spaces) via DATABASE_URL. |
| """ |
| from __future__ import annotations |
|
|
| import os |
| import structlog |
| from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker |
| from sqlalchemy.orm import DeclarativeBase |
|
|
| from app.core.config import settings |
|
|
| logger = structlog.get_logger(__name__) |
|
|
| |
| connect_args = {} |
| if settings.use_sqlite: |
| connect_args["check_same_thread"] = False |
|
|
| engine = create_async_engine( |
| settings.DATABASE_URL, |
| echo=settings.DEBUG, |
| connect_args=connect_args, |
| ) |
|
|
| AsyncSessionLocal = async_sessionmaker( |
| bind=engine, |
| class_=AsyncSession, |
| expire_on_commit=False, |
| autocommit=False, |
| autoflush=False, |
| ) |
|
|
|
|
| class Base(DeclarativeBase): |
| """Base class for all SQLAlchemy models.""" |
| pass |
|
|
|
|
| async def init_db() -> None: |
| """Create all tables on startup (ensures all tables exist in PG & SQLite).""" |
| |
| from app.models import tenant, user, audit, document, chat |
|
|
| if settings.use_sqlite: |
| os.makedirs("./data", exist_ok=True) |
|
|
| async with engine.begin() as conn: |
| await conn.run_sync(Base.metadata.create_all) |
| logger.info("Database tables initialized successfully", db=settings.DATABASE_URL) |
|
|
|
|
| async def get_db() -> AsyncSession: |
| """FastAPI dependency: provides async DB session.""" |
| async with AsyncSessionLocal() as session: |
| try: |
| yield session |
| await session.commit() |
| except Exception: |
| await session.rollback() |
| raise |
| finally: |
| await session.close() |
|
|