153 lines
4.5 KiB
Python
153 lines
4.5 KiB
Python
import logging
|
|
from typing import Generator, AsyncGenerator
|
|
from contextlib import asynccontextmanager
|
|
|
|
from sqlalchemy import create_engine, event
|
|
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
|
from sqlalchemy.pool import StaticPool
|
|
from sqlalchemy.engine import Engine
|
|
import time
|
|
|
|
from .config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Configure SQLAlchemy engine with connection pooling
|
|
engine = create_engine(
|
|
settings.SQLALCHEMY_DATABASE_URI,
|
|
echo=settings.DEBUG,
|
|
pool_size=10,
|
|
max_overflow=20,
|
|
pool_pre_ping=True, # Validates connections before use
|
|
pool_recycle=3600, # Recycle connections every hour
|
|
connect_args={
|
|
"connect_timeout": 10,
|
|
"application_name": settings.PANEL_NAME,
|
|
}
|
|
)
|
|
|
|
# Add connection event listeners for monitoring
|
|
@event.listens_for(Engine, "before_cursor_execute")
|
|
def before_cursor_execute(conn, cursor, statement, parameters, context, executemany):
|
|
context._query_start_time = time.time()
|
|
if settings.DEBUG:
|
|
logger.debug("Start Query: %s", statement)
|
|
|
|
|
|
@event.listens_for(Engine, "after_cursor_execute")
|
|
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
|
|
total = time.time() - context._query_start_time
|
|
if settings.DEBUG:
|
|
logger.debug("Query Complete in %.3f seconds", total)
|
|
if total > 1.0: # Log slow queries
|
|
logger.warning("Slow query detected (%.3f seconds): %s", total, statement)
|
|
|
|
|
|
SessionLocal = sessionmaker(
|
|
autocommit=False,
|
|
autoflush=False,
|
|
bind=engine,
|
|
)
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
def get_db() -> Generator[Session, None, None]:
|
|
"""Dependency to get database session"""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
except Exception as e:
|
|
logger.error(f"Database session error: {e}")
|
|
db.rollback()
|
|
raise
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def get_async_db() -> AsyncGenerator[Session, None]:
|
|
"""Async context manager for database session"""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
db.commit()
|
|
except Exception as e:
|
|
logger.error(f"Database session error: {e}")
|
|
db.rollback()
|
|
raise
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def init_db() -> None:
|
|
"""Initialize database tables"""
|
|
try:
|
|
Base.metadata.create_all(bind=engine)
|
|
logger.info("Database initialized successfully")
|
|
|
|
# Initialize default panel settings if not exist
|
|
init_default_settings()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to initialize database: {e}")
|
|
raise
|
|
|
|
|
|
def init_default_settings() -> None:
|
|
"""Initialize default panel settings"""
|
|
try:
|
|
from .models import PanelSettings
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
# Check if settings already exist
|
|
existing = db.query(PanelSettings).count()
|
|
if existing > 0:
|
|
logger.info(f"Panel settings already initialized ({existing} settings found)")
|
|
return
|
|
|
|
# Add default settings
|
|
defaults = [
|
|
PanelSettings(
|
|
key="refresh_interval",
|
|
value="5",
|
|
description="Интервал обновления данных на панели (секунды)"
|
|
),
|
|
PanelSettings(
|
|
key="log_retention_days",
|
|
value="30",
|
|
description="Срок хранения логов перед автоматической очисткой (дни)"
|
|
),
|
|
PanelSettings(
|
|
key="language",
|
|
value="ru",
|
|
description="Язык интерфейса панели (ru/en)"
|
|
),
|
|
]
|
|
|
|
for setting in defaults:
|
|
db.add(setting)
|
|
|
|
db.commit()
|
|
logger.info(f"Added {len(defaults)} default panel settings")
|
|
|
|
finally:
|
|
db.close()
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Failed to initialize default settings: {e}")
|
|
# Don't raise - this is not critical for app startup
|
|
|
|
|
|
def check_db_connection() -> bool:
|
|
"""Check if database connection is working"""
|
|
try:
|
|
with engine.connect() as conn:
|
|
from sqlalchemy import text
|
|
conn.execute(text("SELECT 1"))
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Database connection check failed: {e}")
|
|
return False
|