from fastapi import APIRouter,Depends, Query from fastapi.responses import JSONResponse from fastapi import HTTPException from db_setup import get_session from sqlalchemy.ext.asyncio import AsyncSession from pydantic import BaseModel, EmailStr, model_validator from users.views import User from users.permissions import CurrentUser, PermissionTag, require_permission from users.serializers import serialize_token from users.plugins import create_access_token,create_refresh_token from dotenv import load_dotenv load_dotenv() router = APIRouter() class UserCreate(BaseModel): name: str email: EmailStr password: str role_id: int | None = None is_active: bool = True class UserSignup(BaseModel): name: str email: EmailStr password: str class UserUpdate(BaseModel): name: str | None = None email: EmailStr | None = None password: str | None = None is_active: bool | None = None class RoleAssign(BaseModel): role_id: int class UserLogin(BaseModel): password: str email: EmailStr | None = None username: str | None = None @model_validator(mode="after") def require_email_or_username(self): if not self.email and not self.username: raise ValueError("email or username is required") return self class TokenRefresh(BaseModel): refresh_token: str @router.post("/users/login") async def login(payload: UserLogin,session: AsyncSession = Depends(get_session)): try: service=User(session=session) user=await service.authenticate_user(payload.email or payload.username,payload.password) tokens=serialize_token(create_access_token(user),create_refresh_token(user),user) return JSONResponse(content={**tokens,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/users/signup") async def signup(payload: UserSignup,session: AsyncSession = Depends(get_session)): try: service=User(session=session) user=await service.signup_user(payload.model_dump()) tokens=serialize_token(create_access_token(user),create_refresh_token(user),user) return JSONResponse(content={**tokens,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/users/refresh") async def refresh(payload: TokenRefresh,session: AsyncSession = Depends(get_session)): try: service=User(session=session) user=await service.refresh_access_token(payload.refresh_token) tokens=serialize_token(create_access_token(user),create_refresh_token(user),user) return JSONResponse(content={**tokens,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/users/me") async def me(current_user: CurrentUser): try: return JSONResponse(content={"data":current_user,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/users/create") async def create_user( payload: UserCreate, current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_CREATE)), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) user=await service.create_user(payload.model_dump(),current_user) tokens=serialize_token(create_access_token(user),create_refresh_token(user),user) return JSONResponse(content={**tokens,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/users/fetch") async def fetch_users( current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_VIEW)), record_id: str | None = Query(None), search: str | None = Query(None), top: int | None = Query(None), skip: int = Query(0, ge=0), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) if record_id: item=await service.get_user_by_id(record_id) return JSONResponse(content={"data":item,"total":1,"status_code":200}) items=await service.get_users(top,skip,search) total=await service.count_users(search) return JSONResponse(content={"data":items,"total":total,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.put("/users/update") async def update_user( payload: UserUpdate, current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_EDIT)), record_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) data=await service.update_user(record_id,payload.model_dump(exclude_unset=True)) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.put("/users/assign-role") async def assign_role( payload: RoleAssign, current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_EDIT)), record_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) data=await service.assign_role(record_id,payload.role_id,current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.put("/users/remove-role") async def remove_role( current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_EDIT)), record_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) data=await service.remove_role(record_id,current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.delete("/users/delete") async def delete_user( current_user: dict = Depends(require_permission(PermissionTag.RBAC_USERS_DELETE)), record_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service=User(session=session) data=await service.delete_user(record_id) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e))