refactor: replace universal_migration.py with Alembic
Remove the 7,791-line universal_migration.py and 16 incomplete individual Alembic migrations. Replace with a single initial schema migration using Base.metadata.create_all(checkfirst=True). Changes: - Add programmatic Alembic runner (app/database/migrations.py) with auto-stamp logic for existing databases transitioning from universal_migration - Extract ensure_default_web_api_token() to web_api_token_service.py - Extract sync_postgres_sequences() to database.py with SQL injection prevention via _quote_ident() - Add HMAC token hashing support with backward-compatible dual-hash fallback and automatic rehashing - Remove dead init_db() function and unused imports - Add Makefile targets: migrate, migration, migrate-stamp, migrate-history - Fix fileConfig() destroying structlog config (disable_existing_loggers) - Remove duplicate migrations/alembic/alembic.ini with credentials - Add script.py.mako template for future migration generation - Update startup flow: alembic upgrade → sync sequences → ensure token - Harden database.py: ParamSpec for retry decorator, safe URL logging, echo='debug' mode, execute_with_retry validation - Update documentation references 31 files changed, 302 insertions(+), 9,226 deletions(-)
This commit is contained in:
@@ -12,9 +12,9 @@ sys.path.append(str(Path(__file__).parent))
|
||||
|
||||
from app.bot import setup_bot
|
||||
from app.config import settings
|
||||
from app.database.database import init_db
|
||||
from app.database.database import sync_postgres_sequences
|
||||
from app.database.migrations import run_alembic_upgrade
|
||||
from app.database.models import PaymentMethod
|
||||
from app.database.universal_migration import run_universal_migration
|
||||
from app.localization.loader import ensure_locale_templates
|
||||
from app.logging_config import setup_logging
|
||||
from app.services.backup_service import backup_service
|
||||
@@ -41,6 +41,7 @@ from app.services.reporting_service import reporting_service
|
||||
from app.services.system_settings_service import bot_configuration_service
|
||||
from app.services.traffic_monitoring_service import traffic_monitoring_scheduler
|
||||
from app.services.version_service import version_service
|
||||
from app.services.web_api_token_service import ensure_default_web_api_token
|
||||
from app.utils.log_handlers import ExcludePaymentFilter, LevelFilterHandler
|
||||
from app.utils.payment_logger import configure_payment_logger
|
||||
from app.utils.startup_timeline import StartupTimeline
|
||||
@@ -179,42 +180,43 @@ async def main():
|
||||
summary_logged = False
|
||||
|
||||
try:
|
||||
async with timeline.stage('Инициализация базы данных', '🗄️', success_message='База данных готова'):
|
||||
await init_db()
|
||||
|
||||
skip_migration = os.getenv('SKIP_MIGRATION', 'false').lower() == 'true'
|
||||
|
||||
if not skip_migration:
|
||||
async with timeline.stage(
|
||||
'Проверка и миграция базы данных',
|
||||
'Миграция базы данных (Alembic)',
|
||||
'🧬',
|
||||
success_message='Миграция завершена успешно',
|
||||
) as stage:
|
||||
try:
|
||||
migration_log = logging.getLogger('app.database.universal_migration')
|
||||
original_level = migration_log.level
|
||||
migration_log.setLevel(logging.WARNING)
|
||||
try:
|
||||
migration_success = await run_universal_migration()
|
||||
finally:
|
||||
migration_log.setLevel(original_level)
|
||||
if migration_success:
|
||||
stage.success('Миграция завершена успешно')
|
||||
else:
|
||||
stage.warning('Миграция завершилась с предупреждениями, запуск продолжится')
|
||||
logger.warning('⚠️ Миграция завершилась с предупреждениями, но продолжаем запуск')
|
||||
await run_alembic_upgrade()
|
||||
stage.success('Миграция завершена успешно')
|
||||
except Exception as migration_error:
|
||||
stage.warning(f'Ошибка выполнения миграции: {migration_error}')
|
||||
logger.error('❌ Ошибка выполнения миграции', migration_error=migration_error)
|
||||
logger.warning('⚠️ Продолжаем запуск без миграции')
|
||||
allow_failure = os.getenv('ALLOW_MIGRATION_FAILURE', 'false').lower() == 'true'
|
||||
logger.error('Ошибка выполнения миграции', migration_error=migration_error)
|
||||
if not allow_failure:
|
||||
raise
|
||||
stage.warning(f'Ошибка миграции: {migration_error} (ALLOW_MIGRATION_FAILURE=true)')
|
||||
else:
|
||||
timeline.add_manual_step(
|
||||
'Проверка и миграция базы данных',
|
||||
'Миграция базы данных (Alembic)',
|
||||
'⏭️',
|
||||
'Пропущено',
|
||||
'SKIP_MIGRATION=true',
|
||||
)
|
||||
|
||||
async with timeline.stage(
|
||||
'Инициализация базы данных',
|
||||
'🗄️',
|
||||
success_message='База данных готова',
|
||||
) as stage:
|
||||
seq_ok = await sync_postgres_sequences()
|
||||
token_ok = await ensure_default_web_api_token()
|
||||
if not seq_ok:
|
||||
stage.warning('Не удалось синхронизировать последовательности PostgreSQL')
|
||||
if not token_ok:
|
||||
stage.warning('Не удалось создать/проверить дефолтный веб-API токен')
|
||||
|
||||
async with timeline.stage(
|
||||
'Синхронизация тарифов из конфига',
|
||||
'💰',
|
||||
|
||||
Reference in New Issue
Block a user