coinsentry / app /core /firebase.py
heisbuba's picture
Upload 3 files
ee43f70 verified
Raw
History Blame Contribute Delete
4.69 kB
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