diff --git a/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala b/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala index b5586c5..bbb74ed 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala @@ -1,16 +1,27 @@ package org.yobble.scala_monolith.api.endpoint.auth +import io.circe.generic.auto.* import org.yobble.scala_monolith.api.dto.{LoginRequest, LoginResponse} -import sttp.tapir._ -import sttp.tapir.generic.auto._ -import sttp.tapir.json.circe._ +import org.yobble.scala_monolith.api.response.ErrorResponse +import org.yobble.scala_monolith.api.util.ErrorExamples +import sttp.model.StatusCode +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.circe.* object AuthEndpoints { - val loginEndpoint: PublicEndpoint[LoginRequest, String, LoginResponse, Any] = + val loginEndpoint: PublicEndpoint[LoginRequest, ErrorResponse, LoginResponse, Any] = endpoint.post .in("auth" / "login") .in(jsonBody[LoginRequest]) .out(jsonBody[LoginResponse]) - .errorOut(stringBody) + .errorOut( + oneOf[ErrorResponse]( + oneOfVariant(StatusCode.BadRequest, jsonBody[ErrorResponse].description("Bad Request").example(ErrorExamples.badRequest)), + oneOfVariant(StatusCode.Unauthorized, jsonBody[ErrorResponse].description("Unauthorized").example(ErrorExamples.unauthorized)), + oneOfVariant(StatusCode.Forbidden, jsonBody[ErrorResponse].description("Forbidden").example(ErrorExamples.forbidden)), + oneOfVariant(StatusCode.UnprocessableEntity, jsonBody[ErrorResponse].description("Validation Error").example(ErrorExamples.validation)) + ) + ) } diff --git a/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala b/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala index abf2b17..3a42c2c 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala @@ -11,6 +11,5 @@ object ErrorResponse { implicit val errorDetailEncoder: Encoder[ErrorDetail] = deriveEncoder[ErrorDetail] // Encoder без поля code -// implicit val errorResponseEncoder: Encoder[ErrorResponse] = -// deriveEncoder[ErrorResponse].mapJsonObject(_.remove("code")) + implicit val errorResponseEncoder: Encoder[ErrorResponse] = deriveEncoder[ErrorResponse] } diff --git a/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala b/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala index 8fa8142..1fb11a3 100644 --- a/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala +++ b/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala @@ -2,24 +2,26 @@ package org.yobble.scala_monolith.service import cats.effect.IO import org.yobble.scala_monolith.api.dto.{LoginRequest, LoginResponse} +import org.yobble.scala_monolith.api.response.ErrorResponse +import org.yobble.scala_monolith.api.util.ErrorUtils import org.yobble.scala_monolith.repository.UserRepository class AuthService(userRepository: UserRepository) { - def login(request: LoginRequest): IO[Either[String, LoginResponse]] = { + def login(request: LoginRequest): IO[Either[ErrorResponse, LoginResponse]] = { userRepository.findByLogin(request.login).map { case Some(user) if user.passwordHash != request.password => - Left("Invalid login or password") + Left(ErrorUtils.unauthorized("Invalid login or password")) case Some(user) if user.isBlocked => - Left("User account is disabled") + Left(ErrorUtils.forbidden("User account is disabled")) case Some(user) if user.isDeleted => - Left("User account is deleted") + Left(ErrorUtils.forbidden("User account is deleted")) case Some(user) => // TODO: Implement proper password hashing (e.g., with bcrypt) // TODO: Implement real token generation Right(LoginResponse(accessToken = "fake-access-token", refreshToken = "fake-refresh-token")) case None => - Left("Invalid login or password") + Left(ErrorUtils.unauthorized("Invalid login or password")) } } }