From d046893165234998c572a000a3647758677bc38e Mon Sep 17 00:00:00 2001 From: denis0001-dev Date: Sun, 5 Oct 2025 22:44:19 +0300 Subject: [PATCH] Fix database migration --- backend/app.py | 29 +++++++++++++++++------------ backend/migration.py | 32 +++++++++++++++++++++++++++++++- backend/models.py | 1 - package.json | 2 +- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/backend/app.py b/backend/app.py index 0570982..f5f42ab 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,25 +1,30 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -import logging from contextlib import asynccontextmanager -from migration import run_migrations +import subprocess +import sys +import os from routes import account, messaging, profile, push -# Configure logging -logger = logging.getLogger("uvicorn.error") -logger.handlers.clear() - @asynccontextmanager async def lifespan(app: FastAPI): - """Handle application lifespan events.""" - # Startup + # Startup - run migration in separate process to avoid logging interference try: - logger.info("Starting database migration check...") - run_migrations() - logger.info("Database migrations completed successfully.") + print("Starting database migration check...") + # Run migration in a separate process + subprocess.run( + [ + sys.executable, + "-c", + "import sys; sys.path.append('.'); from migration import run_migrations; run_migrations()" + ], + cwd=os.path.dirname(os.path.abspath(__file__)) + # No capture_output - let it stream to terminal in real-time + # No text=True - let it use the terminal's encoding + ) except Exception as e: - logger.error(f"Failed to run database migrations: {e}") + print(f"Failed to run database migrations: {e}") raise yield diff --git a/backend/migration.py b/backend/migration.py index fc016c1..3bba779 100644 --- a/backend/migration.py +++ b/backend/migration.py @@ -9,8 +9,9 @@ from alembic.config import Config from alembic.runtime.migration import MigrationContext from sqlalchemy import create_engine from constants import DATABASE_URL +import logging -logger = logging.getLogger("uvicorn.error") +logger = logging.getLogger(__name__) def run_migrations(): """ @@ -25,6 +26,9 @@ def run_migrations(): # Create Alembic configuration alembic_cfg = Config(os.path.join(current_dir, "alembic.ini")) + # Disable Alembic's logging configuration to avoid interfering with FastAPI + alembic_cfg.set_main_option("configure_logging", "false") + # Set the database URL in the config alembic_cfg.set_main_option("sqlalchemy.url", DATABASE_URL) @@ -71,6 +75,32 @@ def run_migrations(): # Create fresh migration command.revision(alembic_cfg, autogenerate=True, message="Initial migration") logger.info("Initial migration created successfully.") + else: + # Migration files exist, check if we need to create a new migration for schema changes + logger.info("Migration files exist. Checking for pending schema changes...") + try: + # Create a new migration to detect any schema changes + command.revision(alembic_cfg, autogenerate=True, message="Auto-generated migration for schema changes") + + # Check if the new migration is empty (no changes detected) + migration_files = [f for f in os.listdir(versions_dir) if f.endswith('.py') and not f.startswith('__')] + if migration_files: + latest_migration = max(migration_files) + migration_path = os.path.join(versions_dir, latest_migration) + + # Check if migration is empty + with open(migration_path, 'r') as f: + content = f.read() + if 'pass' in content and 'op.create_table' not in content and 'op.add_column' not in content and 'op.drop_table' not in content and 'op.drop_column' not in content: + logger.info("No schema changes detected. Removing empty migration...") + # Remove the empty migration + os.remove(migration_path) + else: + logger.info("Schema changes detected. New migration created.") + + except Exception as e: + logger.info(f"No new migrations needed or error creating migration: {e}") + pass # Run the upgrade command logger.info("Running database migrations...") diff --git a/backend/models.py b/backend/models.py index 620fe14..8681b51 100644 --- a/backend/models.py +++ b/backend/models.py @@ -2,7 +2,6 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, inspect, null, text from sqlalchemy.orm import relationship from datetime import datetime -from db import engine from pydantic import BaseModel Base = declarative_base() diff --git a/package.json b/package.json index ab204c5..4fb02cb 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "license": "GPL-3.0", "authors": "denis0001-dev", "scripts": { - "backend:run": "cd backend && dotenv -e ../deployment/.env -- ../.venv/bin/fastapi dev --port 8300 main.py", + "backend:run": "cd backend && dotenv -e ../deployment/.env -- ../.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8300 --reload --reload-exclude './alembic' --reload-exclude './alembic/*' --reload-exclude './alembic/versions/*' --access-log", "backend:dependencies": "python3 -m venv .venv && ./.venv/bin/pip3 install -r backend/requirements.txt", "backend:reinstall": "rm -rf .venv && npm run backend:dependencies", "backend:clean": "rm -rf backend/data",