""" MongoDB connection utilities for PitchFight AI. Phase 9.5 rule: - MongoDB is optional background persistence only. - In-memory session_manager remains the live source of truth. - If MongoDB is disabled, missing, or unavailable, the app must continue normally. - Never print MongoDB URI, password, API keys, or secrets. """ from __future__ import annotations import os from typing import Optional from dotenv import load_dotenv from pymongo import MongoClient from pymongo.collection import Collection from pymongo.database import Database from pymongo.errors import PyMongoError, ServerSelectionTimeoutError load_dotenv() _client: Optional[MongoClient] = None _db: Optional[Database] = None _connected: bool = False _warned_disabled: bool = False _warned_missing_uri: bool = False _warned_connection_failed: bool = False def is_mongodb_enabled() -> bool: """ Return True only when MongoDB persistence is explicitly enabled. Expected env: MONGODB_ENABLED=true """ return os.getenv("MONGODB_ENABLED", "false").strip().lower() == "true" def get_db() -> Optional[Database]: """ Return the MongoDB database instance if enabled and reachable. Returns None when: - MONGODB_ENABLED is not true - MONGODB_URI is missing - MongoDB connection/ping fails This function must never raise DB errors into the main app. """ global _client, _db, _connected global _warned_disabled, _warned_missing_uri, _warned_connection_failed if not is_mongodb_enabled(): if not _warned_disabled: print("[MongoDB] Disabled. Persistence skipped.") _warned_disabled = True return None if _db is not None and _connected: return _db uri = os.getenv("MONGODB_URI", "").strip() db_name = os.getenv("MONGODB_DB_NAME", "pitchfight_db").strip() or "pitchfight_db" if not uri: if not _warned_missing_uri: print("[MongoDB] MONGODB_URI missing. Persistence skipped.") _warned_missing_uri = True return None try: _client = MongoClient(uri, serverSelectionTimeoutMS=5000) _client.admin.command("ping") _db = _client[db_name] _connected = True print(f"[MongoDB] Connected to database: {db_name}") return _db except (ServerSelectionTimeoutError, PyMongoError, Exception) as exc: _connected = False _db = None if not _warned_connection_failed: print( "[MongoDB] Connection unavailable. " f"App will continue without persistence. Reason: {type(exc).__name__}" ) _warned_connection_failed = True return None def get_sessions_collection() -> Optional[Collection]: """ Return the main sessions collection. Target: pitchfight_db.sessions """ db = get_db() if db is None: return None return db["sessions"] def is_connected() -> bool: """ Return True if MongoDB is enabled and currently reachable. """ db = get_db() return db is not None and _connected