# ============================================================ # app/database.py - MongoDB Connection Management (FIXED) # ============================================================ from motor.motor_asyncio import AsyncIOMotorClient as AsyncClient from motor.motor_asyncio import AsyncIOMotorDatabase as AsyncDatabase import logging from app.config import settings logger = logging.getLogger(__name__) class DatabaseConnection: """Singleton for MongoDB async connection""" client: AsyncClient = None database: AsyncDatabase = None db = DatabaseConnection() async def connect_db(): """Connect to MongoDB""" try: db.client = AsyncClient(settings.MONGODB_URL) db.database = db.client[settings.MONGODB_DATABASE] # Test connection await db.client.admin.command("ping") logger.info(f"Connected to MongoDB: {settings.MONGODB_DATABASE}") except Exception as e: logger.error(f"Failed to connect to MongoDB: {str(e)}") raise async def disconnect_db(): """Disconnect from MongoDB""" if db.client is not None: # FIX: Use 'is not None' instead of bool check db.client.close() logger.info("Disconnected from MongoDB") async def get_db() -> AsyncDatabase: """Get database instance - NOW ASYNC""" # FIX 1: Compare with None instead of using bool() # FIX 2: Make this async since it was being called in async context if db.database is None: raise RuntimeError("Database not initialized. Call connect_db() first.") return db.database def get_db_sync() -> AsyncDatabase: """Get database instance synchronously (use with caution)""" if db.database is None: raise RuntimeError("Database not initialized. Call connect_db() first.") return db.database # ============================================================ # Helper function to get collections # ============================================================ async def get_users_collection(): """Get users collection""" database = await get_db() return database["users"] async def get_otps_collection(): """Get OTPs collection""" database = await get_db() return database["otps"] # ============================================================ # Create indexes on startup # ============================================================ async def ensure_indexes(): """Create necessary indexes for collections""" try: users_col = await get_users_collection() otps_col = await get_otps_collection() # Users indexes await users_col.create_index("email", sparse=True, unique=True) await users_col.create_index("phone", sparse=True, unique=True) await users_col.create_index("role") await users_col.create_index("isActive") # OTP indexes await otps_col.create_index("identifier") await otps_col.create_index("purpose") await otps_col.create_index([("createdAt", 1)], expireAfterSeconds=900) # 15 min TTL logger.info("Database indexes created successfully") except Exception as e: logger.error(f"Failed to create indexes: {str(e)}") async def ensure_review_indexes(): """Create indexes for reviews collection""" try: database = await get_db() reviews_col = database["reviews"] # Index for querying reviews by target await reviews_col.create_index([("target_type", 1), ("target_id", 1)]) # Unique index to prevent duplicate reviews (same user can't review same target twice) await reviews_col.create_index( [("reviewer_id", 1), ("target_type", 1), ("target_id", 1)], unique=True ) # Index for sorting by created_at await reviews_col.create_index("created_at") logger.info("Review indexes created successfully") except Exception as e: logger.error(f"Failed to create review indexes: {str(e)}")