from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from app.database import get_db from app.models.user import User from app.schemas.auth import LoginRequest, Token from app.schemas.user import UserCreate, UserRead from app.utils.security import ( verify_password, get_password_hash, create_access_token, decode_access_token, ) router = APIRouter() security = HTTPBearer() def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db), ) -> User: """Dependency to get the current authenticated user.""" token = credentials.credentials payload = decode_access_token(token) if payload is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token", headers={"WWW-Authenticate": "Bearer"}, ) user_id = payload.get("sub") if user_id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token payload", headers={"WWW-Authenticate": "Bearer"}, ) user = db.query(User).filter(User.id == int(user_id)).first() if user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found", headers={"WWW-Authenticate": "Bearer"}, ) return user @router.post( "/auth/register", response_model=UserRead, status_code=status.HTTP_201_CREATED ) def register(user_data: UserCreate, db: Session = Depends(get_db)): """Register a new user.""" # Check if email already exists existing_user = db.query(User).filter(User.email == user_data.email).first() if existing_user: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered" ) # Create new user hashed_password = get_password_hash(user_data.password) new_user = User( email=user_data.email, password_hash=hashed_password, name=user_data.name, role="member", ) db.add(new_user) db.commit() db.refresh(new_user) return new_user @router.post("/auth/login", response_model=Token) def login(login_data: LoginRequest, db: Session = Depends(get_db)): """Login and return JWT token.""" user = db.query(User).filter(User.email == login_data.email).first() if not user or not verify_password(login_data.password, user.password_hash): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or password" ) access_token = create_access_token(data={"sub": str(user.id), "email": user.email}) return Token(access_token=access_token) @router.post("/auth/logout") def logout(current_user: User = Depends(get_current_user)): """Logout (client should discard the token).""" return {"message": "Logged out successfully"} @router.get("/auth/me", response_model=UserRead) def get_me(current_user: User = Depends(get_current_user)): """Get current user info.""" return current_user @router.put("/auth/password") def change_password( current_password: str, new_password: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ): """Change password.""" if not verify_password(current_password, current_user.password_hash): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Current password is incorrect", ) current_user.password_hash = get_password_hash(new_password) db.commit() return {"message": "Password changed successfully"}