From 22651647cbf241bed4e3d7c731458d933de05ed0 Mon Sep 17 00:00:00 2001 From: Lars Date: Sat, 21 Mar 2026 10:07:37 +0100 Subject: [PATCH] fix: add automatic migration system to db_init.py Added migration tracking and execution to db_init.py: - Created schema_migrations table to track applied migrations - Added run_migrations() to automatically apply pending SQL files - Migrations from backend/migrations/*.sql are now applied on startup This fixes the missing email verification columns (migration 003). Co-Authored-By: Claude Opus 4.6 --- backend/db_init.py | 107 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/backend/db_init.py b/backend/db_init.py index 8d7a663..07a16cd 100644 --- a/backend/db_init.py +++ b/backend/db_init.py @@ -91,6 +91,107 @@ def get_profile_count(): print(f"Error getting profile count: {e}") return -1 +def ensure_migration_table(): + """Create migration tracking table if it doesn't exist.""" + try: + conn = get_connection() + cur = conn.cursor() + cur.execute(""" + CREATE TABLE IF NOT EXISTS schema_migrations ( + id SERIAL PRIMARY KEY, + filename VARCHAR(255) UNIQUE NOT NULL, + applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP + ) + """) + conn.commit() + cur.close() + conn.close() + return True + except Exception as e: + print(f"Error creating migration table: {e}") + return False + +def get_applied_migrations(): + """Get list of already applied migrations.""" + try: + conn = get_connection() + cur = conn.cursor() + cur.execute("SELECT filename FROM schema_migrations ORDER BY filename") + migrations = [row[0] for row in cur.fetchall()] + cur.close() + conn.close() + return migrations + except Exception as e: + print(f"Error getting applied migrations: {e}") + return [] + +def apply_migration(filepath, filename): + """Apply a single migration file.""" + try: + with open(filepath, 'r') as f: + migration_sql = f.read() + + conn = get_connection() + cur = conn.cursor() + + # Execute migration + cur.execute(migration_sql) + + # Record migration + cur.execute( + "INSERT INTO schema_migrations (filename) VALUES (%s)", + (filename,) + ) + + conn.commit() + cur.close() + conn.close() + print(f" ✓ Applied: {filename}") + return True + except Exception as e: + print(f" ✗ Failed to apply {filename}: {e}") + return False + +def run_migrations(migrations_dir="/app/migrations"): + """Run all pending migrations.""" + import glob + + if not os.path.exists(migrations_dir): + print("✓ No migrations directory found") + return True + + # Ensure migration tracking table exists + if not ensure_migration_table(): + return False + + # Get already applied migrations + applied = get_applied_migrations() + + # Get all migration files + migration_files = sorted(glob.glob(os.path.join(migrations_dir, "*.sql"))) + + if not migration_files: + print("✓ No migration files found") + return True + + # Apply pending migrations + pending = [] + for filepath in migration_files: + filename = os.path.basename(filepath) + if filename not in applied: + pending.append((filepath, filename)) + + if not pending: + print(f"✓ All {len(applied)} migrations already applied") + return True + + print(f" Found {len(pending)} pending migration(s)...") + for filepath, filename in pending: + if not apply_migration(filepath, filename): + return False + + return True + if __name__ == "__main__": print("═══════════════════════════════════════════════════════════") print("MITAI JINKENDO - Database Initialization (v9c)") @@ -109,6 +210,12 @@ if __name__ == "__main__": else: print("✓ Schema already exists") + # Run migrations + print("\nRunning database migrations...") + if not run_migrations(): + print("✗ Migration failed") + sys.exit(1) + # Check for migration print("\nChecking for SQLite data migration...") sqlite_db = "/app/data/bodytrack.db"