v1.8.3: добавлена директория migrations/ и migrations_runner.py для SQL-миграций
This commit is contained in:
@@ -41,6 +41,11 @@ async def lifespan(app: FastAPI):
|
|||||||
logger.info("Database tables ensured.")
|
logger.info("Database tables ensured.")
|
||||||
|
|
||||||
await _apply_migrations()
|
await _apply_migrations()
|
||||||
|
|
||||||
|
# Применение SQL-миграций из директории migrations/
|
||||||
|
from app.migrations_runner import run_migrations
|
||||||
|
await run_migrations()
|
||||||
|
|
||||||
await _seed_default_settings()
|
await _seed_default_settings()
|
||||||
await _register_webhook()
|
await _register_webhook()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Автоматические миграции БД для max_bot.
|
||||||
|
|
||||||
|
При старте приложения:
|
||||||
|
1. Создаёт таблицу _applied_migrations (если не существует)
|
||||||
|
2. Проверяет какие SQL-файлы уже применены
|
||||||
|
3. Применяет новые миграции по порядку
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from sqlalchemy import text
|
||||||
|
from app.database import engine
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
MIGRATIONS_DIR = os.path.join(os.path.dirname(__file__), "migrations")
|
||||||
|
|
||||||
|
|
||||||
|
async def run_migrations():
|
||||||
|
"""Запуск всех неприменённых миграций из директории migrations/."""
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
# Создаём таблицу для отслеживания миграций
|
||||||
|
await conn.execute(text("""
|
||||||
|
CREATE TABLE IF NOT EXISTS _applied_migrations (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
filename VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
applied_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
"""))
|
||||||
|
|
||||||
|
# Получаем список уже применённых миграций
|
||||||
|
result = await conn.execute(text("SELECT filename FROM _applied_migrations"))
|
||||||
|
applied = {row[0] for row in result.all()}
|
||||||
|
|
||||||
|
# Сканируем директорию миграций
|
||||||
|
if not os.path.isdir(MIGRATIONS_DIR):
|
||||||
|
logger.info("Директория migrations/ не найдена, пропускаем")
|
||||||
|
return
|
||||||
|
|
||||||
|
sql_files = sorted(f for f in os.listdir(MIGRATIONS_DIR) if f.endswith(".sql"))
|
||||||
|
|
||||||
|
for filename in sql_files:
|
||||||
|
if filename in applied:
|
||||||
|
continue
|
||||||
|
|
||||||
|
filepath = os.path.join(MIGRATIONS_DIR, filename)
|
||||||
|
with open(filepath, "r", encoding="utf-8") as f:
|
||||||
|
sql = f.read()
|
||||||
|
|
||||||
|
logger.info(f"Применение миграции: {filename}")
|
||||||
|
try:
|
||||||
|
await conn.execute(text(sql))
|
||||||
|
await conn.execute(
|
||||||
|
text("INSERT INTO _applied_migrations (filename) VALUES (:fn)"),
|
||||||
|
{"fn": filename}
|
||||||
|
)
|
||||||
|
logger.info(f"Миграция применена: {filename}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Миграция {filename} уже применена или ошибка: {e}")
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- Миграция: consent_revoked_date + bot_processing_logs
|
||||||
|
-- Дата: 02.06.2026
|
||||||
|
-- Описание: Добавление поля consent_revoked_date в bot_users и создание таблицы bot_processing_logs
|
||||||
|
|
||||||
|
-- 1. Consent revoked date
|
||||||
|
ALTER TABLE bot_users ADD COLUMN IF NOT EXISTS consent_revoked_date TIMESTAMP NULL;
|
||||||
|
|
||||||
|
-- 2. BotProcessingLog table
|
||||||
|
CREATE TABLE IF NOT EXISTS bot_processing_logs (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL REFERENCES bot_users(id) ON DELETE CASCADE,
|
||||||
|
conversation_id BIGINT REFERENCES bot_conversations(id) ON DELETE SET NULL,
|
||||||
|
step VARCHAR(50) NOT NULL,
|
||||||
|
status VARCHAR(20) DEFAULT 'ok',
|
||||||
|
message TEXT,
|
||||||
|
detail TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для производительности
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_bot_processing_logs_user_id ON bot_processing_logs(user_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_bot_processing_logs_conversation_id ON bot_processing_logs(conversation_id);
|
||||||
Reference in New Issue
Block a user