166 lines
5.9 KiB
Python
166 lines
5.9 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from typing import List
|
|
from datetime import datetime, timezone
|
|
import ulid
|
|
|
|
from app.core.database.db_session import get_db
|
|
from app.repositories.user_repository import user_repository
|
|
from app.models.UserModel import User
|
|
from app.models.RoleModel import Role
|
|
from app.schemas.User import UserCreate, UserUpdate, UserResponse
|
|
from app.core.permissions.RoleChecker import RoleChecker, PermissionChecker
|
|
from app.core.validators.password_validator import validate_password_complexity
|
|
from app.utils.Hash_util import hash_password
|
|
from app.services.code_generator_service import code_generator_service
|
|
|
|
router = APIRouter(prefix="/api/v1/users", tags=["User Creation"])
|
|
|
|
@router.post("/create", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
|
def create_user(
|
|
data: UserCreate,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(PermissionChecker("user.create"))
|
|
):
|
|
# 1. Check duplicate identity parameters
|
|
duplicate = user_repository.check_duplicate_identity(db, data.email, data.phone)
|
|
if duplicate:
|
|
raise HTTPException(status_code=400, detail="User with this email or phone number already exists.")
|
|
|
|
# 2. Check Role existence and fetch prefix
|
|
role = db.get(Role, data.role_id)
|
|
if not role or role.deleted_at is not None:
|
|
raise HTTPException(status_code=400, detail="Specified role ID is invalid or inactive.")
|
|
|
|
# 3. Validate password complexity
|
|
validate_password_complexity(data.password)
|
|
|
|
# 4. Generate sequential employee code
|
|
prefix = role.role_prefix
|
|
employee_code = code_generator_service.generate_next_code(db, "User", prefix)
|
|
|
|
# 5. Build user record
|
|
user_id = str(ulid.ULID())
|
|
new_user = User(
|
|
user_id=user_id,
|
|
employee_code=employee_code,
|
|
first_name=data.first_name,
|
|
last_name=data.last_name,
|
|
display_name=data.display_name or f"{data.first_name} {data.last_name}",
|
|
email=data.email,
|
|
phone=data.phone,
|
|
password_hash=hash_password(data.password),
|
|
gender=data.gender,
|
|
dob=data.dob,
|
|
department_id=data.department_id,
|
|
designation_id=data.designation_id,
|
|
role_id=data.role_id,
|
|
manager_id=data.manager_id,
|
|
is_active=True
|
|
)
|
|
|
|
user_repository.create(db, new_user)
|
|
return new_user
|
|
|
|
@router.get("/all", response_model=List[UserResponse])
|
|
def get_all_users(
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(PermissionChecker("user.view"))
|
|
):
|
|
users = user_repository.get_active_users(db, skip, limit)
|
|
from app.models.AuditLogModel import AuditLog
|
|
for u in users:
|
|
latest = (
|
|
db.query(AuditLog)
|
|
.filter(AuditLog.user_id == u.user_id)
|
|
.order_by(AuditLog.created_at.desc())
|
|
.first()
|
|
)
|
|
if latest:
|
|
u.last_activity = f"{latest.action.replace('_', ' ').title()} {latest.entity_type}"
|
|
else:
|
|
u.last_activity = "No recent activity"
|
|
return users
|
|
|
|
@router.get("/profile/{user_id}", response_model=UserResponse)
|
|
def get_user_profile(
|
|
user_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(PermissionChecker("user.view"))
|
|
):
|
|
user = user_repository.get_by_id(db, user_id)
|
|
if not user or user.deleted_at is not None:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
return user
|
|
|
|
@router.put("/update/{user_id}", response_model=UserResponse)
|
|
def update_user_profile(
|
|
user_id: str,
|
|
payload: UserUpdate,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(PermissionChecker("user.update"))
|
|
):
|
|
user = user_repository.get_by_id(db, user_id)
|
|
if not user or user.deleted_at is not None:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
|
|
# Check email/phone uniqueness if being updated
|
|
if payload.email and payload.email != user.email:
|
|
if user_repository.get_by_email(db, payload.email):
|
|
raise HTTPException(status_code=400, detail="Email is already registered.")
|
|
user.email = payload.email
|
|
|
|
if payload.phone and payload.phone != user.phone:
|
|
if user_repository.get_by_phone(db, payload.phone):
|
|
raise HTTPException(status_code=400, detail="Phone number is already registered.")
|
|
user.phone = payload.phone
|
|
|
|
# Update strings
|
|
if payload.first_name:
|
|
user.first_name = payload.first_name
|
|
if payload.last_name:
|
|
user.last_name = payload.last_name
|
|
if payload.display_name:
|
|
user.display_name = payload.display_name
|
|
if payload.gender:
|
|
user.gender = payload.gender
|
|
if payload.dob:
|
|
user.dob = payload.dob
|
|
if payload.profile_image_id:
|
|
user.profile_image_id = payload.profile_image_id
|
|
|
|
# Update relationships
|
|
if payload.department_id:
|
|
user.department_id = payload.department_id
|
|
if payload.designation_id:
|
|
user.designation_id = payload.designation_id
|
|
if payload.role_id:
|
|
user.role_id = payload.role_id
|
|
if payload.manager_id:
|
|
user.manager_id = payload.manager_id
|
|
|
|
# If resetting password
|
|
if payload.password:
|
|
validate_password_complexity(payload.password)
|
|
user.password_hash = hash_password(payload.password)
|
|
|
|
user_repository.update(db, user)
|
|
return user
|
|
|
|
@router.delete("/delete/{user_id}")
|
|
def delete_user_profile(
|
|
user_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(PermissionChecker("user.delete"))
|
|
):
|
|
user = user_repository.get_by_id(db, user_id)
|
|
if not user or user.deleted_at is not None:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
|
|
# Safe soft delete
|
|
user.deleted_at = datetime.now(timezone.utc)
|
|
user.is_active = False
|
|
db.commit()
|
|
return {"detail": "User soft deleted successfully"}
|