Files
2026-04-13 14:49:12 +05:30

203 lines
6.7 KiB
Python

"""User management routes"""
from fastapi import APIRouter, HTTPException
from db import get_database
from models import UserCreate, UserUpdate, User
from datetime import datetime
from typing import Optional
from bson import ObjectId
from bson.errors import InvalidId
router = APIRouter()
@router.post("/register", response_model=dict)
async def register_user(user_data: UserCreate):
"""
Register or get user (idempotent).
Uses upsert pattern to ensure one user per email.
If user already exists, returns existing user.
Called after Firebase Google Auth on frontend.
"""
db = get_database()
try:
# Upsert: Update if exists, insert if not
result = db.users.update_one(
{"email": user_data.email},
{
"$setOnInsert": {
"email": user_data.email,
"displayName": user_data.displayName or user_data.email.split("@")[0],
"photoURL": user_data.photoURL,
"theme": "light",
"createdAt": datetime.utcnow()
},
"$set": {
"updatedAt": datetime.utcnow()
}
},
upsert=True
)
# Fetch the user (either newly created or existing)
user = db.users.find_one({"email": user_data.email})
if not user:
raise HTTPException(
status_code=500, detail="Failed to retrieve user after upsert")
return {
"id": str(user["_id"]),
"email": user["email"],
"displayName": user["displayName"],
"photoURL": user.get("photoURL"),
"theme": user.get("theme", "light"),
"backgroundImage": user.get("backgroundImage"),
"backgroundImages": user.get("backgroundImages", []),
"createdAt": user["createdAt"].isoformat(),
"updatedAt": user["updatedAt"].isoformat(),
"message": "User registered successfully" if result.upserted_id else "User already exists"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Registration failed: {str(e)}")
@router.get("/by-email/{email}", response_model=dict)
async def get_user_by_email(email: str):
"""Get user profile by email (called after Firebase Auth)."""
db = get_database()
try:
user = db.users.find_one({"email": email})
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {
"id": str(user["_id"]),
"email": user["email"],
"displayName": user.get("displayName"),
"photoURL": user.get("photoURL"),
"theme": user.get("theme", "light"),
"backgroundImage": user.get("backgroundImage"),
"backgroundImages": user.get("backgroundImages", []),
"tutorial": user.get("tutorial"),
"createdAt": user["createdAt"].isoformat(),
"updatedAt": user["updatedAt"].isoformat()
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Failed to fetch user: {str(e)}")
@router.get("/{user_id}", response_model=dict)
async def get_user_by_id(user_id: str):
"""Get user profile by ID."""
db = get_database()
try:
user_oid = ObjectId(user_id)
except InvalidId:
raise HTTPException(status_code=400, detail="Invalid user ID format")
try:
user = db.users.find_one({"_id": user_oid})
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {
"id": str(user["_id"]),
"email": user["email"],
"displayName": user.get("displayName"),
"photoURL": user.get("photoURL"),
"theme": user.get("theme", "light"),
"backgroundImage": user.get("backgroundImage"),
"backgroundImages": user.get("backgroundImages", []),
"createdAt": user["createdAt"].isoformat(),
"updatedAt": user["updatedAt"].isoformat()
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Failed to fetch user: {str(e)}")
@router.put("/{user_id}", response_model=dict)
async def update_user(user_id: str, user_data: UserUpdate):
"""Update user profile."""
db = get_database()
try:
user_oid = ObjectId(user_id)
except InvalidId:
raise HTTPException(status_code=400, detail="Invalid user ID format")
try:
# Prepare update data (exclude None values)
update_data = user_data.model_dump(exclude_unset=True)
update_data["updatedAt"] = datetime.utcnow()
result = db.users.update_one(
{"_id": user_oid},
{"$set": update_data}
)
if result.matched_count == 0:
raise HTTPException(status_code=404, detail="User not found")
# Fetch and return updated user
user = db.users.find_one({"_id": user_oid})
return {
"id": str(user["_id"]),
"email": user["email"],
"displayName": user.get("displayName"),
"photoURL": user.get("photoURL"),
"theme": user.get("theme", "light"),
"backgroundImage": user.get("backgroundImage"),
"backgroundImages": user.get("backgroundImages", []),
"tutorial": user.get("tutorial"),
"createdAt": user["createdAt"].isoformat(),
"updatedAt": user["updatedAt"].isoformat(),
"message": "User updated successfully"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Update failed: {str(e)}")
@router.delete("/{user_id}")
async def delete_user(user_id: str):
"""Delete user account and all associated data."""
db = get_database()
try:
user_oid = ObjectId(user_id)
except InvalidId:
raise HTTPException(status_code=400, detail="Invalid user ID format")
try:
# Delete user
user_result = db.users.delete_one({"_id": user_oid})
if user_result.deleted_count == 0:
raise HTTPException(status_code=404, detail="User not found")
# Delete all user's entries
entry_result = db.entries.delete_many({"userId": user_oid})
return {
"message": "User deleted successfully",
"user_deleted": user_result.deleted_count,
"entries_deleted": entry_result.deleted_count
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Deletion failed: {str(e)}")