Spaces:
Sleeping
Sleeping
| """ | |
| database.py β Firebase Realtime Database backend for MechanicGo | |
| Uses Firebase REST API directly (no MySQL, no IP whitelisting needed) | |
| """ | |
| import os, hashlib, datetime | |
| import requests as http | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| FIREBASE_URL = os.getenv( | |
| "FIREBASE_DATABASE_URL", | |
| "https://mechanic-go-e3f05-default-rtdb.firebaseio.com" | |
| ) | |
| # βββ Low-level REST helpers βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _url(path): | |
| return f"{FIREBASE_URL}/{path}.json" | |
| def fb_get(path): | |
| """GET a node. Returns dict/list/scalar or None.""" | |
| r = http.get(_url(path), timeout=10) | |
| r.raise_for_status() | |
| return r.json() | |
| def fb_set(path, data): | |
| """PUT (overwrite) a node.""" | |
| r = http.put(_url(path), json=data, timeout=10) | |
| r.raise_for_status() | |
| return r.json() | |
| def fb_push(path, data): | |
| """POST to auto-generate a Firebase key. Returns the new key string.""" | |
| r = http.post(_url(path), json=data, timeout=10) | |
| r.raise_for_status() | |
| return r.json().get("name") # Firebase returns {"name": "-NxABC..."} | |
| def fb_patch(path, data): | |
| """PATCH (merge-update) a node.""" | |
| r = http.patch(_url(path), json=data, timeout=10) | |
| r.raise_for_status() | |
| return r.json() | |
| def fb_delete(path): | |
| """DELETE a node.""" | |
| r = http.delete(_url(path), timeout=10) | |
| r.raise_for_status() | |
| # βββ Structured helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def fb_list(path): | |
| """Return list of dicts from a Firebase collection, each with '_id' key.""" | |
| data = fb_get(path) | |
| if not data or not isinstance(data, dict): | |
| return [] | |
| return [{"_id": k, **v} for k, v in data.items() if isinstance(v, dict)] | |
| def fb_find_one(path, field, value): | |
| """Linear scan to find first item where field == value.""" | |
| for item in fb_list(path): | |
| if item.get(field) == value: | |
| return item | |
| return None | |
| def fb_find_one_by_id(path, key): | |
| """Get a single item by its Firebase key.""" | |
| data = fb_get(f"{path}/{key}") | |
| if not data: | |
| return None | |
| return {"_id": key, **data} | |
| # βββ Init β seeds admin account βββββββββββββββββββββββββββββββββββββββββββββββ | |
| def init_db(): | |
| try: | |
| existing = fb_find_one("users", "email", "admin@mechanicgo.com") | |
| if not existing: | |
| pw = hashlib.sha256("admin123".encode()).hexdigest() | |
| fb_push("users", { | |
| "name": "Admin", | |
| "email": "admin@mechanicgo.com", | |
| "password_hash": pw, | |
| "role": "admin", | |
| "phone": "", | |
| "is_active": True, | |
| "created_at": datetime.datetime.utcnow().isoformat() | |
| }) | |
| print("β Default admin seeded β admin@mechanicgo.com / admin123") | |
| else: | |
| print("β Firebase Realtime DB connected") | |
| except Exception as e: | |
| print(f"β οΈ Firebase init warning: {e}") | |
| print(" Make sure FIREBASE_DATABASE_URL is correct and DB rules allow read/write") | |
| def get_db(): | |
| raise RuntimeError("MySQL removed. Use fb_* helpers from database.py directly.") |