from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.db.session import get_db from app.core.security import verify_password, get_password_hash, create_access_token, generate_rsa_key_pair from app.core.deps import get_current_active_user from app.models.user import User as UserModel from app.schemas.user import User, UserCreate, UserLogin, UserUpdate, Token, UserPublic router = APIRouter() @router.post("/register", response_model=Token) def register( user: UserCreate, db: Session = Depends(get_db) ): # Check if username or email already exists existing_user = db.query(UserModel).filter( (UserModel.username == user.username) | (UserModel.email == user.email) ).first() if existing_user: if existing_user.username == user.username: raise HTTPException(status_code=400, detail="Username already registered") else: raise HTTPException(status_code=400, detail="Email already registered") # Generate E2E encryption key pair private_key, public_key = generate_rsa_key_pair() # Create new user hashed_password = get_password_hash(user.password) db_user = UserModel( username=user.username, email=user.email, hashed_password=hashed_password, full_name=user.full_name, bio=user.bio, is_active=user.is_active, public_key=public_key ) db.add(db_user) db.commit() db.refresh(db_user) # Create access token access_token = create_access_token(subject=db_user.id) user_public = UserPublic( id=db_user.id, username=db_user.username, full_name=db_user.full_name, avatar_url=db_user.avatar_url, is_online=db_user.is_online, last_seen=db_user.last_seen ) return Token( access_token=access_token, token_type="bearer", user=user_public ) @router.post("/login", response_model=Token) def login( credentials: UserLogin, db: Session = Depends(get_db) ): user = db.query(UserModel).filter(UserModel.username == credentials.username).first() if not user or not verify_password(credentials.password, user.hashed_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) if not user.is_active: raise HTTPException(status_code=400, detail="Inactive user") access_token = create_access_token(subject=user.id) user_public = UserPublic( id=user.id, username=user.username, full_name=user.full_name, avatar_url=user.avatar_url, is_online=user.is_online, last_seen=user.last_seen ) return Token( access_token=access_token, token_type="bearer", user=user_public ) @router.get("/me", response_model=User) def read_users_me(current_user: UserModel = Depends(get_current_active_user)): return current_user @router.put("/me", response_model=User) def update_user_me( user_update: UserUpdate, current_user: UserModel = Depends(get_current_active_user), db: Session = Depends(get_db) ): update_data = user_update.dict(exclude_unset=True) # Handle password update if "password" in update_data: update_data["hashed_password"] = get_password_hash(update_data.pop("password")) # Check username/email uniqueness if being updated if "username" in update_data: existing = db.query(UserModel).filter( UserModel.username == update_data["username"], UserModel.id != current_user.id ).first() if existing: raise HTTPException(status_code=400, detail="Username already taken") if "email" in update_data: existing = db.query(UserModel).filter( UserModel.email == update_data["email"], UserModel.id != current_user.id ).first() if existing: raise HTTPException(status_code=400, detail="Email already taken") for field, value in update_data.items(): setattr(current_user, field, value) db.commit() db.refresh(current_user) return current_user @router.post("/device-token") def update_device_token( device_token: str, current_user: UserModel = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Update user's device token for push notifications""" current_user.device_token = device_token db.commit() return {"message": "Device token updated successfully"}