import os import json import firebase_admin from firebase_admin import credentials, auth as fb_auth, firestore import requests as http_requests _initialized = False _db = None def init_firebase() -> bool: global _initialized, _db if _initialized: return True creds_json = os.getenv("FIREBASE_CREDENTIALS", "") if not creds_json: print("[firebase] FIREBASE_CREDENTIALS not set — auth disabled.") return False try: cred = credentials.Certificate(json.loads(creds_json)) firebase_admin.initialize_app(cred) _db = firestore.client() _initialized = True print("[firebase] Initialized successfully.") return True except Exception as exc: print(f"[firebase] Init error: {exc}") return False def get_db(): if not _initialized: init_firebase() return _db # ── Auth helpers ──────────────────────────────────────────────────────────── def create_user(email: str, password: str) -> dict | None: """Create Firebase Auth user + Firestore profile. Returns uid or None.""" try: user = fb_auth.create_user(email=email, password=password) db = get_db() if db: db.collection("users").document(user.uid).set({ "email": email, "cg_api_key": "", "created_at": firestore.SERVER_TIMESTAMP, }) return {"uid": user.uid, "email": email} except fb_auth.EmailAlreadyExistsError: return {"error": "email_taken"} except Exception as exc: print(f"[firebase] create_user error: {exc}") return {"error": str(exc)} def sign_in(email: str, password: str) -> dict | None: """Sign in via Firebase REST API. Returns uid + email or None.""" api_key = os.getenv("FIREBASE_WEB_API_KEY", "") if not api_key: return {"error": "no_web_api_key"} url = ( "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword" f"?key={api_key}" ) try: resp = http_requests.post( url, json={"email": email, "password": password, "returnSecureToken": True}, timeout=8, ) if resp.status_code == 200: data = resp.json() return {"uid": data["localId"], "email": data["email"]} err = resp.json().get("error", {}).get("message", "INVALID_CREDENTIALS") return {"error": err} except Exception as exc: return {"error": str(exc)} def get_user_profile(uid: str) -> dict | None: """Fetch user doc from Firestore.""" db = get_db() if not db: return None doc = db.collection("users").document(uid).get() return {"uid": uid, **doc.to_dict()} if doc.exists else None def update_user_profile(uid: str, data: dict) -> bool: """Merge-update user doc.""" db = get_db() if not db: return False try: db.collection("users").document(uid).set(data, merge=True) return True except Exception as exc: print(f"[firebase] update_user_profile error: {exc}") return False def get_watchlist(uid: str) -> list[str]: """Return the user's watchlist as a list of coin IDs.""" db = get_db() if not db: return [] doc = db.collection("users").document(uid).get() if not doc.exists: return [] return doc.to_dict().get("watchlist", []) def toggle_watchlist(uid: str, coin_id: str) -> dict: """Add or remove a coin from the user's watchlist. Returns {watchlist, starred}.""" db = get_db() if not db: return {"error": "db_unavailable"} try: ref = db.collection("users").document(uid) doc = ref.get() current: list = (doc.to_dict() or {}).get("watchlist", []) if coin_id in current: current.remove(coin_id) starred = False else: current.append(coin_id) starred = True ref.set({"watchlist": current}, merge=True) return {"watchlist": current, "starred": starred} except Exception as exc: print(f"[firebase] toggle_watchlist error: {exc}") return {"error": str(exc)} def delete_user_account(uid: str) -> bool: """Delete Firebase Auth user + Firestore doc.""" try: fb_auth.delete_user(uid) db = get_db() if db: db.collection("users").document(uid).delete() return True except Exception as exc: print(f"[firebase] delete_user error: {exc}") return False