"""Migration script to create messages for existing files without message_id This script: 1. Finds all files in tr_room_files that have no message_id 2. Creates an associated message (image_ref or file_ref) for each 3. Updates the file record with the new message_id Run from project root: python scripts/migrate_orphan_files.py """ import sys import os # Add project root to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from datetime import datetime from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.core.config import get_settings from app.modules.file_storage.models import RoomFile from app.modules.realtime.models import Message, MessageType from app.modules.file_storage.services.minio_service import generate_presigned_url settings = get_settings() def get_db_session(): """Create database session""" engine = create_engine(settings.DATABASE_URL) Session = sessionmaker(bind=engine) return Session() def migrate_orphan_files(): """Migrate orphan files by creating associated messages""" db = get_db_session() try: # Find files without message_id orphan_files = db.query(RoomFile).filter( RoomFile.message_id.is_(None), RoomFile.deleted_at.is_(None) ).all() if not orphan_files: print("No orphan files found. Nothing to migrate.") return print(f"Found {len(orphan_files)} orphan files to migrate.") migrated = 0 failed = 0 for file in orphan_files: try: # Generate presigned URL for file download_url = generate_presigned_url( bucket=file.minio_bucket, object_path=file.minio_object_path, expiry_seconds=3600 ) # Determine message type if file.file_type == "image": msg_type = MessageType.IMAGE_REF content = f"[Image] {file.filename}" else: msg_type = MessageType.FILE_REF content = f"[File] {file.filename}" # Get next sequence number for the room max_seq = db.query(Message.sequence_number).filter( Message.room_id == file.room_id ).order_by(Message.sequence_number.desc()).first() next_seq = (max_seq[0] + 1) if max_seq else 1 # Create metadata metadata = { "file_id": file.file_id, "file_url": download_url, "filename": file.filename, "file_type": file.file_type, "mime_type": file.mime_type, "file_size": file.file_size, } # Add thumbnail_url for images if file.file_type == "image": metadata["thumbnail_url"] = download_url # Create message message = Message( room_id=file.room_id, sender_id=file.uploader_id, content=content, message_type=msg_type, message_metadata=metadata, created_at=file.uploaded_at, # Use original upload time sequence_number=next_seq, ) db.add(message) db.flush() # Get the message_id # Update file with message_id file.message_id = message.message_id migrated += 1 print(f" Migrated: {file.filename} -> message {message.message_id}") except Exception as e: failed += 1 print(f" Failed: {file.filename} - {e}") # Commit all changes db.commit() print(f"\nMigration complete: {migrated} migrated, {failed} failed") except Exception as e: db.rollback() print(f"Migration failed: {e}") raise finally: db.close() if __name__ == "__main__": print("Starting orphan files migration...") print("=" * 50) migrate_orphan_files() print("=" * 50) print("Done.")