Spaces:
Running
Running
| 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 | |