from fastapi import Header, HTTPException, Depends import jwt import os from datetime import datetime, timezone from app.database import users_collection SECRET_KEY = os.getenv("JWT_SECRET", "super-secret-fakeshield-key-for-project") ALGORITHM = "HS256" def _as_utc_datetime(value): if value is None: return None if isinstance(value, datetime): return value.replace(tzinfo=timezone.utc) if value.tzinfo is None else value.astimezone(timezone.utc) if isinstance(value, str): try: normalized = value.replace("Z", "+00:00") parsed = datetime.fromisoformat(normalized) return parsed.replace(tzinfo=timezone.utc) if parsed.tzinfo is None else parsed.astimezone(timezone.utc) except ValueError: return None return None def is_subscription_active(user: dict) -> bool: if user.get("subscription_tier") != "paid": return False expires_at = _as_utc_datetime(user.get("subscription_expires_at")) if not expires_at: return False return expires_at > datetime.now(timezone.utc) def serialize_subscription_expires_at(user: dict): expires_at = _as_utc_datetime(user.get("subscription_expires_at")) return expires_at.isoformat().replace("+00:00", "Z") if expires_at else None def public_user_payload(user: dict) -> dict: active = is_subscription_active(user) return { "name": user.get("fullName") or user.get("name") or user.get("full_name"), "email": user.get("email"), "subscription_tier": "paid" if active else "free", "subscription_expires_at": serialize_subscription_expires_at(user) if active else None, "profile_pic": user.get("profile_pic"), } async def get_current_user(authorization: str = Header(None)): if not authorization: raise HTTPException(status_code=401, detail="Authorization header missing") try: token = authorization.replace("Bearer ", "") payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) email = payload.get("sub") if email is None: raise HTTPException(status_code=401, detail="Invalid token") except jwt.PyJWTError: raise HTTPException(status_code=401, detail="Invalid token") try: user = await users_collection.find_one({"email": email}) if user is None: print(f"[AUTH] User {email} not in DB. Granting limited guest access.") return { "email": email, "full_name": "Guest User", "subscription_tier": "free", "is_offline": True } return user except Exception as e: print(f"[AUTH] DB Error during auth: {e}. Granting limited guest access.") return { "email": email, "full_name": "Offline Tester", "subscription_tier": "free", "is_offline": True } async def verify_paid_tier(user: dict = Depends(get_current_user)): if not is_subscription_active(user): raise HTTPException( status_code=403, detail="This feature requires an active Pro subscription. Please upgrade to access." ) return user