173 lines
5.4 KiB
Python

import logging
import os
import uuid
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from sqlalchemy.orm import Session
from app.core.config import settings
from app.core.database import get_db
from app.core.security import get_password_hash, verify_password
from app.dependencies.auth import get_current_active_user, get_current_admin
from app.models.user import User, UserRole
from app.schemas.user import (
User as UserSchema,
)
from app.schemas.user import (
UserPasswordChange,
UserUpdate,
UserWithAddress,
)
router = APIRouter()
logger = logging.getLogger(__name__)
@router.get("/", response_model=list[UserSchema])
async def get_users(
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_admin)
):
"""
Get all users (admin only).
"""
users = db.query(User).offset(skip).limit(limit).all()
return users
@router.get("/{user_id}", response_model=UserWithAddress)
async def get_user(
user_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Get a specific user by ID.
Regular users can only get their own user details.
Admin users can get any user details.
"""
if current_user.id != user_id and current_user.role != UserRole.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions to access this user's data"
)
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return user
@router.put("/me", response_model=UserWithAddress)
async def update_user_me(
user_update: UserUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Update current user information.
"""
for key, value in user_update.dict(exclude_unset=True).items():
setattr(current_user, key, value)
db.commit()
db.refresh(current_user)
logger.info(f"User {current_user.email} updated their profile")
return current_user
@router.post("/me/change-password", response_model=dict)
async def change_password(
password_data: UserPasswordChange,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Change current user's password.
"""
if not verify_password(password_data.current_password, current_user.hashed_password):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Incorrect current password"
)
current_user.hashed_password = get_password_hash(password_data.new_password)
db.commit()
logger.info(f"User {current_user.email} changed their password")
return {"message": "Password updated successfully"}
@router.post("/me/profile-image", response_model=dict)
async def upload_profile_image(
file: UploadFile = File(...),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Upload a profile image for the current user.
"""
# Validate the file
content_type = file.content_type
if not content_type.startswith("image/"):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="File must be an image"
)
# Create user images directory if it doesn't exist
user_images_dir = settings.USER_IMAGES_DIR
user_images_dir.mkdir(parents=True, exist_ok=True)
# Generate a unique filename
file_extension = os.path.splitext(file.filename)[1]
unique_filename = f"{uuid.uuid4()}{file_extension}"
file_path = user_images_dir / unique_filename
# Save the file
with open(file_path, "wb") as buffer:
buffer.write(await file.read())
# Update the user's profile image in the database
relative_path = f"/storage/user_images/{unique_filename}"
current_user.profile_image = relative_path
db.commit()
logger.info(f"User {current_user.email} uploaded a new profile image")
return {"filename": unique_filename, "path": relative_path}
@router.delete("/{user_id}", response_model=dict)
async def delete_user(
user_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Delete a user.
Regular users can only delete their own account.
Admin users can delete any user.
"""
if current_user.id != user_id and current_user.role != UserRole.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions to delete this user"
)
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
# Soft delete the user by setting is_active to False
# In a real-world application, you might want to consider:
# 1. Hard deleting the user data for GDPR compliance
# 2. Anonymizing the user data instead of deleting
# 3. Setting up a scheduled task to actually delete inactive users after a certain period
user.is_active = False
db.commit()
logger.info(f"User {user.email} was marked as inactive (deleted)")
return {"message": "User successfully deleted"}