- Introduced endpoints for user management, including CRUD operations, pagination, and password management. - Added new schema validations for user updates, password strength, pagination, and standardized error responses. - Integrated custom exception handling for a consistent API error experience. - Refined CORS settings: restricted methods and allowed headers, added header exposure, and preflight caching. - Optimized database: added indexes on `is_active` and `is_superuser` fields, updated column types, enforced constraints, and set defaults. - Updated `Dockerfile` to improve security by using a non-root user and adding a health check for the application. - Enhanced tests for database initialization, user operations, and exception handling to ensure better coverage.
112 lines
3.2 KiB
Python
112 lines
3.2 KiB
Python
# app/core/database.py
|
|
import logging
|
|
from contextlib import contextmanager
|
|
from typing import Generator
|
|
from sqlalchemy import create_engine, text
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from sqlalchemy.orm import sessionmaker, Session
|
|
from sqlalchemy.ext.compiler import compiles
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
|
|
from app.core.config import settings
|
|
|
|
# Configure logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# SQLite compatibility for testing
|
|
@compiles(JSONB, 'sqlite')
|
|
def compile_jsonb_sqlite(type_, compiler, **kw):
|
|
return "TEXT"
|
|
|
|
@compiles(UUID, 'sqlite')
|
|
def compile_uuid_sqlite(type_, compiler, **kw):
|
|
return "TEXT"
|
|
|
|
# Declarative base for models
|
|
Base = declarative_base()
|
|
|
|
# Create engine with optimized settings for PostgreSQL
|
|
def create_production_engine():
|
|
return create_engine(
|
|
settings.database_url,
|
|
# Connection pool settings
|
|
pool_size=settings.db_pool_size,
|
|
max_overflow=settings.db_max_overflow,
|
|
pool_timeout=settings.db_pool_timeout,
|
|
pool_recycle=settings.db_pool_recycle,
|
|
pool_pre_ping=True,
|
|
# Query execution settings
|
|
connect_args={
|
|
"application_name": "eventspace",
|
|
"keepalives": 1,
|
|
"keepalives_idle": 60,
|
|
"keepalives_interval": 10,
|
|
"keepalives_count": 5,
|
|
"options": "-c timezone=UTC",
|
|
},
|
|
isolation_level="READ COMMITTED",
|
|
echo=settings.sql_echo,
|
|
echo_pool=settings.sql_echo_pool,
|
|
)
|
|
|
|
# Default production engine and session factory
|
|
engine = create_production_engine()
|
|
SessionLocal = sessionmaker(
|
|
autocommit=False,
|
|
autoflush=False,
|
|
bind=engine,
|
|
expire_on_commit=False # Prevent unnecessary queries after commit
|
|
)
|
|
|
|
# FastAPI dependency
|
|
def get_db() -> Generator[Session, None, None]:
|
|
"""
|
|
FastAPI dependency that provides a database session.
|
|
Automatically closes the session after the request completes.
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@contextmanager
|
|
def transaction_scope() -> Generator[Session, None, None]:
|
|
"""
|
|
Provide a transactional scope for database operations.
|
|
|
|
Automatically commits on success or rolls back on exception.
|
|
Useful for grouping multiple operations in a single transaction.
|
|
|
|
Usage:
|
|
with transaction_scope() as db:
|
|
user = user_crud.create(db, obj_in=user_create)
|
|
profile = profile_crud.create(db, obj_in=profile_create)
|
|
# Both operations committed together
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
db.commit()
|
|
logger.debug("Transaction committed successfully")
|
|
except Exception as e:
|
|
db.rollback()
|
|
logger.error(f"Transaction failed, rolling back: {str(e)}")
|
|
raise
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def check_database_health() -> bool:
|
|
"""
|
|
Check if database connection is healthy.
|
|
Returns True if connection is successful, False otherwise.
|
|
"""
|
|
try:
|
|
with transaction_scope() as db:
|
|
db.execute(text("SELECT 1"))
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Database health check failed: {str(e)}")
|
|
return False |