Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| migrate_users.py | |
| Migrates users from SQLite (users.db) to PostgreSQL (TimescaleDB). | |
| SAFE operation: Reads from SQLite, Inserts into Postgres. Does not delete SQLite data. | |
| """ | |
| import sqlite3 | |
| import psycopg2 | |
| import os | |
| import sys | |
| import logging | |
| from datetime import datetime | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| # Paths | |
| SQLITE_DB_PATH = 'users.db' | |
| # Configuration (mirrors setup_timescaledb.py logic) | |
| def get_postgres_connection(): | |
| try: | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) | |
| try: | |
| import config | |
| host = getattr(config, 'DB_HOST', 'localhost') | |
| port = getattr(config, 'DB_PORT', 5432) | |
| dbname = getattr(config, 'DB_NAME', 'morphguard') | |
| user = getattr(config, 'DB_USER', 'morphguard') | |
| password = getattr(config, 'DB_PASSWORD', 'morphguard') | |
| except ImportError: | |
| # Fallback to env vars or strict defaults matching deploy scripts | |
| host = os.environ.get('MORPHGUARD_DB_HOST', 'localhost') | |
| port = int(os.environ.get('MORPHGUARD_DB_PORT', 5432)) | |
| dbname = os.environ.get('MORPHGUARD_DB_NAME', 'morphguard') | |
| user = os.environ.get('MORPHGUARD_DB_USER', 'morphguard') | |
| password = os.environ.get('MORPHGUARD_DB_PASS', 'morphguard') | |
| logger.info(f"Connecting to Postgres at {host}:{port} db={dbname} user={user}...") | |
| conn = psycopg2.connect( | |
| host=host, port=port, dbname=dbname, user=user, password=password | |
| ) | |
| return conn | |
| except Exception as e: | |
| logger.error(f"Failed to connect to Postgres: {e}") | |
| return None | |
| def migrate(): | |
| if not os.path.exists(SQLITE_DB_PATH): | |
| logger.error(f"SQLite database not found at {SQLITE_DB_PATH}") | |
| return | |
| # Connect to SQLite | |
| try: | |
| sqlite_conn = sqlite3.connect(SQLITE_DB_PATH) | |
| sqlite_conn.row_factory = sqlite3.Row | |
| sqlite_cur = sqlite_conn.cursor() | |
| logger.info("Connected to SQLite") | |
| except Exception as e: | |
| logger.error(f"Failed to connect to SQLite: {e}") | |
| return | |
| # Connect to Postgres | |
| pg_conn = get_postgres_connection() | |
| if not pg_conn: | |
| return | |
| pg_cur = pg_conn.cursor() | |
| try: | |
| # Fetch all users from SQLite | |
| sqlite_cur.execute("SELECT * FROM users") | |
| users = sqlite_cur.fetchall() | |
| logger.info(f"Found {len(users)} users in SQLite database.") | |
| count = 0 | |
| for user in users: | |
| # Map fields | |
| # SQLite: id, username, password_hash, role, email, created_at (REAL), last_login (REAL), active (INT) | |
| # Postgres: id (SERIAL), username, password_hash, role, email, created_at (TIMESTAMPTZ), last_login (TIMESTAMPTZ), active (BOOL) | |
| username = user['username'] | |
| # Check if user exists in Postgres | |
| pg_cur.execute("SELECT id FROM users WHERE username = %s", (username,)) | |
| if pg_cur.fetchone(): | |
| logger.info(f"Skipping existing user: {username}") | |
| continue | |
| password_hash = user['password_hash'] | |
| role = user['role'] | |
| email = user['email'] if 'email' in user.keys() else None | |
| # Handle timestamps (SQLite stores as float timestamp) | |
| created_at_ts = user['created_at'] | |
| last_login_ts = user['last_login'] | |
| created_at = datetime.fromtimestamp(created_at_ts) if created_at_ts else datetime.now() | |
| last_login = datetime.fromtimestamp(last_login_ts) if last_login_ts else None | |
| active = bool(user['active']) | |
| # Insert into Postgres | |
| pg_cur.execute(""" | |
| INSERT INTO users (username, password_hash, role, email, created_at, last_login, active) | |
| VALUES (%s, %s, %s, %s, %s, %s, %s) | |
| """, (username, password_hash, role, email, created_at, last_login, active)) | |
| count += 1 | |
| pg_conn.commit() | |
| logger.info(f"Successfully migrated {count} users.") | |
| except Exception as e: | |
| pg_conn.rollback() | |
| logger.error(f"Migration failed: {e}") | |
| finally: | |
| sqlite_conn.close() | |
| pg_conn.close() | |
| if __name__ == "__main__": | |
| migrate() | |