Files
bakery-ia/services/auth/app/services/user_service.py
2025-07-19 21:16:25 +02:00

153 lines
4.9 KiB
Python

"""
User service for managing user operations
"""
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete
from fastapi import HTTPException, status
from passlib.context import CryptContext
import structlog
from datetime import datetime, timezone
from app.models.users import User
from app.core.config import settings
logger = structlog.get_logger()
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class UserService:
"""Service for user management operations"""
@staticmethod
async def get_user_by_id(user_id: str, db: AsyncSession) -> User:
"""Get user by ID"""
try:
result = await db.execute(
select(User).where(User.id == user_id)
)
user = result.scalar_one_or_none()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return user
except Exception as e:
logger.error(f"Error getting user by ID {user_id}: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get user"
)
@staticmethod
async def update_user(user_id: str, user_data: dict, db: AsyncSession) -> User:
"""Update user information"""
try:
# Get current user
user = await UserService.get_user_by_id(user_id, db)
# Update fields
update_data = {}
allowed_fields = ['full_name', 'phone', 'language', 'timezone']
for field in allowed_fields:
if field in user_data:
update_data[field] = user_data[field]
if update_data:
update_data["updated_at"] = datetime.now(timezone.utc)
await db.execute(
update(User)
.where(User.id == user_id)
.values(**update_data)
)
await db.commit()
# Refresh user object
await db.refresh(user)
return user
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating user {user_id}: {e}")
await db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to update user"
)
@staticmethod
async def change_password(
user_id: str,
current_password: str,
new_password: str,
db: AsyncSession
):
"""Change user password"""
try:
# Get current user
user = await UserService.get_user_by_id(user_id, db)
# Verify current password
if not pwd_context.verify(current_password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Current password is incorrect"
)
# Hash new password
new_hashed_password = pwd_context.hash(new_password)
# Update password
await db.execute(
update(User)
.where(User.id == user_id)
.values(hashed_password=new_hashed_password, updated_at=datetime.now(timezone.utc))
)
await db.commit()
logger.info(f"Password changed for user {user_id}")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error changing password for user {user_id}: {e}")
await db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to change password"
)
@staticmethod
async def delete_user(user_id: str, db: AsyncSession):
"""Delete user account"""
try:
# Get current user first
user = await UserService.get_user_by_id(user_id, db)
# Soft delete by deactivating
await db.execute(
update(User)
.where(User.id == user_id)
.values(is_active=False)
)
await db.commit()
logger.info(f"User {user_id} deactivated (soft delete)")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error deleting user {user_id}: {e}")
await db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to delete user"
)