From d983854c7c8d668161cca0e150093803d1145fe9 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Tue, 2 Dec 2025 16:30:34 +1100 Subject: [PATCH] Add python script for applying migrations --- migrations/003_create_login_history.sql | 16 +++++++ run_migration.py | 55 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 migrations/003_create_login_history.sql create mode 100644 run_migration.py diff --git a/migrations/003_create_login_history.sql b/migrations/003_create_login_history.sql new file mode 100644 index 0000000..0bd6f5e --- /dev/null +++ b/migrations/003_create_login_history.sql @@ -0,0 +1,16 @@ +-- Create login_history table to track user logins +CREATE TABLE IF NOT EXISTS login_history ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + login_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + ip_address VARCHAR(45), -- IPv6 max length is 45 characters + user_agent TEXT, + success BOOLEAN NOT NULL DEFAULT TRUE, + failure_reason VARCHAR(255) +); + +-- Create index on user_id for faster queries +CREATE INDEX IF NOT EXISTS idx_login_history_user_id ON login_history(user_id); + +-- Create index on login_time for sorting +CREATE INDEX IF NOT EXISTS idx_login_history_login_time ON login_history(login_time DESC); diff --git a/run_migration.py b/run_migration.py new file mode 100644 index 0000000..670f191 --- /dev/null +++ b/run_migration.py @@ -0,0 +1,55 @@ +""" +Database migration runner +Execute SQL migration files using the Flask app's database connection +""" +import os +import sys + +# Add parent directory to path to import app +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from extensions import db as database + +def run_migration(migration_file): + """Execute a SQL migration file""" + try: + # Read migration file + migration_path = os.path.join('migrations', migration_file) + if not os.path.exists(migration_path): + print(f"ERROR: Migration file not found: {migration_path}") + sys.exit(1) + + with open(migration_path, 'r') as f: + sql = f.read() + + # Execute migration using the database connection + conn = database.pool.getconn() + cursor = conn.cursor() + + try: + cursor.execute(sql) + conn.commit() + print(f"✓ Successfully executed migration: {migration_file}") + except Exception as e: + conn.rollback() + print(f"ERROR executing migration: {e}") + sys.exit(1) + finally: + cursor.close() + database.pool.putconn(conn) + + except Exception as e: + print(f"ERROR: {e}") + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python run_migration.py ") + print("Example: python run_migration.py 003_create_login_history.sql") + sys.exit(1) + + # Initialize the app to set up database connection + from app import app + with app.app_context(): + migration_file = sys.argv[1] + run_migration(migration_file)