Spaces:
Sleeping
Sleeping
Download utils/firebase_config.py from Markishime/ags: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/spaces/Markishime/ags/resolve/main/utils/firebase_config.py
- Command line
-
hf download hf://spaces/Markishime/ags/utils/firebase_config.py
-
curl -L -o firebase_config.py https://huggingface.co/spaces/Markishime/ags/resolve/main/utils/firebase_config.py
15.7 kB
| import os | |
| import json | |
| import streamlit as st | |
| from typing import Optional | |
| try: | |
| from utils.secrets_utils import get_secret_section, has_secret | |
| except ImportError: | |
| from secrets_utils import get_secret_section, has_secret | |
| # Optional Firebase Admin imports | |
| try: | |
| import firebase_admin | |
| from firebase_admin import credentials, firestore, auth, storage | |
| from google.cloud.firestore import FieldFilter | |
| FIREBASE_AVAILABLE = True | |
| except ImportError as e: | |
| firebase_admin = None | |
| credentials = None | |
| firestore = None | |
| auth = None | |
| storage = None | |
| FieldFilter = None | |
| FIREBASE_AVAILABLE = False | |
| print(f"Warning: Firebase Admin SDK not available: {e}") | |
| # Do not load from .env in deployment; rely on Streamlit secrets only | |
| # Avoid global monkey patching of Google Auth entirely | |
| def initialize_firebase() -> bool: | |
| """Initialize Firebase Admin SDK | |
| Returns: | |
| bool: True if initialization successful, False otherwise | |
| """ | |
| if not FIREBASE_AVAILABLE: | |
| error_msg = "Firebase Admin SDK is not installed. Please install it with: pip install firebase-admin" | |
| st.error(error_msg) | |
| print(error_msg) | |
| return False | |
| try: | |
| # Prefer project_id from Streamlit secrets | |
| project_id = None | |
| firebase_secrets = get_secret_section('firebase') | |
| if firebase_secrets is not None: | |
| project_id = firebase_secrets.get('project_id') | |
| # Check if Firebase is already initialized | |
| if firebase_admin and firebase_admin._apps: | |
| print("Firebase already initialized") | |
| return True | |
| print("Initializing Firebase...") | |
| # Get Firebase credentials from environment or Streamlit secrets | |
| firebase_creds = get_firebase_credentials() | |
| if firebase_creds: | |
| print(f"Found Firebase credentials for project: {firebase_creds.get('project_id', 'unknown')}") | |
| if not credentials: | |
| print("Firebase credentials module not available") | |
| return False | |
| # Initialize Firebase with credentials | |
| cred = credentials.Certificate(firebase_creds) | |
| # Get storage bucket from Streamlit secrets | |
| storage_bucket = None | |
| if firebase_secrets is not None: | |
| storage_bucket = ( | |
| firebase_secrets.get('firebase_storage_bucket') or | |
| firebase_secrets.get('storage_bucket') | |
| ) | |
| if not storage_bucket and firebase_creds.get('project_id'): | |
| storage_bucket = f"{firebase_creds.get('project_id')}.firebasestorage.app" | |
| # Initialize Firebase with explicit configuration | |
| if firebase_admin: | |
| firebase_admin.initialize_app(cred, { | |
| 'storageBucket': storage_bucket, | |
| 'projectId': firebase_creds.get('project_id', project_id or 'agriai-cbd8b') | |
| }) | |
| else: | |
| print("Firebase Admin module not available") | |
| return False | |
| # Do not modify global google.auth defaults; rely on initialized app | |
| print(f"Firebase initialized successfully with storage bucket: {storage_bucket}") | |
| return True | |
| else: | |
| # Provide more specific guidance about missing credentials | |
| if not has_secret('firebase') and not os.getenv("FIREBASE_SERVICE_ACCOUNT_KEY"): | |
| error_msg = ( | |
| "Firebase credentials not found. Set FIREBASE_SERVICE_ACCOUNT_KEY " | |
| "in Hugging Face Space secrets, or add a [firebase] section." | |
| ) | |
| elif has_secret('firebase'): | |
| firebase_cfg = get_secret_section('firebase') or {} | |
| missing_fields = [] | |
| if not firebase_cfg.get('project_id'): | |
| missing_fields.append('project_id') | |
| if not firebase_cfg.get('firebase_private_key'): | |
| missing_fields.append('firebase_private_key') | |
| if not firebase_cfg.get('firebase_client_email'): | |
| missing_fields.append('firebase_client_email') | |
| if missing_fields: | |
| error_msg = ( | |
| "Missing required Firebase credentials in Space secrets: " | |
| f"{', '.join(missing_fields)}." | |
| ) | |
| else: | |
| error_msg = ( | |
| "Firebase credentials found but failed validation. " | |
| "Check private_key, client_email, and project_id." | |
| ) | |
| else: | |
| error_msg = ( | |
| "Firebase credentials not configured. " | |
| "Add FIREBASE_SERVICE_ACCOUNT_KEY to your Hugging Face Space secrets." | |
| ) | |
| print(error_msg) | |
| return False | |
| except Exception as e: | |
| error_type = type(e).__name__ | |
| error_details = str(e) | |
| # Provide more specific error messages | |
| if "Certificate" in error_type or "private_key" in error_details.lower(): | |
| error_msg = f"Firebase credential error: Invalid private key or certificate. Please check your Firebase service account credentials in Streamlit secrets." | |
| elif "project_id" in error_details.lower() or "project" in error_details.lower(): | |
| error_msg = f"Firebase project error: {error_details}. Please verify your project_id in Streamlit secrets." | |
| elif "already exists" in error_details.lower() or "already initialized" in error_details.lower(): | |
| # Firebase already initialized - this is actually OK | |
| print(f"Firebase already initialized: {error_details}") | |
| return True | |
| else: | |
| error_msg = f"Failed to initialize Firebase: {error_details}. Please check your configuration in Streamlit secrets." | |
| print(f"Failed to initialize Firebase ({error_type}): {error_details}") | |
| return False | |
| def get_firebase_credentials() -> Optional[dict]: | |
| """Get Firebase credentials from environment variables or Streamlit secrets | |
| Returns: | |
| dict: Firebase service account credentials or None | |
| """ | |
| try: | |
| # 1) Prefer JSON from environment variable (e.g. on Hugging Face Spaces) | |
| env_creds = os.getenv("FIREBASE_SERVICE_ACCOUNT_KEY") | |
| if env_creds: | |
| try: | |
| print("Loading Firebase credentials from FIREBASE_SERVICE_ACCOUNT_KEY env var") | |
| return json.loads(env_creds) | |
| except Exception as e: | |
| print(f"Error parsing FIREBASE_SERVICE_ACCOUNT_KEY: {e}") | |
| # 2) Fallback to Streamlit secrets | |
| try: | |
| firebase_secrets = get_secret_section('firebase') | |
| if firebase_secrets is not None: | |
| print("Loading Firebase credentials from Streamlit secrets") | |
| # Construct credentials from individual fields in secrets | |
| firebase_config = { | |
| "type": firebase_secrets.get('firebase_type', 'service_account'), | |
| "project_id": firebase_secrets.get('project_id'), | |
| "private_key_id": firebase_secrets.get('firebase_private_key_id'), | |
| "private_key": firebase_secrets.get('firebase_private_key', '').replace('\\n', '\n'), | |
| "client_email": firebase_secrets.get('firebase_client_email'), | |
| "client_id": firebase_secrets.get('firebase_client_id'), | |
| "auth_uri": firebase_secrets.get('firebase_auth_uri', 'https://accounts.google.com/o/oauth2/auth'), | |
| "token_uri": firebase_secrets.get('firebase_token_uri', 'https://oauth2.googleapis.com/token'), | |
| "auth_provider_x509_cert_url": firebase_secrets.get('firebase_auth_provider_x509_cert_url', 'https://www.googleapis.com/oauth2/v1/certs'), | |
| "client_x509_cert_url": firebase_secrets.get('firebase_client_x509_cert_url'), | |
| "universe_domain": firebase_secrets.get('firebase_universe_domain', 'googleapis.com') | |
| } | |
| # Check if all required fields are present | |
| required_fields = ['project_id', 'private_key', 'client_email'] | |
| missing_fields = [field for field in required_fields if not firebase_config.get(field)] | |
| if not missing_fields: | |
| print("Successfully loaded Firebase credentials from Streamlit secrets") | |
| return firebase_config | |
| else: | |
| print(f"Missing required Firebase credentials in secrets: {missing_fields}") | |
| elif has_secret('FIREBASE_SERVICE_ACCOUNT_KEY'): | |
| print("Loading Firebase credentials from st.secrets['FIREBASE_SERVICE_ACCOUNT_KEY']") | |
| return dict(st.secrets['FIREBASE_SERVICE_ACCOUNT_KEY']) | |
| except Exception as e: | |
| print(f"Error loading Firebase credentials from Streamlit secrets: {e}") | |
| # If nothing found, return None to signal missing credentials | |
| return None | |
| except Exception as e: | |
| print(f"Error loading Firebase credentials: {e}") | |
| return None | |
| def get_firestore_client(): | |
| """Get Firestore client instance | |
| Returns: | |
| firestore.Client: Firestore client instance | |
| """ | |
| if not FIREBASE_AVAILABLE: | |
| return None | |
| try: | |
| # Ensure Firebase is initialized before creating the client | |
| if firebase_admin and not firebase_admin._apps: | |
| initialize_firebase() | |
| # Return Firestore client from the initialized Firebase app | |
| return firestore.client() if firestore else None | |
| except Exception as e: | |
| # Firebase initialization handled elsewhere, silently return None | |
| return None | |
| def get_storage_bucket(): | |
| """Get Firebase Storage bucket instance | |
| Returns: | |
| storage.Bucket: Storage bucket instance | |
| """ | |
| if not FIREBASE_AVAILABLE: | |
| return None | |
| try: | |
| # Get credentials explicitly to avoid metadata service | |
| firebase_creds = get_firebase_credentials() | |
| if firebase_creds and credentials: | |
| cred = credentials.Certificate(firebase_creds) | |
| return storage.bucket(credentials=cred) if storage else None | |
| else: | |
| # Fallback to default bucket (should work if Firebase is initialized) | |
| return storage.bucket() if storage else None | |
| except Exception as e: | |
| st.error(f"Failed to get Storage bucket: {str(e)}") | |
| return None | |
| def get_auth_client(): | |
| """Get Firebase Auth client | |
| Returns: | |
| auth: Firebase Auth client | |
| """ | |
| if not FIREBASE_AVAILABLE: | |
| return None | |
| try: | |
| return auth if auth else None | |
| except Exception as e: | |
| st.error(f"Failed to get Auth client: {str(e)}") | |
| return None | |
| # Collection names | |
| COLLECTIONS = { | |
| 'users': 'users', | |
| 'analyses': 'analyses', | |
| 'analysis_results': 'analysis_results', | |
| 'analysis_prompts': 'analysis_prompts', | |
| 'feedback': 'feedback', | |
| 'admin_requests': 'admin_requests', | |
| 'admin_codes': 'admin_codes', | |
| 'reference_documents': 'reference_documents', | |
| 'ai_configuration': 'ai_configuration', | |
| 'reference_materials': 'reference_materials', | |
| 'output_formats': 'output_formats', | |
| 'tagging_config': 'tagging_config', | |
| 'prompt_templates': 'prompt_templates' | |
| } | |
| # Default MPOB standards - Accurate values for Malaysian Oil Palm cultivation (matching actual data format) | |
| DEFAULT_MPOB_STANDARDS = { | |
| 'leaf_standards': { | |
| 'N (%)': {'min': 2.4, 'max': 2.8, 'unit': '%', 'optimal': 2.6}, | |
| 'P (%)': {'min': 0.14, 'max': 0.20, 'unit': '%', 'optimal': 0.17}, | |
| 'K (%)': {'min': 0.9, 'max': 1.3, 'unit': '%', 'optimal': 1.1}, | |
| 'Mg (%)': {'min': 0.25, 'max': 0.45, 'unit': '%', 'optimal': 0.35}, | |
| 'Ca (%)': {'min': 0.5, 'max': 0.9, 'unit': '%', 'optimal': 0.7}, | |
| 'B (mg/kg)': {'min': 18, 'max': 28, 'unit': 'mg/kg', 'optimal': 23}, | |
| 'Cu (mg/kg)': {'min': 8, 'max': 18, 'unit': 'mg/kg', 'optimal': 13}, | |
| 'Zn (mg/kg)': {'min': 18, 'max': 35, 'unit': 'mg/kg', 'optimal': 26} | |
| }, | |
| 'soil_standards': { | |
| 'pH': {'min': 4.5, 'max': 6.0, 'unit': '', 'optimal': 5.25}, | |
| 'N (%)': {'min': 0.15, 'max': 0.25, 'unit': '%', 'optimal': 0.20}, | |
| 'Org. C (%)': {'min': 1.5, 'max': 2.5, 'unit': '%', 'optimal': 2.0}, | |
| 'Total P (mg/kg)': {'min': 15, 'max': 25, 'unit': 'mg/kg', 'optimal': 20}, | |
| 'Avail P (mg/kg)': {'min': 10, 'max': 20, 'unit': 'mg/kg', 'optimal': 15}, | |
| 'Exch. K (meq%)': {'min': 0.20, 'max': 0.40, 'unit': 'meq%', 'optimal': 0.30}, | |
| 'Exch. Ca (meq%)': {'min': 2.0, 'max': 4.0, 'unit': 'meq%', 'optimal': 3.0}, | |
| 'Exch. Mg (meq%)': {'min': 0.6, 'max': 1.2, 'unit': 'meq%', 'optimal': 0.9}, | |
| 'CEC (meq%)': {'min': 15, 'max': 25, 'unit': 'meq%', 'optimal': 20} | |
| } | |
| } | |
| def initialize_admin_codes(): | |
| """Initialize default admin codes in Firestore""" | |
| import secrets | |
| import string | |
| from datetime import datetime, timedelta, timezone | |
| try: | |
| db = get_firestore_client() | |
| if not db: | |
| print("Failed to get Firestore client") | |
| return None | |
| admin_codes_ref = db.collection(COLLECTIONS['admin_codes']) | |
| # Check if default admin code already exists | |
| if FieldFilter: | |
| existing_codes = admin_codes_ref.where(filter=FieldFilter('is_default', '==', True)).limit(1).get() | |
| else: | |
| # Fallback if FieldFilter is not available | |
| existing_codes = admin_codes_ref.where('is_default', '==', True).limit(1).get() | |
| if existing_codes: | |
| # Return existing default code | |
| for doc in existing_codes: | |
| code_data = doc.to_dict() | |
| expires_at = code_data.get('expires_at') | |
| # Handle both timezone-aware and naive datetime objects | |
| if expires_at: | |
| if hasattr(expires_at, 'tzinfo') and expires_at.tzinfo is not None: | |
| current_time = datetime.now(timezone.utc) | |
| else: | |
| current_time = datetime.now() | |
| if expires_at > current_time: | |
| print("Default admin code already exists") | |
| return code_data['code'] | |
| # Generate new default admin code | |
| alphabet = string.ascii_uppercase + string.digits | |
| default_code = ''.join(secrets.choice(alphabet) for _ in range(8)) | |
| # Set expiration to 1 year from now (timezone-aware) | |
| current_time = datetime.now(timezone.utc) | |
| expires_at = current_time + timedelta(days=365) | |
| # Store in Firestore | |
| admin_code_data = { | |
| 'code': default_code, | |
| 'is_default': True, | |
| 'created_at': current_time, | |
| 'expires_at': expires_at, | |
| 'used': False, | |
| 'created_by': 'system', | |
| 'description': 'Default admin registration code' | |
| } | |
| admin_codes_ref.add(admin_code_data) | |
| print("Default admin code created successfully") | |
| return default_code | |
| except Exception as e: | |
| print(f"Error initializing admin codes: {str(e)}") | |
| return None |