From 99e7592cdc2d2921c407c00b81f5f36bc4a8fc7b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Nov 2025 13:29:29 +0000 Subject: [PATCH] fix: Add automatic database migration for missing columns - Add run_migrations() function that runs at app startup - Automatically adds price_selector column to search_sites if missing - Prevents UndefinedColumn errors on existing databases --- app/main.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/main.py b/app/main.py index 8d78da9..266956e 100644 --- a/app/main.py +++ b/app/main.py @@ -9,7 +9,9 @@ from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded +from sqlalchemy import text +from app.database import engine from app.limiter import limiter from app.routers import items, jobs, notifications, openrouter, search, search_sites, settings from app.services.scheduler_service import scheduled_refresh, scheduler @@ -22,8 +24,34 @@ logging.basicConfig( logger = logging.getLogger(__name__) +def run_migrations(): + """Run database migrations for missing columns""" + migrations = [ + # Add price_selector to search_sites if missing + ("search_sites", "price_selector", "ALTER TABLE search_sites ADD COLUMN price_selector VARCHAR(512)"), + ] + + with engine.connect() as conn: + for table, column, sql in migrations: + result = conn.execute(text( + f"SELECT 1 FROM information_schema.columns WHERE table_name = '{table}' AND column_name = '{column}'" + )) + if not result.fetchone(): + logger.info(f"Adding missing column: {table}.{column}") + conn.execute(text(sql)) + conn.commit() + + @asynccontextmanager async def lifespan(app: FastAPI): + # Run database migrations + logger.info("Running database migrations...") + try: + run_migrations() + logger.info("Database migrations completed") + except Exception as e: + logger.error(f"Migration error: {e}") + logger.info("Starting smart scheduler (Heartbeat: 1 minute)") scheduler.add_job(scheduled_refresh, IntervalTrigger(minutes=1), id="refresh_job", replace_existing=True) scheduler.start()