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)