# [DEF:backend.src.core.database:Module] # # @SEMANTICS: database, sqlite, sqlalchemy, session, persistence # @PURPOSE: Configures the SQLite database connection and session management. # @LAYER: Core # @RELATION: DEPENDS_ON -> sqlalchemy # @RELATION: USES -> backend.src.models.mapping # @RELATION: USES -> backend.src.core.auth.config # # @INVARIANT: A single engine instance is used for the entire application. # [SECTION: IMPORTS] from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, Session from ..models.mapping import Base # Import models to ensure they're registered with Base from ..models.task import TaskRecord from ..models.connection import ConnectionConfig from ..models.git import GitServerConfig, GitRepository, DeploymentEnvironment from ..models.auth import User, Role, Permission, ADGroupMapping from ..models.llm import LLMProvider, ValidationRecord from .logger import belief_scope from .auth.config import auth_config import os from pathlib import Path # [/SECTION] # [DEF:BASE_DIR:Variable] # @PURPOSE: Base directory for the backend (where .db files should reside). BASE_DIR = Path(__file__).resolve().parent.parent.parent # [/DEF:BASE_DIR:Variable] # [DEF:DATABASE_URL:Constant] # @PURPOSE: URL for the main mappings database. DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite:///{BASE_DIR}/mappings.db") # [/DEF:DATABASE_URL:Constant] # [DEF:TASKS_DATABASE_URL:Constant] # @PURPOSE: URL for the tasks execution database. TASKS_DATABASE_URL = os.getenv("TASKS_DATABASE_URL", f"sqlite:///{BASE_DIR}/tasks.db") # [/DEF:TASKS_DATABASE_URL:Constant] # [DEF:AUTH_DATABASE_URL:Constant] # @PURPOSE: URL for the authentication database. AUTH_DATABASE_URL = os.getenv("AUTH_DATABASE_URL", auth_config.AUTH_DATABASE_URL) # If it's a relative sqlite path starting with ./backend/, fix it to be absolute or relative to BASE_DIR if AUTH_DATABASE_URL.startswith("sqlite:///./backend/"): AUTH_DATABASE_URL = AUTH_DATABASE_URL.replace("sqlite:///./backend/", f"sqlite:///{BASE_DIR}/") elif AUTH_DATABASE_URL.startswith("sqlite:///./") and not AUTH_DATABASE_URL.startswith("sqlite:///./backend/"): # If it's just ./ but we are in backend, it's fine, but let's make it absolute for robustness AUTH_DATABASE_URL = AUTH_DATABASE_URL.replace("sqlite:///./", f"sqlite:///{BASE_DIR}/") # [/DEF:AUTH_DATABASE_URL:Constant] # [DEF:engine:Variable] # @PURPOSE: SQLAlchemy engine for mappings database. engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) # [/DEF:engine:Variable] # [DEF:tasks_engine:Variable] # @PURPOSE: SQLAlchemy engine for tasks database. tasks_engine = create_engine(TASKS_DATABASE_URL, connect_args={"check_same_thread": False}) # [/DEF:tasks_engine:Variable] # [DEF:auth_engine:Variable] # @PURPOSE: SQLAlchemy engine for authentication database. auth_engine = create_engine(AUTH_DATABASE_URL, connect_args={"check_same_thread": False}) # [/DEF:auth_engine:Variable] # [DEF:SessionLocal:Class] # @PURPOSE: A session factory for the main mappings database. # @PRE: engine is initialized. SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # [/DEF:SessionLocal:Class] # [DEF:TasksSessionLocal:Class] # @PURPOSE: A session factory for the tasks execution database. # @PRE: tasks_engine is initialized. TasksSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=tasks_engine) # [/DEF:TasksSessionLocal:Class] # [DEF:AuthSessionLocal:Class] # @PURPOSE: A session factory for the authentication database. # @PRE: auth_engine is initialized. AuthSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=auth_engine) # [/DEF:AuthSessionLocal:Class] # [DEF:init_db:Function] # @PURPOSE: Initializes the database by creating all tables. # @PRE: engine, tasks_engine and auth_engine are initialized. # @POST: Database tables created in all databases. # @SIDE_EFFECT: Creates physical database files if they don't exist. def init_db(): with belief_scope("init_db"): Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=tasks_engine) Base.metadata.create_all(bind=auth_engine) # [/DEF:init_db:Function] # [DEF:get_db:Function] # @PURPOSE: Dependency for getting a database session. # @PRE: SessionLocal is initialized. # @POST: Session is closed after use. # @RETURN: Generator[Session, None, None] def get_db(): with belief_scope("get_db"): db = SessionLocal() try: yield db finally: db.close() # [/DEF:get_db:Function] # [DEF:get_tasks_db:Function] # @PURPOSE: Dependency for getting a tasks database session. # @PRE: TasksSessionLocal is initialized. # @POST: Session is closed after use. # @RETURN: Generator[Session, None, None] def get_tasks_db(): with belief_scope("get_tasks_db"): db = TasksSessionLocal() try: yield db finally: db.close() # [/DEF:get_tasks_db:Function] # [DEF:get_auth_db:Function] # @PURPOSE: Dependency for getting an authentication database session. # @PRE: AuthSessionLocal is initialized. # @POST: Session is closed after use. # @RETURN: Generator[Session, None, None] def get_auth_db(): with belief_scope("get_auth_db"): db = AuthSessionLocal() try: yield db finally: db.close() # [/DEF:get_auth_db:Function] # [/DEF:backend.src.core.database:Module]