diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..49fb9b5 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,42 @@ +[alembic] +script_location = migrations +prepend_sys_path = . +path_separator = os +file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(rev)s_%%(slug)s +timezone = Europe/Moscow + +sqlalchemy.url = sqlite+aiosqlite:///tgbot/data/database.db + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/dj_bot.conf b/dj_bot.conf new file mode 100644 index 0000000..19e317a --- /dev/null +++ b/dj_bot.conf @@ -0,0 +1,13 @@ +# supervisorctl +[program:dj_shop] +directory=/root/autoshopDjimbo/ +command=python3.11 -u main.py +environment=PYTHONUNBUFFERED="1" + +autostart=True +autorestart=True + +stderr_logfile=/root/autoshopDjimbo/tgbot/data/sv_log_err.log +stderr_logfile_maxbytes=50MB +stdout_logfile=/root/autoshopDjimbo/tgbot/data/sv_log_out.log +stdout_logfile_maxbytes=50MB diff --git a/pyproject.toml b/pyproject.toml index 719ea59..f67cd4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "aiofiles>=25.1,<26.0", "aiohttp>=3.13.5,<4.0", "aiosqlite>=0.20,<1.0", + "alembic>=1.13,<2.0", "cachetools>=7.1,<8.0", "certifi>=2025.8,<2027.0", "colorama>=0.4,<1.0", diff --git a/requirements.txt b/requirements.txt index 667fa9f..2ffa36b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ aiogram==3.28.2 aiofiles==25.1.0 aiohttp==3.13.5 aiosqlite==0.22.1 +alembic==1.18.4 cachetools==7.1.4 certifi==2026.5.20 colorama==0.4.6 diff --git a/tgbot/database/migration_runner.py b/tgbot/database/migration_runner.py new file mode 100644 index 0000000..3d08120 --- /dev/null +++ b/tgbot/database/migration_runner.py @@ -0,0 +1,53 @@ +# - *- coding: utf- 8 - *- +import asyncio +from pathlib import Path +from typing import Optional + +from alembic import command +from alembic.config import Config +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import AsyncEngine + +from tgbot.database.core import database_url + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +ALEMBIC_INI = PROJECT_ROOT / "alembic.ini" + + +# Сбор Alembic-конфига от корня проекта +def get_alembic_config(url: str = database_url) -> Config: + config = Config(str(ALEMBIC_INI)) + config.set_main_option("sqlalchemy.url", url) + + return config + + +# Применение миграций до последней версии +async def run_migrations(engine: Optional[AsyncEngine] = None) -> None: + if engine is None: + config = get_alembic_config() + await asyncio.to_thread(command.upgrade, config, "head") + return + + config = get_alembic_config(str(engine.url)) + + connection = engine.connect() + await connection.start() + transaction = connection.begin() + await transaction.start() + + try: + await connection.run_sync(_upgrade_with_connection, config) + except Exception: + await transaction.rollback() + raise + else: + await transaction.commit() + finally: + await connection.close() + + +# Запуск Alembic на готовом соединении +def _upgrade_with_connection(connection: Connection, config: Config) -> None: + config.attributes["connection"] = connection + command.upgrade(config, "head")