saveinmed/saveinmed-bff/src/modules/auth/router.py
2025-12-19 19:22:20 -03:00

191 lines
5.8 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Body, Depends, HTTPException, Response, status
from src.core.auth import create_access_token
from src.core.config import get_settings
from src.core.deps import get_auth_service, get_current_user_or_401
from .schemas import (
AuthOut,
ForgotPasswordIn,
LoginIn,
MeOut,
MessageOut,
RegisterIn,
ResetPasswordIn,
VerifyEmailIn,
)
from .service import AuthService
router = APIRouter(prefix="/api/v1/auth", tags=["Auth"])
@router.post(
"/register",
response_model=MeOut,
status_code=status.HTTP_201_CREATED,
responses={
201: {
"description": "Successful Response",
"content": {
"application/json": {
"example": {
"id": "user_abc123",
"identificador": "tiago",
"email": "tiago.silva@empresa.com",
"nome": "Tiago Silva",
"ativo": True,
"superadmin": False,
"nivel": "colaborador",
"createdAt": "2025-10-02T10:00:00.000Z",
"updatedAt": "2025-10-02T10:00:00.000Z",
}
}
},
}
},
)
async def register(
payload: RegisterIn = Body(
example={
"identificador": "tiago",
"email": "tiago.silva@empresa.com",
"nome": "Tiago Silva",
"senha": "Minhasenha@123",
"ativo": True,
"nivel": "colaborador",
"superadmin": False,
"auth-id-appwrite": "user_1234567890",
"registro-completo": False,
}
),
svc: AuthService = Depends(get_auth_service),
) -> MeOut:
user = await svc.register(payload)
return MeOut.model_validate(user)
@router.post("/register/customer", response_model=MeOut, status_code=status.HTTP_201_CREATED)
async def register_customer(
payload: RegisterIn,
svc: AuthService = Depends(get_auth_service),
) -> MeOut:
customer_payload = payload.model_copy(update={"nivel": "colaborador"})
user = await svc.register(customer_payload)
return MeOut.model_validate(user)
@router.post("/register/tenant", response_model=MeOut, status_code=status.HTTP_201_CREATED)
async def register_tenant(
payload: RegisterIn,
svc: AuthService = Depends(get_auth_service),
) -> MeOut:
tenant_payload = payload.model_copy(update={"nivel": "admin"})
user = await svc.register(tenant_payload)
return MeOut.model_validate(user)
@router.post("/login", response_model=AuthOut)
async def login(
payload: LoginIn,
response: Response,
svc: AuthService = Depends(get_auth_service),
) -> AuthOut:
document = await svc.authenticate(payload.identificador, payload.senha)
user_id = document.get("$id")
if not isinstance(user_id, str) or not user_id:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Documento de usuário inválido retornado pelo Appwrite.",
)
user = await svc.map_user(document)
settings = get_settings()
ttl_seconds = int(settings.jwt_access_ttl_seconds)
token = create_access_token(
sub=user_id,
expires_seconds=ttl_seconds,
secret=settings.jwt_secret,
algorithm=settings.jwt_algorithm,
)
domain = settings.cookie_domain or None
response.set_cookie(
key=settings.session_cookie_name,
value=token,
max_age=ttl_seconds,
expires=ttl_seconds,
httponly=True,
samesite="lax",
secure=bool(settings.cookie_secure),
domain=domain,
path="/",
)
return AuthOut(access_token=token, user=MeOut.model_validate(user))
@router.post("/refresh-token", response_model=AuthOut)
async def refresh_token(
response: Response,
user=Depends(get_current_user_or_401),
) -> AuthOut:
settings = get_settings()
ttl_seconds = int(settings.jwt_access_ttl_seconds)
token = create_access_token(
sub=user["id"],
expires_seconds=ttl_seconds,
secret=settings.jwt_secret,
algorithm=settings.jwt_algorithm,
)
domain = settings.cookie_domain or None
response.set_cookie(
key=settings.session_cookie_name,
value=token,
max_age=ttl_seconds,
expires=ttl_seconds,
httponly=True,
samesite="lax",
secure=bool(settings.cookie_secure),
domain=domain,
path="/",
)
return AuthOut(access_token=token, user=MeOut.model_validate(user))
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
async def logout(response: Response) -> Response:
settings = get_settings()
domain = settings.cookie_domain or None
response.delete_cookie(
key=settings.session_cookie_name,
path="/",
domain=domain,
httponly=True,
secure=bool(settings.cookie_secure),
)
response.status_code = status.HTTP_204_NO_CONTENT
return response
@router.post("/password/forgot", response_model=MessageOut, status_code=status.HTTP_202_ACCEPTED)
async def forgot_password(_: ForgotPasswordIn) -> MessageOut:
return MessageOut(message="Se existir uma conta, enviaremos instruções de redefinição.")
@router.post("/password/reset", response_model=MessageOut)
async def reset_password(_: ResetPasswordIn) -> MessageOut:
return MessageOut(message="Senha atualizada com sucesso.")
@router.post("/verify-email", response_model=MessageOut)
async def verify_email(_: VerifyEmailIn) -> MessageOut:
return MessageOut(message="E-mail verificado com sucesso.")
@router.get("/me", response_model=MeOut)
async def me(user=Depends(get_current_user_or_401)) -> MeOut:
return MeOut.model_validate(user)