ibraheem007 commited on
Commit
64a99f4
Β·
verified Β·
1 Parent(s): d1a375f

Update db/migration.py

Browse files
Files changed (1) hide show
  1. db/migration.py +222 -9
db/migration.py CHANGED
@@ -1,22 +1,41 @@
1
  import os
2
  from dotenv import load_dotenv
3
  from sqlalchemy import create_engine, text
 
4
 
5
- # Load .env variables
6
- load_dotenv()
7
 
8
- DB_NAME = os.getenv("DB_NAME")
9
- DB_USER = os.getenv("DB_USERNAME")
10
- DB_PASS = os.getenv("DB_PASSWORD")
11
- DB_HOST = os.getenv("DB_HOST", "localhost")
12
- DB_PORT = os.getenv("DB_PORT", "5432")
13
 
14
- DATABASE_URL = f"postgresql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  def add_generated_model_column():
17
  """Add generated_model column to content_history table"""
18
  print("πŸ”„ Running database migration: adding generated_model column...")
19
 
 
 
 
 
 
20
  # Create engine
21
  engine = create_engine(DATABASE_URL)
22
 
@@ -57,5 +76,199 @@ def add_generated_model_column():
57
  conn.rollback()
58
  raise
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  if __name__ == "__main__":
61
- add_generated_model_column()
 
 
 
 
 
1
  import os
2
  from dotenv import load_dotenv
3
  from sqlalchemy import create_engine, text
4
+ import sys
5
 
6
+ # Add the parent directory to Python path to import your connection module
7
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8
 
9
+ # Import your existing database connection logic
10
+ from db.connection import get_database_config
 
 
 
11
 
12
+ def get_database_url():
13
+ """Get database URL using the same logic as your main app"""
14
+ db_config = get_database_config()
15
+
16
+ if not db_config:
17
+ print("❌ No database configuration found")
18
+ return None
19
+
20
+ # Construct connection URL
21
+ DATABASE_URL = f"postgresql://{db_config['user']}:{db_config['password']}@{db_config['host']}:{db_config['port']}/{db_config['dbname']}"
22
+
23
+ # Add SSL mode for Supabase
24
+ if db_config['host'].endswith('supabase.co'):
25
+ DATABASE_URL += "?sslmode=require"
26
+
27
+ print(f"πŸ”— Using database: {db_config['host']}:{db_config['port']}")
28
+ return DATABASE_URL
29
 
30
  def add_generated_model_column():
31
  """Add generated_model column to content_history table"""
32
  print("πŸ”„ Running database migration: adding generated_model column...")
33
 
34
+ DATABASE_URL = get_database_url()
35
+ if not DATABASE_URL:
36
+ print("❌ Cannot proceed without database configuration")
37
+ return
38
+
39
  # Create engine
40
  engine = create_engine(DATABASE_URL)
41
 
 
76
  conn.rollback()
77
  raise
78
 
