191 lines
5.8 KiB
Python
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)
|