import logging import os import json from google.cloud import firestore from google.oauth2 import service_account from src.config.config import settings logger = logging.getLogger(__name__) class Database: client = None database_name = None def connect_to_database(self): """Create database connection.""" try: # Store database name self.database_name = settings.FIRESTORE_DATABASE_NAME # Print project ID and database name for debugging logger.info(f"Attempting to connect to Firestore with project ID: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") # First try using Application Default Credentials (for Cloud environments) try: logger.info("Attempting to connect using Application Default Credentials") self.client = firestore.Client( project=settings.FIRESTORE_PROJECT_ID, database=self.database_name ) # Test connection by trying to access a collection self.client.collection('test').limit(1).get() logger.info(f"Connected to Firestore project using Application Default Credentials: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") return except Exception as adc_error: logger.error(f"Application Default Credentials failed: {adc_error}", exc_info=True) # Fall back to service account file credentials_path = settings.FIRESTORE_CREDENTIALS_FILE if not os.path.exists(credentials_path): logger.error(f"Firestore credentials file not found: {credentials_path}") raise FileNotFoundError(f"Credentials file not found: {credentials_path}") # Print key file contents (without sensitive parts) for debugging try: with open(credentials_path, 'r') as f: key_data = json.load(f) # Log non-sensitive parts of the key logger.info(f"Using credentials file with project_id: {key_data.get('project_id')}") logger.info(f"Client email: {key_data.get('client_email')}") logger.info(f"Key file type: {key_data.get('type')}") except Exception as e: logger.error(f"Error reading key file: {e}") # Load credentials credentials = service_account.Credentials.from_service_account_file(credentials_path) # Initialize Firestore client self.client = firestore.Client( project=settings.FIRESTORE_PROJECT_ID, database=self.database_name, credentials=credentials ) # Test connection by trying to access a collection self.client.collection('test').limit(1).get() logger.info(f"Connected to Firestore project using credentials file: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") except Exception as e: logger.error(f"Failed to connect to Firestore: {e}", exc_info=True) raise def close_database_connection(self): """Close database connection.""" try: # No explicit close method needed for Firestore client self.client = None logger.info("Closed Firestore connection") except Exception as e: logger.error(f"Failed to close Firestore connection: {e}") def get_database(self): """Get the database instance.""" if self.client is None: logger.warning("Database client is None. Attempting to reconnect...") try: self.connect_to_database() except Exception as e: logger.error(f"Failed to reconnect to database: {e}") # Return None to avoid further errors but log this issue return None # Verify that client is properly initialized if self.client is None: logger.error("Database client is still None after reconnect attempt") return None return self.client # Create a singleton database instance db = Database()