# rag/alembic/env.py # -*- coding: utf-8 -*- """ Среда выполнения Alembic для управления схемой PostgreSQL. Загружает конфигурацию из проекта (AppConfig) для получения строки подключения. """ import asyncio import os import sys from logging.config import fileConfig from sqlalchemy import engine_from_config, pool from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine from alembic import context # Добавляем путь к проекту (чтобы импортировать модули) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from rag.config_models import AppConfig from rag.utils.config_loader import load_config # Это объект конфигурации Alembic, предоставляющий доступ к значениям из alembic.ini config = context.config # Настройка логирования (если указано в alembic.ini) if config.config_file_name is not None: fileConfig(config.config_file_name) # Метаданные моделей (если используются SQLAlchemy ORM) – пока None target_metadata = None def get_app_config() -> AppConfig: """ Загружает конфигурацию проекта из переменной окружения или из стандартного пути. """ profile_dir = os.environ.get('FCKBOT_PROFILE_DIR') if profile_dir: return load_config(profile_dir) # Если не задано, пробуем стандартные пути (для разработки) possible_paths = [ '/usr/local/etc/fckbot/bots/metabot', # по умолчанию методолог '/usr/local/etc/fckbot/profiles/metabot', './profiles/metabot', ] for path in possible_paths: if os.path.exists(path): try: return load_config(path) except Exception: continue raise RuntimeError( "Не удалось загрузить конфигурацию проекта. " "Установите переменную окружения FCKBOT_PROFILE_DIR с путём к профилю бота." ) def run_migrations_offline() -> None: """ Запуск миграций в офлайн-режиме (без подключения к БД). """ app_config = get_app_config() url = app_config.db_url # должно быть свойство, которое формирует URL context.configure( url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, ) with context.begin_transaction(): context.run_migrations() async def run_migrations_online() -> None: """ Запуск миграций в онлайн-режиме (с подключением к БД). """ app_config = get_app_config() url = app_config.db_url # Создаём асинхронный движок connectable = create_async_engine( url, poolclass=pool.NullPool, ) async with connectable.connect() as connection: await connection.run_sync(do_run_migrations) await connectable.dispose() def do_run_migrations(connection): """ Выполняет миграции на синхронном соединении (обёртка для асинхронного). """ context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): context.run_migrations() if context.is_offline_mode(): run_migrations_offline() else: asyncio.run(run_migrations_online())