-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmigrate_to_postgres.py
More file actions
165 lines (134 loc) · 5.75 KB
/
migrate_to_postgres.py
File metadata and controls
165 lines (134 loc) · 5.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env python3
"""
Migration script from SQLite to PostgreSQL
Preserves all existing data during migration
"""
import os
import sys
import logging
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import sessionmaker
from datetime import datetime
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def migrate_database():
"""Migrate data from SQLite to PostgreSQL"""
# Get database URLs
postgres_url = os.getenv("DATABASE_URL", "")
# Fix Railway PostgreSQL URL format
if postgres_url.startswith("postgres://"):
postgres_url = postgres_url.replace("postgres://", "postgresql://", 1)
if not postgres_url or "postgresql" not in postgres_url:
logger.error("PostgreSQL DATABASE_URL not found or invalid")
logger.info("Please add PostgreSQL to Railway and set DATABASE_URL environment variable")
return False
# SQLite database path
if os.path.exists('/app'):
sqlite_path = "sqlite:////app/data/api_orchestrator.db"
else:
sqlite_path = "sqlite:///./api_orchestrator.db"
# Check if SQLite database exists
sqlite_file = sqlite_path.replace("sqlite:///", "")
if not os.path.exists(sqlite_file):
logger.info("No SQLite database found. Starting fresh with PostgreSQL.")
return True
try:
# Create engines
logger.info("Connecting to databases...")
sqlite_engine = create_engine(sqlite_path)
postgres_engine = create_engine(postgres_url)
# Create all tables in PostgreSQL first
logger.info("Creating tables in PostgreSQL...")
from backend.src.database import Base
Base.metadata.create_all(postgres_engine)
# Get all tables
metadata = MetaData()
metadata.reflect(bind=sqlite_engine)
# Tables to migrate in order (respecting foreign keys)
table_order = [
'users',
'projects',
'apis',
'api_endpoints',
'api_tests',
'tasks',
'mock_servers',
'api_keys',
'usage_events',
'invoices',
'password_reset_tokens'
]
# Migrate each table
for table_name in table_order:
if table_name not in metadata.tables:
logger.warning(f"Table {table_name} not found in SQLite, skipping...")
continue
logger.info(f"Migrating table: {table_name}")
# Get table
table = metadata.tables[table_name]
# Read all data from SQLite
with sqlite_engine.connect() as sqlite_conn:
rows = sqlite_conn.execute(table.select()).fetchall()
if not rows:
logger.info(f" No data in {table_name}")
continue
# Insert into PostgreSQL
with postgres_engine.connect() as pg_conn:
# Convert rows to dictionaries
data = [dict(row._mapping) for row in rows]
# Insert data
pg_conn.execute(table.insert(), data)
pg_conn.commit()
logger.info(f" Migrated {len(rows)} rows from {table_name}")
logger.info("✅ Migration completed successfully!")
# Verify migration
logger.info("Verifying migration...")
with postgres_engine.connect() as pg_conn:
for table_name in table_order:
if table_name not in metadata.tables:
continue
table = metadata.tables[table_name]
count = pg_conn.execute(f"SELECT COUNT(*) FROM {table_name}").scalar()
logger.info(f" {table_name}: {count} rows")
return True
except Exception as e:
logger.error(f"Migration failed: {str(e)}")
logger.exception("Full error:")
return False
def test_postgres_connection():
"""Test PostgreSQL connection"""
postgres_url = os.getenv("DATABASE_URL", "")
# Fix Railway PostgreSQL URL format
if postgres_url.startswith("postgres://"):
postgres_url = postgres_url.replace("postgres://", "postgresql://", 1)
if not postgres_url or "postgresql" not in postgres_url:
logger.error("PostgreSQL DATABASE_URL not configured")
return False
try:
engine = create_engine(postgres_url)
with engine.connect() as conn:
result = conn.execute("SELECT version()")
version = result.scalar()
logger.info(f"✅ PostgreSQL connected successfully!")
logger.info(f" Version: {version}")
return True
except Exception as e:
logger.error(f"❌ PostgreSQL connection failed: {str(e)}")
return False
if __name__ == "__main__":
logger.info("=== StreamAPI Database Migration Tool ===")
# Test PostgreSQL connection first
if not test_postgres_connection():
logger.error("\n⚠️ PostgreSQL not available. Please:")
logger.error("1. Add PostgreSQL to your Railway project")
logger.error("2. Copy the DATABASE_URL from PostgreSQL service")
logger.error("3. Add it to your web service environment variables")
sys.exit(1)
# Perform migration
if migrate_database():
logger.info("\n🎉 Database migration successful!")
logger.info("Your app is now using PostgreSQL")
else:
logger.error("\n❌ Migration failed. Please check the logs above.")
sys.exit(1)