79
+ def add_auth_columns_to_users():
80
+ """Add authentication columns to users table - FIXED for existing data"""
81
+ print("πŸ”„ Running database migration: adding authentication columns to users table...")
82
+
83
+ DATABASE_URL = get_database_url()
84
+ if not DATABASE_URL:
85
+ print("❌ Cannot proceed without database configuration")
86
+ return
87
+
88
+ # Create engine
89
+ engine = create_engine(DATABASE_URL)
90
+
91
+ with engine.connect() as conn:
92
+ try:
93
+ # Check if users table exists first
94
+ result = conn.execute(text("""
95
+ SELECT table_name
96
+ FROM information_schema.tables
97
+ WHERE table_name='users'
98
+ """))
99
+
100
+ if result.fetchone() is None:
101
+ print("❌ Users table doesn't exist. Please run the initial database setup first.")
102
+ return
103
+
104
+ # List of columns to add with their definitions
105
+ columns_to_add = [
106
+ {
107
+ 'name': 'fullname',
108
+ 'definition': 'VARCHAR(200) NOT NULL DEFAULT \'\'',
109
+ 'update_existing': "UPDATE users SET fullname = 'User' WHERE fullname IS NULL OR fullname = ''"
110
+ },
111
+ {
112
+ 'name': 'username',
113
+ 'definition': 'VARCHAR(50) NOT NULL DEFAULT \'\'', # Remove UNIQUE constraint initially
114
+ 'update_existing': "UPDATE users SET username = 'user_' || id::text WHERE username IS NULL OR username = ''"
115
+ },
116
+ {
117
+ 'name': 'password_hash',
118
+ 'definition': 'VARCHAR(128) NOT NULL DEFAULT \'\'',
119
+ 'update_existing': None
120
+ },
121
+ {
122
+ 'name': 'salt',
123
+ 'definition': 'VARCHAR(32) NOT NULL DEFAULT \'\'',
124
+ 'update_existing': None
125
+ },
126
+ {
127
+ 'name': 'last_login',
128
+ 'definition': 'TIMESTAMP',
129
+ 'update_existing': None
130
+ }
131
+ ]
132
+
133
+ added_columns = []
134
+
135
+ for column in columns_to_add:
136
+ # Check if column already exists
137
+ result = conn.execute(text(f"""
138
+ SELECT column_name
139
+ FROM information_schema.columns
140
+ WHERE table_name='users' AND column_name='{column['name']}'
141
+ """))
142
+
143
+ if result.fetchone() is None:
144
+ print(f"πŸ“ Adding {column['name']} column...")
145
+
146
+ try:
147
+ # Add the column without UNIQUE constraint initially
148
+ conn.execute(text(f"""
149
+ ALTER TABLE users
150
+ ADD COLUMN {column['name']} {column['definition']}
151
+ """))
152
+
153
+ # Update existing records if needed
154
+ if column['update_existing']:
155
+ conn.execute(text(column['update_existing']))
156
+ print(f"βœ… Updated existing records for {column['name']}")
157
+
158
+ added_columns.append(column['name'])
159
+ print(f"βœ… Added {column['name']} column to users table")
160
+
161
+ except Exception as column_error:
162
+ print(f"⚠️ Failed to add {column['name']} column: {column_error}")
163
+ continue
164
+ else:
165
+ print(f"βœ… {column['name']} column already exists")
166
+
167
+ # NOW add the UNIQUE constraint after all usernames are populated
168
+ if 'username' in added_columns:
169
+ print("πŸ”§ Adding UNIQUE constraint to username column...")
170
+ try:
171
+ conn.execute(text("""
172
+ ALTER TABLE users
173
+ ADD CONSTRAINT users_username_unique UNIQUE (username)
174
+ """))
175
+ print("βœ… Added UNIQUE constraint to username column")
176
+ except Exception as e:
177
+ print(f"⚠️ Could not add UNIQUE constraint: {e}")
178
+ # This might happen if there are duplicate usernames, but we generated unique ones
179
+
180
+ if added_columns:
181
+ conn.commit()
182
+ print(f"πŸŽ‰ Migration completed successfully! Added columns: {', '.join(added_columns)}")
183
+ else:
184
+ print("βœ… All authentication columns already exist")
185
+
186
+ # Create index on username for faster lookups
187
+ print("πŸ”§ Creating index on username column...")
188
+ conn.execute(text("""
189
+ CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)
190
+ """))
191
+ print("βœ… Username index created")
192
+
193
+ conn.commit()
194
+
195
+ except Exception as e:
196
+ print(f"❌ Migration failed: {e}")
197
+ conn.rollback()
198
+ raise
199
+
200
+ def migrate_existing_users():
201
+ """Migrate existing users to have proper usernames and fullnames"""
202
+ print("πŸ”„ Migrating existing users to new authentication system...")
203
+
204
+ DATABASE_URL = get_database_url()
205
+ if not DATABASE_URL:
206
+ print("❌ Cannot proceed without database configuration")
207
+ return
208
+
209
+ engine = create_engine(DATABASE_URL)
210
+
211
+ with engine.connect() as conn:
212
+ try:
213
+ # Check if we have users without proper usernames
214
+ result = conn.execute(text("""
215
+ SELECT COUNT(*) as count
216
+ FROM users
217
+ WHERE username = '' OR username IS NULL
218
+ """))
219
+
220
+ users_without_username = result.fetchone()[0]
221
+
222
+ if users_without_username > 0:
223
+ print(f"πŸ“ Found {users_without_username} users without usernames, migrating...")
224
+
225
+ # Generate unique usernames for existing users
226
+ conn.execute(text("""
227
+ UPDATE users
228
+ SET
229
+ username = 'user_' || id::text,
230
+ fullname = 'User ' || id::text
231
+ WHERE username = '' OR username IS NULL
232
+ """))
233
+
234
+ print(f"βœ… Migrated {users_without_username} users to new authentication system")
235
+ else:
236
+ print("βœ… All users already have usernames")
237
+
238
+ conn.commit()
239
+
240
+ except Exception as e:
241
+ print(f"❌ User migration failed: {e}")
242
+ conn.rollback()
243
+ raise
244
+
245
+ def run_all_migrations():
246
+ """Run all database migrations"""
247
+ print("πŸš€ Starting database migrations...")
248
+
249
+ try:
250
+ # 1. Add generated_model column to content_history
251
+ add_generated_model_column()
252
+
253
+ print("\n" + "="*50 + "\n")
254
+
255
+ # 2. Add authentication columns to users table
256
+ add_auth_columns_to_users()
257
+
258
+ print("\n" + "="*50 + "\n")
259
+
260
+ # 3. Migrate existing users
261
+ migrate_existing_users()
262
+
263
+ print("\nπŸŽ‰ All migrations completed successfully!")
264
+
265
+ except Exception as e:
266
+ print(f"❌ Migration process failed: {e}")
267
+ raise
268
+
269
  if __name__ == "__main__":
270
+ # Load environment variables
271
+ load_dotenv()
272
+
273
+ # Run migrations
274
+ run_all_migrations()