- Removed 11 duplicate router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py) - Removed duplicate core files (logging_config.py, settings.py) - Removed unused services (card_mirror_service.py, mtgjson_loader.py, mtgjson_uploader.py) - Removed unused schemas (proto_messages.py, protocol_constants.py) - Created run.py startup script for simple execution - Created app/core/config.py for unified configuration management - Created app/core/logging.py for logging configuration - Consolidated routers into 6 focused files (auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py) - Updated all imports to use consolidated model paths - Added backward compatibility aliases in database.py - Created SIMPLIFICATION_SUMMARY.md documentation Architecture improvements: - Single unified database engine with auto-detection (SQLite/PostgreSQL) - Consolidated models into 2 files (models.py, mtg_models.py) - Clean router structure with one file per domain - Simplified configuration with sensible defaults - SQLite by default for development, PostgreSQL for production Testing: - All imports verified working - App loads successfully - Ready for development and testing
194 lines
5.4 KiB
Python
194 lines
5.4 KiB
Python
"""
|
|
Database engine and session management.
|
|
|
|
Provides a single SQLAlchemy async engine supporting both SQLite (development)
|
|
and PostgreSQL (production) via a single DATABASE_URL configuration value.
|
|
|
|
## Usage
|
|
|
|
from app.core.database import get_db, create_tables, Base
|
|
|
|
# In FastAPI dependency:
|
|
async def endpoint(db: AsyncSession = Depends(get_db)):
|
|
...
|
|
|
|
# Create all tables:
|
|
await create_tables()
|
|
|
|
## Architecture
|
|
|
|
- Single engine for all database operations (no dual databases)
|
|
- Sync DeclarativeBase for model definitions
|
|
- Async engine and sessions for async operations
|
|
- Automatic dialect detection (SQLite vs PostgreSQL)
|
|
|
|
## Backward Compatibility
|
|
|
|
The following exports are provided for backward compatibility with code that
|
|
still references the old dual-database naming convention:
|
|
|
|
- ``mtg_engine`` → alias for ``engine``
|
|
- ``mtg_async_session`` → alias for ``async_session``
|
|
- ``mtg_get_db`` → alias for ``get_db``
|
|
- ``mirror_get_db`` → alias for ``get_db``
|
|
|
|
These should be removed as callers are migrated to the new unified API.
|
|
"""
|
|
|
|
import logging
|
|
from typing import AsyncGenerator
|
|
|
|
from sqlalchemy.ext.asyncio import (
|
|
AsyncSession,
|
|
async_sessionmaker,
|
|
create_async_engine,
|
|
)
|
|
from sqlalchemy.orm import DeclarativeBase
|
|
|
|
from app.core.config import get_config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
config = get_config()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Engine setup — single engine, auto-detect SQLite vs PostgreSQL
|
|
# ---------------------------------------------------------------------------
|
|
|
|
engine = create_async_engine(
|
|
config.DATABASE_URL,
|
|
echo=config.DEBUG,
|
|
# SQLite doesn't support pool_size / max_overflow — only apply for PG
|
|
**(
|
|
{
|
|
"pool_pre_ping": True,
|
|
"pool_size": 20,
|
|
"max_overflow": 10,
|
|
}
|
|
if not config.is_sqlite
|
|
else {
|
|
"pool_pre_ping": False,
|
|
}
|
|
),
|
|
)
|
|
|
|
async_session = async_sessionmaker(
|
|
engine,
|
|
class_=AsyncSession,
|
|
expire_on_commit=False,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Base class for ORM models
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
"""Base class for all ORM models.
|
|
|
|
Uses sync DeclarativeBase (not async-specific) for simplicity.
|
|
All models inherit from this class.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# FastAPI dependency — provides a database session
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
"""FastAPI dependency that provides a database session.
|
|
|
|
Yields an async session and handles commit/rollback/close automatically.
|
|
|
|
Usage in FastAPI endpoints:
|
|
|
|
from fastapi import Depends
|
|
from app.core.database import get_db
|
|
|
|
@app.get("/items")
|
|
async def list_items(db: AsyncSession = Depends(get_db)):
|
|
...
|
|
|
|
Yields:
|
|
An async SQLAlchemy session.
|
|
"""
|
|
async with async_session() as session:
|
|
try:
|
|
yield session
|
|
await session.commit()
|
|
except Exception:
|
|
await session.rollback()
|
|
raise
|
|
finally:
|
|
await session.close()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Table creation helper
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def create_tables() -> None:
|
|
"""Create all database tables from model metadata.
|
|
|
|
Imports all model modules to ensure Base.metadata is populated,
|
|
then creates all tables. This is intended for development and testing.
|
|
In production, use Alembic migrations instead.
|
|
|
|
Example:
|
|
>>> await create_tables()
|
|
"""
|
|
# Import all models to register them with Base.metadata
|
|
import app.models # noqa: F401
|
|
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
logger.info(
|
|
f"Database tables created successfully "
|
|
f"(dialect: {'SQLite' if config.is_sqlite else 'PostgreSQL'})"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Backward-compatible aliases
|
|
#
|
|
# The old codebase referenced dual databases (mtg_engine, mtg_async_session,
|
|
# mtg_get_db, mirror_get_db). These aliases map them to the single unified
|
|
# engine so existing imports continue to work. They will be removed once all
|
|
# callers have been migrated.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Alias: mtg_engine → engine (was the "primary" database engine)
|
|
mtg_engine = engine
|
|
|
|
# Alias: mtg_async_session → async_session (was the "primary" session factory)
|
|
mtg_async_session = async_session
|
|
|
|
# Alias: mtg_get_db → get_db (was the "primary" session dependency)
|
|
mtg_get_db = get_db
|
|
|
|
# Alias: mirror_get_db → get_db (was the "mirror" database session dependency)
|
|
mirror_get_db = get_db
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Exports
|
|
# ---------------------------------------------------------------------------
|
|
|
|
__all__ = [
|
|
"Base",
|
|
"engine",
|
|
"async_session",
|
|
"get_db",
|
|
"create_tables",
|
|
# Backward-compatible aliases
|
|
"mtg_engine",
|
|
"mtg_async_session",
|
|
"mtg_get_db",
|
|
"mirror_get_db",
|
|
]
|