""" GUARDiA ITSM — 데이터베이스 엔진 설정. 환경변수 DATABASE_URL 로 DB를 자동 선택한다: - 미설정(기본): SQLite + aiosqlite (개발/단독 실행) - postgresql+asyncpg://user:pass@host:5432/db: PostgreSQL (운영) PostgreSQL 사용 시 asyncpg, alembic 패키지가 필요하다: pip install asyncpg alembic 마이그레이션: alembic init migrations alembic revision --autogenerate -m "initial_schema" alembic upgrade head """ from __future__ import annotations import logging import os from sqlalchemy.ext.asyncio import ( AsyncSession, async_sessionmaker, create_async_engine, ) from sqlalchemy.orm import DeclarativeBase logger = logging.getLogger(__name__) # ── 환경변수 기반 DB URL 자동 선택 ─────────────────────────────────────────── DATABASE_URL: str = os.getenv( "DATABASE_URL", "sqlite+aiosqlite:///./guardia_itsm.db", # 기본: SQLite (개발) ) # URL 정규화: postgres:// → postgresql+asyncpg:// (Heroku/Gitpod 호환) if DATABASE_URL.startswith("postgres://"): DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+asyncpg://", 1) _is_postgres = DATABASE_URL.startswith("postgresql") # ── 엔진 생성 ──────────────────────────────────────────────────────────────── _engine_kwargs: dict = { "echo": os.getenv("DB_ECHO", "false").lower() == "true", "pool_pre_ping": True, } if _is_postgres: # PostgreSQL: MVCC 기반 연결 풀링 _engine_kwargs["pool_size"] = int(os.getenv("DB_POOL_SIZE", "10")) _engine_kwargs["max_overflow"] = int(os.getenv("DB_MAX_OVERFLOW", "20")) logger.info("GUARDiA DB: PostgreSQL 모드 (pool_size=%s)", _engine_kwargs["pool_size"]) else: # SQLite: 파일 잠금 방식, 오버플로 불가 _engine_kwargs["connect_args"] = {"check_same_thread": False} logger.info("GUARDiA DB: SQLite 모드") engine = create_async_engine(DATABASE_URL, **_engine_kwargs) SessionLocal = async_sessionmaker( engine, class_=AsyncSession, expire_on_commit=False, ) # ── Base ───────────────────────────────────────────────────────────────────── class Base(DeclarativeBase): pass # ── 의존성 주입 ─────────────────────────────────────────────────────────────── async def get_db(): """FastAPI Depends용 — DB 세션 제공 후 자동 정리.""" async with SessionLocal() as session: yield session # ── 테이블 초기화 ───────────────────────────────────────────────────────────── async def init_db() -> None: """ 개발 환경에서 테이블 자동 생성. 운영(PostgreSQL)에서는 Alembic migration을 사용하므로 create_all은 이미 존재하는 테이블을 건드리지 않는다. """ from models import Base as ModelBase # noqa: F401 — 모델 등록용 임포트 async with engine.begin() as conn: await conn.run_sync(ModelBase.metadata.create_all) logger.info("DB 테이블 초기화 완료")