Spaces:
Running
Running
Merge pull request #106 from SatyamPrakash09/feat/user-update
Browse files- backend/app/routes/auth.py +60 -2
- backend/app/schemas.py +18 -0
backend/app/routes/auth.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
| 2 |
Auth API routes — register, login, and user profile.
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
|
|
|
|
| 5 |
from sqlalchemy.orm import Session
|
| 6 |
-
|
| 7 |
from app.database import get_db
|
| 8 |
from app.models import User
|
| 9 |
-
from app.schemas import UserRegister, UserLogin, TokenResponse, UserResponse, RefreshRequest
|
|
|
|
| 10 |
from app.auth import hash_password, verify_password, create_access_token, create_refresh_token, get_current_user, decode_token
|
| 11 |
|
| 12 |
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
|
@@ -102,3 +105,58 @@ def refresh_token(payload: RefreshRequest, db: Session = Depends(get_db)):
|
|
| 102 |
def get_me(user: User = Depends(get_current_user)):
|
| 103 |
"""Get current authenticated user profile."""
|
| 104 |
return UserResponse.model_validate(user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
Auth API routes — register, login, and user profile.
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
| 5 |
+
from langsmith import expect
|
| 6 |
+
from sqlalchemy.exc import SQLAlchemyError
|
| 7 |
from sqlalchemy.orm import Session
|
| 8 |
+
from sqlalchemy import select
|
| 9 |
from app.database import get_db
|
| 10 |
from app.models import User
|
| 11 |
+
from app.schemas import UserRegister, UserLogin, TokenResponse, UserResponse, RefreshRequest, UserUpdate, \
|
| 12 |
+
UserUpdateResponse, UpdatePassword, UpdatePasswordResponse
|
| 13 |
from app.auth import hash_password, verify_password, create_access_token, create_refresh_token, get_current_user, decode_token
|
| 14 |
|
| 15 |
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
|
|
|
| 105 |
def get_me(user: User = Depends(get_current_user)):
|
| 106 |
"""Get current authenticated user profile."""
|
| 107 |
return UserResponse.model_validate(user)
|
| 108 |
+
|
| 109 |
+
@router.put("/update")
|
| 110 |
+
def update_user_info(payload:UserUpdate,
|
| 111 |
+
user: User = Depends(get_current_user),
|
| 112 |
+
db: Session = Depends(get_db))-> UserUpdateResponse:
|
| 113 |
+
|
| 114 |
+
"""Update user info."""
|
| 115 |
+
if payload.username is None and payload.email is None:
|
| 116 |
+
raise HTTPException(status_code=400, detail="Username and email are required")
|
| 117 |
+
|
| 118 |
+
try:
|
| 119 |
+
if payload.username:
|
| 120 |
+
existing_user = db.execute(select(User).where(User.username == payload.username)).scalar_one_or_none()
|
| 121 |
+
|
| 122 |
+
if existing_user:
|
| 123 |
+
raise HTTPException(status_code=400, detail="Username already exists")
|
| 124 |
+
user.username = payload.username
|
| 125 |
+
if payload.email:
|
| 126 |
+
existing_user = db.execute(select(User).where(User.username == payload.email)).scalar_one_or_none()
|
| 127 |
+
|
| 128 |
+
if existing_user:
|
| 129 |
+
raise HTTPException(status_code=400, detail="Username already exists")
|
| 130 |
+
user.email = payload.email
|
| 131 |
+
db.commit()
|
| 132 |
+
db.refresh(user)
|
| 133 |
+
return user
|
| 134 |
+
except HTTPException:
|
| 135 |
+
raise
|
| 136 |
+
except SQLAlchemyError:
|
| 137 |
+
db.rollback()
|
| 138 |
+
|
| 139 |
+
raise HTTPException(status_code=400, detail="Database error")
|
| 140 |
+
|
| 141 |
+
@router.put("/password")
|
| 142 |
+
def update_password(payload:UpdatePassword,
|
| 143 |
+
user: User = Depends(get_current_user),
|
| 144 |
+
db: Session = Depends(get_db))-> UpdatePasswordResponse:
|
| 145 |
+
"""Update user password."""
|
| 146 |
+
if not payload.password and not payload.confirm_password:
|
| 147 |
+
raise HTTPException(status_code=400, detail="Password and confirm_password are required")
|
| 148 |
+
if len(payload.password) == 0 and len(payload.confirm_password) == 0:
|
| 149 |
+
raise HTTPException(status_code=400, detail="Password and confirm_password are required")
|
| 150 |
+
if payload.password != payload.confirm_password:
|
| 151 |
+
raise HTTPException(status_code=400, detail="Password and confirm_password are different")
|
| 152 |
+
try:
|
| 153 |
+
hashed_password = hash_password(payload.password)
|
| 154 |
+
user.hashed_password = hashed_password
|
| 155 |
+
db.commit()
|
| 156 |
+
db.refresh(user)
|
| 157 |
+
return user
|
| 158 |
+
except HTTPException:
|
| 159 |
+
raise
|
| 160 |
+
except SQLAlchemyError:
|
| 161 |
+
db.rollback()
|
| 162 |
+
raise HTTPException(status_code=400, detail="Database error")
|
backend/app/schemas.py
CHANGED
|
@@ -18,6 +18,24 @@ class UserLogin(BaseModel):
|
|
| 18 |
email: EmailStr
|
| 19 |
password: str
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
class TokenResponse(BaseModel):
|
| 23 |
access_token: str
|
|
|
|
| 18 |
email: EmailStr
|
| 19 |
password: str
|
| 20 |
|
| 21 |
+
class UserUpdate(BaseModel):
|
| 22 |
+
email: Optional[EmailStr] = None
|
| 23 |
+
username:Optional[str] = None
|
| 24 |
+
|
| 25 |
+
class UserUpdateResponse(BaseModel):
|
| 26 |
+
id: str
|
| 27 |
+
username: str
|
| 28 |
+
email: EmailStr
|
| 29 |
+
|
| 30 |
+
class UpdatePassword(BaseModel):
|
| 31 |
+
password: str
|
| 32 |
+
confirm_password: str
|
| 33 |
+
|
| 34 |
+
class UpdatePasswordResponse(BaseModel):
|
| 35 |
+
id: str
|
| 36 |
+
username: str
|
| 37 |
+
email: EmailStr
|
| 38 |
+
password_changed:bool = True
|
| 39 |
|
| 40 |
class TokenResponse(BaseModel):
|
| 41 |
access_token: str
|